Skip to main content

migrate-guide

Migrate a standalone guide or learning path to the Pathfinder package format by generating manifest.json (and path-level content.json for LJs). Reads content.json, index.json, recommender rules, and optionally website markdown to derive all manifest fields. Use when the user wants to migrate a guide directory to the package format, or asks to create a manifest.json for a guide.

معلومات المصدر

المستودع
grafana/interactive-tutorials
آخر نشاط في المصدر
١٦ أغسطس ٢٠٢٦ في ٠٣:٢٦
لغة SKILL.md المكتشفة
الإنجليزية
النجوم
١٣
التفرعات
٨

خيارات التثبيت

يُحدَّد Prompt الذي يراجع المصدر أولًا بشكل افتراضي. يمكنك التبديل إلى أمر مباشر أو تنزيل نسخة محلية.

مراجعة ملفات المصدر

اقرأ SKILL.md وأي ملفات مرافقة يعرضها SkillsMP قبل أن تقرر التثبيت.

عرض SKILL.md

SKILL.md
تعليمات المصدر · معاينة للقراءة فقط
name
migrate-guide
description
Migrate a standalone guide or learning path to the Pathfinder package format by generating manifest.json (and path-level content.json for LJs). Reads content.json, index.json, recommender rules, and optionally website markdown to derive all manifest fields. Use when the user wants to migrate a guide directory to the package format, or asks to create a manifest.json for a guide.
# Migrate Guide to Package Format Migrate a single guide directory or a learning path (`*-lj`) to the Pathfinder two-file package model (`content.json` + `manifest.json`). The skill is invoked on one directory at a time and is safe to run in parallel across different directories. **Read these reference documents on demand** for field-level detail: - `docs/manifest-reference.md` — authoritative derivation rules, templates, naming conventions, fallbacks - `.cursor/authoring-guide.mdc` — guide content conventions (for path-level content.json authoring) Keep this skill focused on workflow/orchestration. Do not duplicate field derivation tables from `docs/manifest-reference.md`. --- ## Safety Invariants These rules are **inviolable** — the skill must never break them: 1. **Never modify an existing `content.json`.** The skill may *create* a new `content.json` (path-level cover page) but must never modify one that already exists. 2. **Never modify `index.json`.** Read it as a data source; never write to it. 3. **Never modify recommender files.** The `grafana-recommender` repo is a read-only data source; never write to it. 4. **Verify after writing.** Before any writes, snapshot every in-scope pre-existing `content.json` as raw bytes (or SHA-256). After writes, confirm each snapshot is byte-identical. --- ## Batch Mode When this skill is invoked as a sub-agent by an orchestrator (i.e., the agent was not started interactively by a human), it operates in **batch mode**. In batch mode: - **Never block waiting for user input.** Any situation that would normally require asking the user is resolved by writing a `TODO` item in the migration notes instead. - **Mark the migration as incomplete** when a TODO item is written. Include `status: incomplete` in the migration-notes frontmatter and add a `## TODO` section listing every unresolved item. - **Continue past blockers.** Generate as much of the output as possible. A partially-completed manifest with TODO items is preferable to no output at all. **TODO item format** (use consistently throughout migration notes): ``` - [ ] TODO(<category>): <what is missing> — <what the reviewer must do> ``` Categories: `description`, `conflict`, `review`, `fallback`. Example: `- [ ] TODO(description): step "configure-alloy" has no description source — reviewer must supply a one-line catalog description` --- ## Mode Detection Determine the mode from the target directory: | Condition | Mode | |-----------|------| | Directory name ends with `-lj` OR a directory contains other nested directories with `content.json` | **Mode 2: Learning path** | | Otherwise | **Mode 1: Standalone guide** | --- ## Data Sources ### index.json (read-only) Location: `index.json` (repo root). **index.json is at the repository root only; there is no per-guide index.json.** Each rule has: `title`, `url`, `description`, `type`, `match`. Match a rule to a guide by: 1. Strip any trailing `/content.json` from the rule's `url` 2. Extract the last path segment (e.g., `alerting-101`) 3. Compare against the guide's directory name or `content.json` `id` If no rule matches, the guide has no targeting — omit the `targeting` field. ### Website learning path markdown (read-only, optional) Location: `<path_to_local_clone>/website/content/docs/learning-paths/` If the hardcoded path does not exist (e.g., on another machine or in CI), the agent may shallow-clone the `grafana/website` repo into a temporary directory and use that path instead; the structure under `content/docs/learning-paths/` is the same as in a local checkout. Map `*-lj` directory names to website paths by stripping the `-lj` suffix (e.g., `prometheus-lj` → `prometheus`). Step directory names are identical in both repos. The website markdown `pathfinder_data` frontmatter provides the authoritative mapping when present, but most steps lack it — fall back to directory name matching (see step 3 canonical mapping rules). If the website repo is unavailable, apply fallback rules from `docs/manifest-reference.md` under "When website markdown is unavailable" and flag affected fields for manual review. ### journeys.yaml (read-only, optional) Location: `<path_to_local_clone>/website/content/docs/learning-paths/journeys.yaml` Provides inter-journey category and relationship data. ### Recommender rules (read-only) Location: `grafana-recommender/internal/configs/state_recommendations/*.json` This is the **primary source of targeting/match rules for learning journeys**. `index.json` contains only `"type": "interactive"` entries — it never has learning-journey routing rules. All learning-journey match rules live in the recommender repo. **Locating the recommender repo (in priority order):** 1. Check for a local checkout at common paths: `~/hax/grafana-recommender`, `~/Documents/repositories/grafana-recommender`, or a sibling directory to the interactive-tutorials repo 2. If not found locally, shallow-clone `git@github.com:grafana/grafana-recommender.git` to `/tmp/grafana-recommender` 3. If a `/tmp/grafana-recommender` clone already exists, run `git -C /tmp/grafana-recommender pull --ff-only` to ensure it has the latest rules **Freshness:** Always ensure the recommender checkout is up-to-date before extracting rules. If using a `/tmp` clone, pull at the start of each migration run. Stale rules can lead to incorrect or missing targeting. **Matching rules to a learning path:** Each `*.json` file in `state_recommendations/` has a `rules` array. Filter to entries where `"type": "learning-journey"`. Match by extracting the slug from the `url` field: 1. Parse the `url` (e.g., `https://grafana.com/docs/learning-journeys/prometheus/`) 2. Extract the path name — the segment after `/learning-journeys/` or `/learning-paths/` (e.g., `prometheus`). The recommender uses both URL prefixes interchangeably; handle both. 3. Compare against the directory name with `-lj` stripped (e.g., `prometheus-lj` → `prometheus`) **Multiple rules per learning path:** A single learning path may have **multiple rules** across different recommender files (different URL contexts, different tags, different platform targets). Collect **all** matching rules. When a learning path has multiple rules: - **`targeting.match`**: If all rules share the same match expression, use it directly. If there are multiple distinct match expressions, wrap them in a top-level `{"or": [...]}` to preserve all routing contexts. Record which recommender files contributed which rules in the migration notes. - **`startingLocation`**: Traverse all collected match expressions, collect all URL-bearing leaves (`urlPrefix` values and `urlPrefixIn` entries), pick the first. - **`testEnvironment`**: Apply the standard tier inference rules across all collected matches. If any match contains `"targetPlatform": "cloud"`, the tier is `"cloud"`. If rules span both cloud and oss, use `"cloud"` (the more common deployment) and note the dual-platform targeting in migration notes. - **`description`**: If a recommender rule has a non-empty `description`, it is a valid source for the manifest description (see [Description Conventions](#description-conventions)). If the recommender repo is unavailable (clone fails, no network, no SSH key), fall back to index.json-only behavior and flag the absence in migration notes. --- ## Description Conventions The `description` field is a **compact, one-line summary** suitable for a course catalog listing. It is not introductory prose — that belongs in the `content.json` markdown blocks. **Priority for resolving `description`:** 1. **`index.json` rule or recommender rule** — if the guide has a matching rule in either source, use its `description` verbatim. These are already written in catalog style. For learning journeys, the recommender is the primary source since `index.json` does not contain learning-journey entries. Skip entries with empty `description` values. 2. **Summarize available sources** — if no rule with a non-empty description exists, collect all available description sources (website markdown frontmatter `description`, `content.json` title, path-level metadata) and boil them down into a single sentence. Write it in the style of the rule descriptions (e.g., "Hands-on guide: Learn how to..."). 3. **No sources at all** — if no sources exist at all, do not invent a description. - **Interactive mode:** Stop and ask the user for a description. - **Batch mode:** Write `- [ ] TODO(description): <guide-id> has no description source — reviewer must supply a one-line catalog description` in the migration notes, leave the `description` field as `"TODO: manual description required"`, and mark the migration incomplete. Continue generating all other fields. --- ## Author Conventions The `author` field has a `team` value that depends on content type: | Content type | `author.team` | |-------------|---------------| | Learning path (`type: "path"`) | `"Grafana Documentation"` | | Step within a learning path (inside a `*-lj` directory) | `"Grafana Documentation"` | | Standalone guide (not inside a `*-lj` directory) | `"interactive-learning"` | `"interactive-learning"` is the fallback default when content type is unknown. If you know the content is a learning path or a step within one, always use `"Grafana Documentation"`. The `author.name` field is optional. To derive it: 1. **`.github/CODEOWNERS` (preferred when guide-specific):** Read `.github/CODEOWNERS` from the interactive-tutorials repo root. If a **directory-scoped** rule applies to the package being migrated, use the listed GitHub handle(s) for `author.name` (strip the leading `@`; comma-separate multiple handles). - **Matching:** Normalize the migrated directory to a repo-relative path (e.g. standalone `alerting-101`, path `prometheus-lj`, step `prometheus-lj/add-data-source`). Find a line whose pattern is a path prefix for that directory, e.g. `/alerting-101/` or `/prometheus-lj/` (GitHub CODEOWNERS uses last matching rule wins). If a step directory has no own line, try the parent `*-lj` directory (e.g. `/prometheus-lj/` for any step under it). - **Do not** use generic review patterns as the author source: `*`, `**/content.json`, `**/manifest.json`, `**/assets/*`, or other repo-wide globs — those list many reviewers and are not primary author attribution. If only those patterns match, skip to git history. - Team owners (e.g. `@grafana/slo-squad`) may appear; use the handle as `name` (without `@`) when that is the listed owner. 2. **Git history** — if step 1 did not yield `author.name`, look at all git revisions since the `content.json` file was created (use `git log --follow` to track renames) 3. Prefer GitHub handles; extract commit author names/handles 4. Exclude any obvious automation or bot authors (e.g., `dependabot`, `renovate`, `github-actions`, `bot`, etc.) 5. If multiple authors remain, comma-separate them 6. If only full names (not handles) appear in git history, use full names — some data is better than none. If no authors remain after filtering bots, or if unsure, omit `name` entirely Record in migration notes when `author.name` came from CODEOWNERS vs git history. --- ## Mode 1: Standalone Guide Invoked on a guide directory (e.g., `alerting-101/`). ### Steps #### 1. Read content.json Read `{dir}/content.json` and extract the `id` and `title` fields. **Do not modify this file.** #### 2. Look up matching index.json rule Read `index.json` from the repo root. Find the rule whose `url` path segment matches the directory name or the `content.json` `id`. Record: - `description` from the rule - `match` object from the rule - `startingLocation`: traverse the `match` expression recursively, collect all URL-bearing leaves (`urlPrefix` values and entries from `urlPrefixIn` arrays), then pick the first one. If no URL can be extracted, omit `startingLocation` entirely — a missing value is preferable to a wrong one. If no rule matches, record that targeting is absent. #### 3. Check for website markdown (if step is inside a `*-lj`) If this guide directory is a direct child of a `*-lj` directory, look up the parent path's website markdown `_index.md` and extract `journey.group` for the `category` field. Otherwise, `category` defaults to `"general"`. #### 4. Derive testEnvironment **`testEnvironment` must NEVER be omitted.** Every manifest must include it. Apply these rules in order: **IF match expression exists (and is not empty):** - **IF** `match` contains `source` at any depth → evaluate the source value: - **If the source value is a regex matching all `*.grafana.net` hosts** (e.g., `".*\\.grafana\\.net"`) → `{ "tier": "cloud" }` — this means "any Grafana Cloud instance", so no specific `instance` is set - **If the source value is a concrete hostname** (e.g., `"play.grafana.org"`) → `{ "tier": "cloud", "instance": "<source value>" }` - **If the source value is any other regex or pattern** → `{ "tier": "cloud" }` and flag for manual review — do not copy a regex into `instance` - **ELSE IF** `match` contains `"targetPlatform": "cloud"` → `{ "tier": "cloud" }` - **ELSE** → `{ "tier": "local" }` **ELSE (no match expression or match expression is empty):** - → `{ "tier": "cloud" }` (minimum default) Note: An empty match expression (`match: {}`) is treated the same as no match expression — both default to `"cloud"`. #### 5. Generate manifest.json Write `{dir}/manifest.json` with the derived fields: ```json { "id": "<from content.json>", "type": "guide", "description": "<compact one-line summary; see Description Conventions>", "category": "<from journey.group if inside *-lj, else 'general'>", "author": { "team": "<see Author Conventions>" }, "startingLocation": "<first URL from match, if derivable>", "targeting": { "match": { "<copied verbatim from index.json rule>" } }, "testEnvironment": { "tier": "<local|cloud|play>", "instance": "<if applicable>" }, "depends": [], "recommends": [], "suggests": [], "provides": [] } ``` Field omission rules: - Omit `repository` (schema default `"interactive-tutorials"` applies) - Omit `language` (schema default `"en"` applies) - Include `author.name` when derived per [Author Conventions](#author-conventions); omit otherwise - Omit `targeting` entirely if no targeting rule was found (index.json for standalone guides, recommender for learning journeys) - Omit `startingLocation` if no URL can be derived from the match expression — do not fall back to `"/"`. A missing value is preferable to a wrong one. - **Never omit `testEnvironment`.** Minimum is `{ "tier": "cloud" }`. Omit `instance` when no instance value is available. - Always include `depends`, `recommends`, `suggests`, and `provides` — even when empty (`[]`). This makes the fields visible to authors so they know they can fill them out later. Never invent values; use `[]` when no information is available. #### 6. Validate - Confirm `id` matches between `content.json` and `manifest.json` - Confirm the generated JSON is syntactically valid
عرض على GitHub
ملف SKILL.md هذا كبير جدا، لذلك يعرض SkillsMP القسم الاول فقط هنا. عرض على GitHub