[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M69B2SDJ0A4EYX0F4F243SSB":3,"skill-same:01M69B2SDJ0A4EYX0F4F243SSB":89},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":16,"categories":17,"keywords":31,"publishedAt":35,"linkOnly":36,"markdownContent":37,"markdownBody":38,"frontMatter":39,"refName":40,"commitSha":41,"skillDirectory":42,"forksCount":43,"repositoryHtmlUrl":44,"repositoryHomepageUrl":6,"ownerHtmlUrl":45,"ownerBlogUrl":46,"ownerDescription":47,"updatedAt":48,"install":49,"resources":62},"01M69B2SDJ0A4EYX0F4F243SSB","assessment-report-converter",null,"Convert an arbitrary CSV report (e.g. a Black Duck export or a custom migration-issue inventory) into\na schema-valid assessment `report.json` the modernization pipeline can consume — so it appears in the\nassessment UI and migration solutions resolve automatically.\nThis skill is LLM-driven: you read and interpret the CSV yourself and author `report.json` by hand against\nthe schema. A small helper script only does deterministic lookups (migration solutions, the ruleId for a\nsolution) and validates the finished report. There is NO \"convert everything\" script and NO assumed column layout.\nTriggers: \"convert csv to assessment report\", \"import csv report\", \"turn this spreadsheet into a report.json\",\n\"Black Duck csv to report\", \"build report.json from csv\", \"migrate a third-party assessment export\".\nNOT for: AppCAT-style analysis from source (use `assessment`), generating a modernization plan\n(use `create-modernization-plan`), or editing a report.json the pipeline already produced.","microsoft\u002Fgithub-copilot-modernization","microsoft","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F6154722?v=4","01M698VYDXZ6JW5WH3CRV650R8","Microsoft","01M69B2DAY0HTBHWQN4WF3JDBQ",55,"MIT","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fgithub-copilot-modernization\u002Fblob\u002Fmain\u002Fplugins\u002Fgithub-copilot-modernization\u002Fskills\u002Fassessment-report-converter\u002FSKILL.md",[18,23,26],{"categoryNo":19,"categoryName":20,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000004","电子表格","SCENARIO","使用场景",{"categoryNo":24,"categoryName":25,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000023","Skill与智能体开发",{"categoryNo":27,"categoryName":28,"taxonomyType":29,"taxonomyTypeDesc":30},"01JBSEEDCAT000000000000034","行政与办公人员","PROFESSION","适用职业",[32,33,34],"assessment report converter","valid","modernization","2026-11-10 10:12:30",false,"---\nname: assessment-report-converter\ndescription: |\n  Convert an arbitrary CSV report (e.g. a Black Duck export or a custom migration-issue inventory) into\n  a schema-valid assessment `report.json` the modernization pipeline can consume — so it appears in the\n  assessment UI and migration solutions resolve automatically.\n  This skill is LLM-driven: you read and interpret the CSV yourself and author `report.json` by hand against\n  the schema. A small helper script only does deterministic lookups (migration solutions, the ruleId for a\n  solution) and validates the finished report. There is NO \"convert everything\" script and NO assumed column layout.\n  Triggers: \"convert csv to assessment report\", \"import csv report\", \"turn this spreadsheet into a report.json\",\n  \"Black Duck csv to report\", \"build report.json from csv\", \"migrate a third-party assessment export\".\n  NOT for: AppCAT-style analysis from source (use `assessment`), generating a modernization plan\n  (use `create-modernization-plan`), or editing a report.json the pipeline already produced.\n---\n\n# Assessment Report Converter (CSV → report.json)\n\n## What this skill does\n\nYou take a **row-oriented CSV** of migration issues and produce a canonical **`report.json`** conforming to the\nassessment report schema. Once written to the versioned reports directory, the\nreport is picked up by the assessment UI and by the solution-resolution logic — exactly like a native AppCAT report.\n\n**This is an LLM-driven conversion, not a fixed mapping script.** CSV layouts vary between tools and change over time,\nso *you* read the file, decide what each column means, classify every row, and author `report.json`. You do **not**\nrely on hard-coded column names.\n\nA small helper script — [scripts\u002Freport_tools.sh](scripts\u002Freport_tools.sh) (bash + jq) or its PowerShell twin\n[scripts\u002Freport_tools.ps1](scripts\u002Freport_tools.ps1) (PowerShell 7+) — provides only the deterministic pieces\nyou should never guess:\n\n- **`list-solutions`** — discover the available migration solutions.\n- **`rules-for-solution`** — get the canonical `ruleId`(s) for a chosen solution.\n- **`upgrade-solutions`** — the canonical JDK \u002F Spring Boot \u002F Spring Framework \u002F Jakarta EE upgrade solutions + ruleIds.\n- **`validate`** — structural + consistency validation of the finished `report.json`.\n\n```mermaid\nflowchart LR\n    A[Read CSV] --> B[Understand columns] --> C[Find projects] --> D{Audit each row}\n    D -->|CVE \u002F CWE| E[Security finding]\n    D -->|Needs upgrade| F[Upgrade incident]\n    D -->|Other| G[Solution → ruleId → incident]\n    E --> H[Assemble report]\n    F --> H\n    G --> H\n    H --> I[Validate] --> J[Save report.json] --> K[Summarize]\n```\n\n## Input parameters\n\n- `csv-path` (mandatory): Path to the source CSV file.\n- `workspace-path` (optional): Output root. Defaults to the current directory. The report is written to\n  `{workspace-path}\u002F.github\u002Fmodernize\u002Fassessment\u002Freports\u002Freport-{reportId}\u002Freport.json`.\n- `producer` (optional): A label identifying the source tool, stored in `report.producer` (e.g. `\"Black Duck\"`).\n  Defaults to `\"CSV import\"`.\n\n## When to use this skill\n\nUse this when you have a **CSV** — not an AppCAT `report.json` — and you want it to behave like a real assessment\nreport: a Black Duck \u002F third-party export, or a hand-maintained spreadsheet of migration issues. Do **not** use it to\nrun analysis from source code (that is the `assessment` skill).\n\n## The helper script\n\nThe helper ships as two interchangeable implementations of the same CLI — use whichever fits the machine:\n[scripts\u002Freport_tools.sh](scripts\u002Freport_tools.sh) (**bash + jq**) and\n[scripts\u002Freport_tools.ps1](scripts\u002Freport_tools.ps1) (**PowerShell 7+**, no extra dependencies). Both behave\nidentically and never read the CSV. Run them from the `scripts\u002F` directory.\n\n```bash\n# bash + jq\n.\u002Freport_tools.sh list-solutions [--query KW] [--type Formula|Chat] [--ids-only]\n.\u002Freport_tools.sh rules-for-solution \u003CsolutionId>\n.\u002Freport_tools.sh upgrade-solutions\n.\u002Freport_tools.sh validate \u003Cpath\u002Fto\u002Freport.json>\n```\n\n```powershell\n# PowerShell 7+\npwsh .\u002Freport_tools.ps1 list-solutions [--query KW] [--type Formula|Chat] [--ids-only]\npwsh .\u002Freport_tools.ps1 rules-for-solution \u003CsolutionId>\npwsh .\u002Freport_tools.ps1 upgrade-solutions\npwsh .\u002Freport_tools.ps1 validate \u003Cpath\u002Fto\u002Freport.json>\n```\n\n- `list-solutions` prints the matching solutions from [scripts\u002Fsolution-mapping.json](scripts\u002Fsolution-mapping.json) as\n  whole JSON objects (each always has `solutionId`, `name`, `type`, and `tooltip`; some also carry `effort` \u002F `prompt`);\n  `--query` filters by substring on id\u002Fname\u002Ftooltip.\n- `rules-for-solution` prints `{ solutionId, ruleCount, rules: [{ruleId, sourceCategory}], preferredRuleId }`. An empty\n  list means the solution has **no** rule (e.g. a security-only solution) — do not invent a ruleId.\n- `upgrade-solutions` prints, per component (`jdk`, `spring-boot`, `spring-framework`, `jakarta-ee`), the `solutionId`\n  and its resolved `preferredRuleId`.\n- `validate` runs structural + cross-field consistency checks\n  (incident→rule references, required `domain`\u002F`category` rule fields, enum values, security-finding shape and dedupe, and\n  `metadata.domains`↔content consistency). Exit code is `0` valid, `1` invalid, `2` when the report can't be read\u002Fparsed.\n  It accepts a `--schema \u003Cpath>` flag for CLI compatibility but ignores it — the checks are self-contained.\n\n## Reference material\n\nDetailed reference material is consolidated in the [Reference](#reference) section at the end of this file. The workflow\nbelow links to the relevant part at the step where you need it — you do not have to open any separate files:\n\n- [Report structure](#report-structure) — the top-level `report.json` shape and the authoring conventions the schema can't express (the schema owns the field shape).\n- [Assessment domains](#assessment-domains) — the `metadata.domains` values and when to include each.\n- [Security severity mapping](#security-severity-mapping) — normalizing a source CVE\u002FCWE severity to the security `mandatory|potential|optional` scale.\n- [Rule classification](#rule-classification) — the required `domain` \u002F `category` rule fields that group and render a rule's incidents.\n\n## Workflow\n\n### Step 1 — Understand the report structure (do this first)\n\nRead the authoritative schema [scripts\u002Fassessment-report.schema.json](scripts\u002Fassessment-report.schema.json) — it is\nthe source of truth for the field shape. The report is a single root object `{ version, producer, metadata, projects,\nrules, security? }`, and `additionalProperties` is `false` almost everywhere (there is **no** `summary` object). The\n[report structure reference](#report-structure) adds the authoring conventions the schema can't express.\nYou must know the required fields before authoring anything.\n\n### Step 2 — Read and understand the CSV\n\nRead the CSV file directly (headers + a representative sample of rows). Then **interpret the columns by meaning**,\nsince names and order vary by tool. Identify whichever of these the CSV actually carries (any may be absent):\n\n- a **project \u002F module \u002F service** identifier,\n- an **application** name,\n- an issue **title** and **description**,\n- a **severity \u002F criticality \u002F priority**,\n- a **category \u002F domain \u002F type**,\n- a **CVE \u002F CWE** identifier,\n- an **affected component \u002F package \u002F library** and its **version**,\n- a **file** path and **line**,\n- an **effort \u002F story-point** estimate,\n- a **reference \u002F URL**.\n\nWhen a concept is missing, leave the corresponding report field empty or at its default — never fabricate data.\n\n### Step 3 — Determine the projects\n\nWork out **how many projects** the CSV describes and group rows accordingly:\n\n- If a project\u002Fmodule\u002Fservice column exists, group rows by it — one project per distinct value, using that value\n  as `project.path`.\n- If the CSV has **no project information**, create a **single project** with `path` `\".\"` and minimal properties\n  (`appName` `\"\"`; leave optional `jdkVersion` \u002F `frameworks` \u002F `languages` \u002F `tools` off or empty).\n- Populate `project.properties` **only** from what the CSV actually provides; otherwise keep them empty. Only `appName`\n  is required.\n\nEvery incident you create later belongs to exactly one of these projects.\n\n### Step 4 — Audit each row, within its project\n\nClassify every row into exactly one of three branches and attach the result to the row's project. Every rule you add to\n`rules{}` must set its `domain` and `category` **fields** (that is what groups and renders its incidents) — see\n[rule classification](#rule-classification). Incidents do **not** require any label.\n\n1. **CVE \u002F CWE → security finding (directly).**\n   When a row carries a `CVE-…` \u002F `CWE-…` identifier (or unambiguously describes one), add a security finding\n   to `report.security[]`. Capture **as much of the column as possible**: `id` (the CVE\u002FCWE token), `title`, `category`,\n   `severity` (the security scale `mandatory | potential | optional` — normalize the source severity per\n   [security issue severity mapping](#security-severity-mapping)), `description`, `evidence.files`\n   (affected paths), `evidence.explanation`, and optional `storyPoint`. **Merge by id** — one finding per CVE\u002FCWE;\n   accumulate evidence files and keep the strongest severity (`mandatory` > `potential` > `optional`). Security\n   findings are **not** incidents; they live only in `report.security[]` (there is no summary to count them in).\n\n2. **Needs a major-component version upgrade → upgrade incident.**\n   If the row implies upgrading a major component — **JDK**, **Spring Boot**, **Spring Framework**, or **Java EE \u002F\n   Jakarta EE** (e.g. a CVE against `spring-boot`, an out-of-support runtime, an explicit \"upgrade JDK\") — run\n   the `upgrade-solutions` command, pick the component, and use its `preferredRuleId`. Add that rule to `rules{}`\n   (`severity: \"mandatory\"`, `domain: \"java-upgrade\"`, a `category`, a reasonable `effort`) and add an **incident** to the\n   project — one upgrade rule per component per project, one incident per triggering row. A CVE that\n   implies an upgrade produces **both** a security finding (branch 1) **and** an upgrade incident — that is what makes the\n   upgrade resolve as a migration solution.\n\n3. **Any other issue → solution → ruleId → incident.**\n   Find the migration solution that fits the issue with\n   the `list-solutions --query \u003Ckeyword>` command, then get its canonical ruleId with\n   `rules-for-solution \u003CsolutionId>` (use `preferredRuleId`). Add that rule to `rules{}` (with `domain`,\n   `category`, `severity`, `effort`) and an incident to the project. If no solution fits, you may still record the issue\n   with a clear **synthetic** ruleId (it just won't\n   carry an automatic Formula solution) — or leave it for the \"remaining\" list in your summary. Either way, report it.\n\n#### Process rows in parallel\n\nClassifying a row is independent work, so for large CSVs do it concurrently rather than one row at a time:\n\n- **Batch the rows** (e.g. 20–50 per batch, or one batch per project) and dispatch the batches **in parallel** — launch\n  several `Explore`\u002Fworker subagents at once, each auditing its batch into a partial result (security findings, upgrade\n  hits, and ordinary incidents with their resolved ruleIds). Ask each worker to return structured JSON; it does **not**\n  write files.\n- **Share the deterministic lookups.** Run the `upgrade-solutions` command and the `list-solutions` \u002F\n  `rules-for-solution` queries **once up front** (results are stable) and pass them to the workers, so parallel batches\n  don't repeat the same lookups or race on them.\n- **Keep workers side-effect free**, then **merge sequentially** in one place so shared state stays correct:\n  - **Security findings** — merge by `id` (one finding per CVE\u002FCWE; union `evidence.files`, keep the strongest severity).\n  - **Upgrade rules** — collapse to one rule per `(project, component)`; keep every triggering incident.\n  - **`incidentId`s** — assign `\"\u003CruleId>\u002F\u003Cn>\"` **after** the merge, never inside a worker (so ids are deterministic\n    regardless of batch order).\n- If the CSV is small, just process the rows sequentially — the parallel split only pays off at scale.\n\n### Step 5 — Assemble the report and finalize metadata\n\nThere is **no** `summary` object to compute — assemble the top-level document and fill `metadata`:\n\n- `projects[]` = your projects, each with `properties` (only `appName` required) and its `incidents[]`.\n- `rules{}` = every distinct rule you referenced, keyed by ruleId, each with `id`, `title`, `severity`, `effort`,\n  `domain`, and `category`.\n- `report.security[]` = the deduped findings (omit or leave empty when there are none).\n- `metadata.domains` = the assessment domains your report actually has content for, consistent with your `rule.domain`\n  values — see [assessment domains](#assessment-domains).\n- `metadata.mode` (optional) = `\"full\"` when any security finding exists, else `\"issue-only\"`.\n- `metadata.status` = `\"completed\"`; `metadata.targetIds` = the target ids in scope (may be empty).\n- `metadata.id` (and the report-directory id) = `analysisStartTime` formatted `yyyyMMddHHmmss` (UTC); use the current UTC\n  time when the CSV has no timestamp.\n\n### Step 6 — Write and validate\n\nWrite `report.json` to the versioned location ([Output location](#output-location)), then validate and fix until clean:\n\n```bash\n# bash + jq\n.\u002Freport_tools.sh validate \"\u003Cworkspace>\u002F.github\u002Fmodernize\u002Fassessment\u002Freports\u002Freport-\u003Cid>\u002Freport.json\"\n# …or PowerShell 7+\npwsh .\u002Freport_tools.ps1 validate \"\u003Cworkspace>\u002F.github\u002Fmodernize\u002Fassessment\u002Freports\u002Freport-\u003Cid>\u002Freport.json\"\n```\n\nResolve every reported consistency error before finishing.\n\n### Step 7 — Summarize for the user\n\nReport a concise conversion summary:\n\n- **Converted rows** — number of security findings (CVE\u002FCWE), upgrade incidents (with components), and ordinary issue\n  incidents (with the solutions they mapped to); plus the project \u002F rule \u002F incident counts and the report path + id.\n- **Remaining rows** — rows you could not confidently map, and *why* (no matching solution, ambiguous column, missing id).\n- **Suggestions** — concrete next steps (e.g. pick a specific solution for a remaining row, add a project column to the\n  CSV, supply severities), so the user can close the gaps.\n\n## Output location\n\n- `{workspace-path}\u002F.github\u002Fmodernize\u002Fassessment\u002Freports\u002Freport-{reportId}\u002Freport.json`\n- `reportId` = the report's `metadata.id` from Step 5 (`analysisStartTime` as `yyyyMMddHHmmss`, UTC).\n- Consider copying the original CSV next to `report.json` as `source.csv` for provenance.\n\n## Success criteria\n\n- ✅ `report.json` is written to the versioned reports directory and the `validate` command reports **VALID**.\n- ✅ Projects reflect the CSV (one per module\u002Fservice, or a single project with `appName: \"\"` when none is given).\n- ✅ CVE\u002FCWE rows are `security[]` findings (deduped by id) with a `mandatory|potential|optional` severity; `mode` is `full`.\n- ✅ Rows implying a JDK \u002F Spring Boot \u002F Spring Framework \u002F Jakarta EE upgrade add a mandatory upgrade incident whose\n  `ruleId` came from the `upgrade-solutions` command.\n- ✅ Other issues map to a solution's canonical `ruleId` (via `list-solutions` + `rules-for-solution`) wherever one fits.\n- ✅ Every rule carries `domain` and `category` fields; every incident's `ruleId` resolves to a rule in `rules{}`; CVE\u002FCWE\n  findings stay in `security[]`, not `incidents[]`.\n- ✅ `metadata.domains` matches the report's content and the emitted `rule.domain` values (enforced by `validate`).\n- ✅ The user gets a summary of converted rows, remaining rows, and suggestions.\n\n## Troubleshooting\n\n- **`validate` reports a consistency error** (e.g. an incident `ruleId` with no matching rule, or a\n  `domains`\u002F`security` mismatch) — the message names the exact field; fix that field.\n- **Don't add fields the schema doesn't allow** (e.g. a `summary` object, or `issues`\u002F`storyPoints` on a project). The\n  helper's `validate` is lenient about extra keys, but the app's importer enforces `additionalProperties: false` and will\n  reject the report — this schema has no summary. Keep to the documented shape.\n- **A row has a CVE\u002FCWE *and* needs an upgrade** — emit both: a `security[]` finding **and** an upgrade incident. They are\n  not duplicates; the finding documents the vulnerability, the incident drives the upgrade solution.\n- **No solution fits an issue** — `list-solutions --query` returns nothing useful. Record the issue with a synthetic,\n  descriptive `ruleId` (no Formula will attach) or list it under \"remaining\" with a suggestion.\n- **`rules-for-solution` returns an empty list** — that solution has no rule (often security-only). Don't fabricate a\n  ruleId; handle the issue via the security-finding path or pick a different solution.\n- **Wrong severity enum** — rule\u002Fincident severity is the 4-value `mandatory|potential|optional|information` enum;\n  security-finding severity uses the same scale **minus `information`** (`mandatory|potential|optional`, since a finding\n  is always at least optional). Don't use the old `critical|high|medium|low|info` values.\n\n## Reference\n\nConsolidated reference material. The workflow above links here at the step where each part is needed.\n\n### Report structure\n\nYou author a single `report.json` conforming to the authoritative schema\n[scripts\u002Fassessment-report.schema.json](scripts\u002Fassessment-report.schema.json). The schema is one root object\n(draft-07, its sub-types live under `definitions`) with the top-level shape `{ version, producer, metadata, projects,\nrules, security? }`. Read the schema for the exact required fields, types, and enums per object — it is the source of\ntruth and the `validate` command checks it, so this section does **not** restate the field list. `additionalProperties`\nis `false` almost everywhere, so **do not invent fields** (there is no `summary` object, and projects have no `issues` \u002F\n`storyPoints`).\n\n**Authoring rules of thumb** — the conventions and cross-field rules the schema can't fully express:\n\n- `version` is the string `\"1.0.0\"` (the schema accepts any string; this is the value to use).\n- Rule\u002Fincident `severity` is the enum `mandatory | potential | optional | information`. Map source severities by meaning\n  (e.g. critical\u002Fblocker → `mandatory`, major\u002Fmedium → `potential`, minor\u002Flow → `optional`, info → `information`).\n- **Classification is done with rule fields, not labels.** Each `rules{}` entry sets `domain`\n  (`cloud-readiness | java-upgrade | security`) and `category` (a free string heading) directly — see\n  [Rule classification](#rule-classification). `rules{}.labels` and `incidents[].labels` are optional free-form arrays;\n  you normally leave them out.\n- Security-finding `severity` uses the report criticality scale `mandatory | potential | optional` (the rule `Severity`\n  values minus `information` — a security finding is always at least optional) — normalize per\n  [Security severity mapping](#security-severity-mapping).\n- `metadata.domains` records which assessment domains the report has content for — see\n  [Assessment domains](#assessment-domains). It must be consistent with the `rule.domain` values you emit.\n- `incidentId` convention: `\"\u003CruleId>\u002F\u003Cn>\"` (n is a per-rule counter).\n- `locationKind` = `\"source-file\"` when a file path is present, else `\"unknown\"`.\n- `status` is normally `\"completed\"`.\n- `metadata.mode` is optional; use `\"full\"` when the report has security findings, else `\"issue-only\"`.\n\n### Assessment domains\n\n`metadata.domains` is a `string[]` recording which assessment **domains** produced the report. Allowed values are\n`cloud-readiness`, `java-upgrade`, and `security`.\n\n| Domain | Meaning | When to include it for a CSV conversion |\n|--------|---------|------------------------------------------|\n| `cloud-readiness` | Azure cloud-migration issues | any ordinary issue → solution incident (branch 3) |\n| `java-upgrade` | JDK \u002F Spring Boot \u002F Spring Framework \u002F Jakarta EE upgrades | any upgrade incident (branch 2) |\n| `security` | CVE \u002F CWE vulnerability findings | `report.security[]` is non-empty (branch 1) |\n\n- The native Java default is `[\"cloud-readiness\", \"java-upgrade\"]`.\n- Set `metadata.domains` to **exactly the domains your report has content for** — don't list `security` with no\n  findings, or `java-upgrade` with no upgrade incidents. `validate` flags a `security`\u002F`report.security` mismatch in\n  either direction.\n- `metadata.domains` must be consistent with the `rule.domain` **field** on your rules — every `rule.domain` value you\n  emit should appear in `metadata.domains`.\n\n### Security severity mapping\n\nA security finding's `severity` uses the report criticality scale — `mandatory | potential | optional`. This is the rule\n`Severity` enum **minus `information`** (a security finding is always at least optional). The extension renders these\nvalues directly, so there is no separate security severity scale and no conversion step.\n\n**Map the source CVE\u002FCWE severity by meaning to the nearest value** (case-insensitive):\n\n| Source severity (CSV, case-insensitive) | Report `security[].severity` |\n|-----------------------------------------|------------------------------|\n| `critical` \u002F `blocker`                  | `mandatory`                  |\n| `high`                                  | `mandatory`                  |\n| `medium` \u002F `moderate`                   | `potential`                  |\n| `low`                                   | `optional`                   |\n| anything else \u002F unknown \u002F missing       | `optional`                   |\n\n- When merging duplicate findings by `id`, keep the **strongest** severity (`mandatory` > `potential` > `optional`).\n- Security findings live in `report.security[]` only; there is no `summary` object to key by severity. `validate` checks\n  each finding's `severity` is one of the three values and that findings are unique by `id`.\n- Include the `security` domain in `metadata.domains` whenever `report.security[]` is non-empty (and only then).\n\n### Rule classification\n\nIn this schema a rule is classified with **fields on the rule object**, not with labels. Every `rules{}` entry is\n**required** to carry a `domain` and a `category` (the `validate` command enforces both, and the schema rejects a\nrule that is missing them).\n\n| Field | Value | Effect |\n|-------|-------|--------|\n| `domain` | `cloud-readiness` \\| `java-upgrade` \\| `security` | Groups the rule under that domain tab. Use the same domain you list in `metadata.domains`. **Required.** |\n| `category` | the issue category heading (e.g. `postgresql`, `java-version-upgrade`, `deprecated-apis`) | Shown as the group heading. **Required.** |\n\n**Choosing `category` (and the source label).** Each mapped rule also has a `sourceCategory` in the solution mapping,\nwhich you get from `rules-for-solution \u003CsolutionId>` (`rules[].sourceCategory`). The UI builds the group heading as\n`category` when it **equals** `sourceCategory` (or `sourceCategory` is empty \u002F `null`), otherwise as\n`category (sourceCategory)`. So:\n\n- When a rule's `sourceCategory` is **non-null**, set its `category` to **exactly that value** — e.g.\n  `mi-postgresql` → rule `azure-database-postgresql-02000`, `sourceCategory: \"postgresql\"` → `category: \"postgresql\"`,\n  which renders as one clean **Postgresql** heading (mismatching it, e.g. `category: \"database\"`, would render the\n  doubled **Database (Postgresql)**).\n- When `sourceCategory` is **`null`** (e.g. the JDK-upgrade rules), choose a sensible `category` yourself\n  (e.g. `upgrade`) — it renders as-is.\n\n**Labels are optional.** `rules{}.labels` and `incidents[].labels` are optional free-form `string[]`s in the schema.\nClassification no longer depends on them, so you normally leave them out. The engine may still emit context labels\n(`target=`, `os=`, `capability=`) on native reports, but when hand-authoring a CSV conversion you do not need any label\nto make content render — the required `domain`\u002F`category` **fields** do that.\n","# Assessment Report Converter (CSV → report.json)\n\n## What this skill does\n\nYou take a **row-oriented CSV** of migration issues and produce a canonical **`report.json`** conforming to the\nassessment report schema. Once written to the versioned reports directory, the\nreport is picked up by the assessment UI and by the solution-resolution logic — exactly like a native AppCAT report.\n\n**This is an LLM-driven conversion, not a fixed mapping script.** CSV layouts vary between tools and change over time,\nso *you* read the file, decide what each column means, classify every row, and author `report.json`. You do **not**\nrely on hard-coded column names.\n\nA small helper script — [scripts\u002Freport_tools.sh](scripts\u002Freport_tools.sh) (bash + jq) or its PowerShell twin\n[scripts\u002Freport_tools.ps1](scripts\u002Freport_tools.ps1) (PowerShell 7+) — provides only the deterministic pieces\nyou should never guess:\n\n- **`list-solutions`** — discover the available migration solutions.\n- **`rules-for-solution`** — get the canonical `ruleId`(s) for a chosen solution.\n- **`upgrade-solutions`** — the canonical JDK \u002F Spring Boot \u002F Spring Framework \u002F Jakarta EE upgrade solutions + ruleIds.\n- **`validate`** — structural + consistency validation of the finished `report.json`.\n\n```mermaid\nflowchart LR\n    A[Read CSV] --> B[Understand columns] --> C[Find projects] --> D{Audit each row}\n    D -->|CVE \u002F CWE| E[Security finding]\n    D -->|Needs upgrade| F[Upgrade incident]\n    D -->|Other| G[Solution → ruleId → incident]\n    E --> H[Assemble report]\n    F --> H\n    G --> H\n    H --> I[Validate] --> J[Save report.json] --> K[Summarize]\n```\n\n## Input parameters\n\n- `csv-path` (mandatory): Path to the source CSV file.\n- `workspace-path` (optional): Output root. Defaults to the current directory. The report is written to\n  `{workspace-path}\u002F.github\u002Fmodernize\u002Fassessment\u002Freports\u002Freport-{reportId}\u002Freport.json`.\n- `producer` (optional): A label identifying the source tool, stored in `report.producer` (e.g. `\"Black Duck\"`).\n  Defaults to `\"CSV import\"`.\n\n## When to use this skill\n\nUse this when you have a **CSV** — not an AppCAT `report.json` — and you want it to behave like a real assessment\nreport: a Black Duck \u002F third-party export, or a hand-maintained spreadsheet of migration issues. Do **not** use it to\nrun analysis from source code (that is the `assessment` skill).\n\n## The helper script\n\nThe helper ships as two interchangeable implementations of the same CLI — use whichever fits the machine:\n[scripts\u002Freport_tools.sh](scripts\u002Freport_tools.sh) (**bash + jq**) and\n[scripts\u002Freport_tools.ps1](scripts\u002Freport_tools.ps1) (**PowerShell 7+**, no extra dependencies). Both behave\nidentically and never read the CSV. Run them from the `scripts\u002F` directory.\n\n```bash\n# bash + jq\n.\u002Freport_tools.sh list-solutions [--query KW] [--type Formula|Chat] [--ids-only]\n.\u002Freport_tools.sh rules-for-solution \u003CsolutionId>\n.\u002Freport_tools.sh upgrade-solutions\n.\u002Freport_tools.sh validate \u003Cpath\u002Fto\u002Freport.json>\n```\n\n```powershell\n# PowerShell 7+\npwsh .\u002Freport_tools.ps1 list-solutions [--query KW] [--type Formula|Chat] [--ids-only]\npwsh .\u002Freport_tools.ps1 rules-for-solution \u003CsolutionId>\npwsh .\u002Freport_tools.ps1 upgrade-solutions\npwsh .\u002Freport_tools.ps1 validate \u003Cpath\u002Fto\u002Freport.json>\n```\n\n- `list-solutions` prints the matching solutions from [scripts\u002Fsolution-mapping.json](scripts\u002Fsolution-mapping.json) as\n  whole JSON objects (each always has `solutionId`, `name`, `type`, and `tooltip`; some also carry `effort` \u002F `prompt`);\n  `--query` filters by substring on id\u002Fname\u002Ftooltip.\n- `rules-for-solution` prints `{ solutionId, ruleCount, rules: [{ruleId, sourceCategory}], preferredRuleId }`. An empty\n  list means the solution has **no** rule (e.g. a security-only solution) — do not invent a ruleId.\n- `upgrade-solutions` prints, per component (`jdk`, `spring-boot`, `spring-framework`, `jakarta-ee`), the `solutionId`\n  and its resolved `preferredRuleId`.\n- `validate` runs structural + cross-field consistency checks\n  (incident→rule references, required `domain`\u002F`category` rule fields, enum values, security-finding shape and dedupe, and\n  `metadata.domains`↔content consistency). Exit code is `0` valid, `1` invalid, `2` when the report can't be read\u002Fparsed.\n  It accepts a `--schema \u003Cpath>` flag for CLI compatibility but ignores it — the checks are self-contained.\n\n## Reference material\n\nDetailed reference material is consolidated in the [Reference](#reference) section at the end of this file. The workflow\nbelow links to the relevant part at the step where you need it — you do not have to open any separate files:\n\n- [Report structure](#report-structure) — the top-level `report.json` shape and the authoring conventions the schema can't express (the schema owns the field shape).\n- [Assessment domains](#assessment-domains) — the `metadata.domains` values and when to include each.\n- [Security severity mapping](#security-severity-mapping) — normalizing a source CVE\u002FCWE severity to the security `mandatory|potential|optional` scale.\n- [Rule classification](#rule-classification) — the required `domain` \u002F `category` rule fields that group and render a rule's incidents.\n\n## Workflow\n\n### Step 1 — Understand the report structure (do this first)\n\nRead the authoritative schema [scripts\u002Fassessment-report.schema.json](scripts\u002Fassessment-report.schema.json) — it is\nthe source of truth for the field shape. The report is a single root object `{ version, producer, metadata, projects,\nrules, security? }`, and `additionalProperties` is `false` almost everywhere (there is **no** `summary` object). The\n[report structure reference](#report-structure) adds the authoring conventions the schema can't express.\nYou must know the required fields before authoring anything.\n\n### Step 2 — Read and understand the CSV\n\nRead the CSV file directly (headers + a representative sample of rows). Then **interpret the columns by meaning**,\nsince names and order vary by tool. Identify whichever of these the CSV actually carries (any may be absent):\n\n- a **project \u002F module \u002F service** identifier,\n- an **application** name,\n- an issue **title** and **description**,\n- a **severity \u002F criticality \u002F priority**,\n- a **category \u002F domain \u002F type**,\n- a **CVE \u002F CWE** identifier,\n- an **affected component \u002F package \u002F library** and its **version**,\n- a **file** path and **line**,\n- an **effort \u002F story-point** estimate,\n- a **reference \u002F URL**.\n\nWhen a concept is missing, leave the corresponding report field empty or at its default — never fabricate data.\n\n### Step 3 — Determine the projects\n\nWork out **how many projects** the CSV describes and group rows accordingly:\n\n- If a project\u002Fmodule\u002Fservice column exists, group rows by it — one project per distinct value, using that value\n  as `project.path`.\n- If the CSV has **no project information**, create a **single project** with `path` `\".\"` and minimal properties\n  (`appName` `\"\"`; leave optional `jdkVersion` \u002F `frameworks` \u002F `languages` \u002F `tools` off or empty).\n- Populate `project.properties` **only** from what the CSV actually provides; otherwise keep them empty. Only `appName`\n  is required.\n\nEvery incident you create later belongs to exactly one of these projects.\n\n### Step 4 — Audit each row, within its project\n\nClassify every row into exactly one of three branches and attach the result to the row's project. Every rule you add to\n`rules{}` must set its `domain` and `category` **fields** (that is what groups and renders its incidents) — see\n[rule classification](#rule-classification). Incidents do **not** require any label.\n\n1. **CVE \u002F CWE → security finding (directly).**\n   When a row carries a `CVE-…` \u002F `CWE-…` identifier (or unambiguously describes one), add a security finding\n   to `report.security[]`. Capture **as much of the column as possible**: `id` (the CVE\u002FCWE token), `title`, `category`,\n   `severity` (the security scale `mandatory | potential | optional` — normalize the source severity per\n   [security issue severity mapping](#security-severity-mapping)), `description`, `evidence.files`\n   (affected paths), `evidence.explanation`, and optional `storyPoint`. **Merge by id** — one finding per CVE\u002FCWE;\n   accumulate evidence files and keep the strongest severity (`mandatory` > `potential` > `optional`). Security\n   findings are **not** incidents; they live only in `report.security[]` (there is no summary to count them in).\n\n2. **Needs a major-component version upgrade → upgrade incident.**\n   If the row implies upgrading a major component — **JDK**, **Spring Boot**, **Spring Framework**, or **Java EE \u002F\n   Jakarta EE** (e.g. a CVE against `spring-boot`, an out-of-support runtime, an explicit \"upgrade JDK\") — run\n   the `upgrade-solutions` command, pick the component, and use its `preferredRuleId`. Add that rule to `rules{}`\n   (`severity: \"mandatory\"`, `domain: \"java-upgrade\"`, a `category`, a reasonable `effort`) and add an **incident** to the\n   project — one upgrade rule per component per project, one incident per triggering row. A CVE that\n   implies an upgrade produces **both** a security finding (branch 1) **and** an upgrade incident — that is what makes the\n   upgrade resolve as a migration solution.\n\n3. **Any other issue → solution → ruleId → incident.**\n   Find the migration solution that fits the issue with\n   the `list-solutions --query \u003Ckeyword>` command, then get its canonical ruleId with\n   `rules-for-solution \u003CsolutionId>` (use `preferredRuleId`). Add that rule to `rules{}` (with `domain`,\n   `category`, `severity`, `effort`) and an incident to the project. If no solution fits, you may still record the issue\n   with a clear **synthetic** ruleId (it just won't\n   carry an automatic Formula solution) — or leave it for the \"remaining\" list in your summary. Either way, report it.\n\n#### Process rows in parallel\n\nClassifying a row is independent work, so for large CSVs do it concurrently rather than one row at a time:\n\n- **Batch the rows** (e.g. 20–50 per batch, or one batch per project) and dispatch the batches **in parallel** — launch\n  several `Explore`\u002Fworker subagents at once, each auditing its batch into a partial result (security findings, upgrade\n  hits, and ordinary incidents with their resolved ruleIds). Ask each worker to return structured JSON; it does **not**\n  write files.\n- **Share the deterministic lookups.** Run the `upgrade-solutions` command and the `list-solutions` \u002F\n  `rules-for-solution` queries **once up front** (results are stable) and pass them to the workers, so parallel batches\n  don't repeat the same lookups or race on them.\n- **Keep workers side-effect free**, then **merge sequentially** in one place so shared state stays correct:\n  - **Security findings** — merge by `id` (one finding per CVE\u002FCWE; union `evidence.files`, keep the strongest severity).\n  - **Upgrade rules** — collapse to one rule per `(project, component)`; keep every triggering incident.\n  - **`incidentId`s** — assign `\"\u003CruleId>\u002F\u003Cn>\"` **after** the merge, never inside a worker (so ids are deterministic\n    regardless of batch order).\n- If the CSV is small, just process the rows sequentially — the parallel split only pays off at scale.\n\n### Step 5 — Assemble the report and finalize metadata\n\nThere is **no** `summary` object to compute — assemble the top-level document and fill `metadata`:\n\n- `projects[]` = your projects, each with `properties` (only `appName` required) and its `incidents[]`.\n- `rules{}` = every distinct rule you referenced, keyed by ruleId, each with `id`, `title`, `severity`, `effort`,\n  `domain`, and `category`.\n- `report.security[]` = the deduped findings (omit or leave empty when there are none).\n- `metadata.domains` = the assessment domains your report actually has content for, consistent with your `rule.domain`\n  values — see [assessment domains](#assessment-domains).\n- `metadata.mode` (optional) = `\"full\"` when any security finding exists, else `\"issue-only\"`.\n- `metadata.status` = `\"completed\"`; `metadata.targetIds` = the target ids in scope (may be empty).\n- `metadata.id` (and the report-directory id) = `analysisStartTime` formatted `yyyyMMddHHmmss` (UTC); use the current UTC\n  time when the CSV has no timestamp.\n\n### Step 6 — Write and validate\n\nWrite `report.json` to the versioned location ([Output location](#output-location)), then validate and fix until clean:\n\n```bash\n# bash + jq\n.\u002Freport_tools.sh validate \"\u003Cworkspace>\u002F.github\u002Fmodernize\u002Fassessment\u002Freports\u002Freport-\u003Cid>\u002Freport.json\"\n# …or PowerShell 7+\npwsh .\u002Freport_tools.ps1 validate \"\u003Cworkspace>\u002F.github\u002Fmodernize\u002Fassessment\u002Freports\u002Freport-\u003Cid>\u002Freport.json\"\n```\n\nResolve every reported consistency error before finishing.\n\n### Step 7 — Summarize for the user\n\nReport a concise conversion summary:\n\n- **Converted rows** — number of security findings (CVE\u002FCWE), upgrade incidents (with components), and ordinary issue\n  incidents (with the solutions they mapped to); plus the project \u002F rule \u002F incident counts and the report path + id.\n- **Remaining rows** — rows you could not confidently map, and *why* (no matching solution, ambiguous column, missing id).\n- **Suggestions** — concrete next steps (e.g. pick a specific solution for a remaining row, add a project column to the\n  CSV, supply severities), so the user can close the gaps.\n\n## Output location\n\n- `{workspace-path}\u002F.github\u002Fmodernize\u002Fassessment\u002Freports\u002Freport-{reportId}\u002Freport.json`\n- `reportId` = the report's `metadata.id` from Step 5 (`analysisStartTime` as `yyyyMMddHHmmss`, UTC).\n- Consider copying the original CSV next to `report.json` as `source.csv` for provenance.\n\n## Success criteria\n\n- ✅ `report.json` is written to the versioned reports directory and the `validate` command reports **VALID**.\n- ✅ Projects reflect the CSV (one per module\u002Fservice, or a single project with `appName: \"\"` when none is given).\n- ✅ CVE\u002FCWE rows are `security[]` findings (deduped by id) with a `mandatory|potential|optional` severity; `mode` is `full`.\n- ✅ Rows implying a JDK \u002F Spring Boot \u002F Spring Framework \u002F Jakarta EE upgrade add a mandatory upgrade incident whose\n  `ruleId` came from the `upgrade-solutions` command.\n- ✅ Other issues map to a solution's canonical `ruleId` (via `list-solutions` + `rules-for-solution`) wherever one fits.\n- ✅ Every rule carries `domain` and `category` fields; every incident's `ruleId` resolves to a rule in `rules{}`; CVE\u002FCWE\n  findings stay in `security[]`, not `incidents[]`.\n- ✅ `metadata.domains` matches the report's content and the emitted `rule.domain` values (enforced by `validate`).\n- ✅ The user gets a summary of converted rows, remaining rows, and suggestions.\n\n## Troubleshooting\n\n- **`validate` reports a consistency error** (e.g. an incident `ruleId` with no matching rule, or a\n  `domains`\u002F`security` mismatch) — the message names the exact field; fix that field.\n- **Don't add fields the schema doesn't allow** (e.g. a `summary` object, or `issues`\u002F`storyPoints` on a project). The\n  helper's `validate` is lenient about extra keys, but the app's importer enforces `additionalProperties: false` and will\n  reject the report — this schema has no summary. Keep to the documented shape.\n- **A row has a CVE\u002FCWE *and* needs an upgrade** — emit both: a `security[]` finding **and** an upgrade incident. They are\n  not duplicates; the finding documents the vulnerability, the incident drives the upgrade solution.\n- **No solution fits an issue** — `list-solutions --query` returns nothing useful. Record the issue with a synthetic,\n  descriptive `ruleId` (no Formula will attach) or list it under \"remaining\" with a suggestion.\n- **`rules-for-solution` returns an empty list** — that solution has no rule (often security-only). Don't fabricate a\n  ruleId; handle the issue via the security-finding path or pick a different solution.\n- **Wrong severity enum** — rule\u002Fincident severity is the 4-value `mandatory|potential|optional|information` enum;\n  security-finding severity uses the same scale **minus `information`** (`mandatory|potential|optional`, since a finding\n  is always at least optional). Don't use the old `critical|high|medium|low|info` values.\n\n## Reference\n\nConsolidated reference material. The workflow above links here at the step where each part is needed.\n\n### Report structure\n\nYou author a single `report.json` conforming to the authoritative schema\n[scripts\u002Fassessment-report.schema.json](scripts\u002Fassessment-report.schema.json). The schema is one root object\n(draft-07, its sub-types live under `definitions`) with the top-level shape `{ version, producer, metadata, projects,\nrules, security? }`. Read the schema for the exact required fields, types, and enums per object — it is the source of\ntruth and the `validate` command checks it, so this section does **not** restate the field list. `additionalProperties`\nis `false` almost everywhere, so **do not invent fields** (there is no `summary` object, and projects have no `issues` \u002F\n`storyPoints`).\n\n**Authoring rules of thumb** — the conventions and cross-field rules the schema can't fully express:\n\n- `version` is the string `\"1.0.0\"` (the schema accepts any string; this is the value to use).\n- Rule\u002Fincident `severity` is the enum `mandatory | potential | optional | information`. Map source severities by meaning\n  (e.g. critical\u002Fblocker → `mandatory`, major\u002Fmedium → `potential`, minor\u002Flow → `optional`, info → `information`).\n- **Classification is done with rule fields, not labels.** Each `rules{}` entry sets `domain`\n  (`cloud-readiness | java-upgrade | security`) and `category` (a free string heading) directly — see\n  [Rule classification](#rule-classification). `rules{}.labels` and `incidents[].labels` are optional free-form arrays;\n  you normally leave them out.\n- Security-finding `severity` uses the report criticality scale `mandatory | potential | optional` (the rule `Severity`\n  values minus `information` — a security finding is always at least optional) — normalize per\n  [Security severity mapping](#security-severity-mapping).\n- `metadata.domains` records which assessment domains the report has content for — see\n  [Assessment domains](#assessment-domains). It must be consistent with the `rule.domain` values you emit.\n- `incidentId` convention: `\"\u003CruleId>\u002F\u003Cn>\"` (n is a per-rule counter).\n- `locationKind` = `\"source-file\"` when a file path is present, else `\"unknown\"`.\n- `status` is normally `\"completed\"`.\n- `metadata.mode` is optional; use `\"full\"` when the report has security findings, else `\"issue-only\"`.\n\n### Assessment domains\n\n`metadata.domains` is a `string[]` recording which assessment **domains** produced the report. Allowed values are\n`cloud-readiness`, `java-upgrade`, and `security`.\n\n| Domain | Meaning | When to include it for a CSV conversion |\n|--------|---------|------------------------------------------|\n| `cloud-readiness` | Azure cloud-migration issues | any ordinary issue → solution incident (branch 3) |\n| `java-upgrade` | JDK \u002F Spring Boot \u002F Spring Framework \u002F Jakarta EE upgrades | any upgrade incident (branch 2) |\n| `security` | CVE \u002F CWE vulnerability findings | `report.security[]` is non-empty (branch 1) |\n\n- The native Java default is `[\"cloud-readiness\", \"java-upgrade\"]`.\n- Set `metadata.domains` to **exactly the domains your report has content for** — don't list `security` with no\n  findings, or `java-upgrade` with no upgrade incidents. `validate` flags a `security`\u002F`report.security` mismatch in\n  either direction.\n- `metadata.domains` must be consistent with the `rule.domain` **field** on your rules — every `rule.domain` value you\n  emit should appear in `metadata.domains`.\n\n### Security severity mapping\n\nA security finding's `severity` uses the report criticality scale — `mandatory | potential | optional`. This is the rule\n`Severity` enum **minus `information`** (a security finding is always at least optional). The extension renders these\nvalues directly, so there is no separate security severity scale and no conversion step.\n\n**Map the source CVE\u002FCWE severity by meaning to the nearest value** (case-insensitive):\n\n| Source severity (CSV, case-insensitive) | Report `security[].severity` |\n|-----------------------------------------|------------------------------|\n| `critical` \u002F `blocker`                  | `mandatory`                  |\n| `high`                                  | `mandatory`                  |\n| `medium` \u002F `moderate`                   | `potential`                  |\n| `low`                                   | `optional`                   |\n| anything else \u002F unknown \u002F missing       | `optional`                   |\n\n- When merging duplicate findings by `id`, keep the **strongest** severity (`mandatory` > `potential` > `optional`).\n- Security findings live in `report.security[]` only; there is no `summary` object to key by severity. `validate` checks\n  each finding's `severity` is one of the three values and that findings are unique by `id`.\n- Include the `security` domain in `metadata.domains` whenever `report.security[]` is non-empty (and only then).\n\n### Rule classification\n\nIn this schema a rule is classified with **fields on the rule object**, not with labels. Every `rules{}` entry is\n**required** to carry a `domain` and a `category` (the `validate` command enforces both, and the schema rejects a\nrule that is missing them).\n\n| Field | Value | Effect |\n|-------|-------|--------|\n| `domain` | `cloud-readiness` \\| `java-upgrade` \\| `security` | Groups the rule under that domain tab. Use the same domain you list in `metadata.domains`. **Required.** |\n| `category` | the issue category heading (e.g. `postgresql`, `java-version-upgrade`, `deprecated-apis`) | Shown as the group heading. **Required.** |\n\n**Choosing `category` (and the source label).** Each mapped rule also has a `sourceCategory` in the solution mapping,\nwhich you get from `rules-for-solution \u003CsolutionId>` (`rules[].sourceCategory`). The UI builds the group heading as\n`category` when it **equals** `sourceCategory` (or `sourceCategory` is empty \u002F `null`), otherwise as\n`category (sourceCategory)`. So:\n\n- When a rule's `sourceCategory` is **non-null**, set its `category` to **exactly that value** — e.g.\n  `mi-postgresql` → rule `azure-database-postgresql-02000`, `sourceCategory: \"postgresql\"` → `category: \"postgresql\"`,\n  which renders as one clean **Postgresql** heading (mismatching it, e.g. `category: \"database\"`, would render the\n  doubled **Database (Postgresql)**).\n- When `sourceCategory` is **`null`** (e.g. the JDK-upgrade rules), choose a sensible `category` yourself\n  (e.g. `upgrade`) — it renders as-is.\n\n**Labels are optional.** `rules{}.labels` and `incidents[].labels` are optional free-form `string[]`s in the schema.\nClassification no longer depends on them, so you normally leave them out. The engine may still emit context labels\n(`target=`, `os=`, `capability=`) on native reports, but when hand-authoring a CSV conversion you do not need any label\nto make content render — the required `domain`\u002F`category` **fields** do that.\n",{"name":5,"description":7},"main","a9c9469d86e9dc078018e8fb391abc7744088078","plugins\u002Fgithub-copilot-modernization\u002Fskills\u002Fassessment-report-converter",15,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fgithub-copilot-modernization","https:\u002F\u002Fgithub.com\u002Fmicrosoft","https:\u002F\u002Fopensource.microsoft.com","Open source projects and samples from Microsoft","2026-09-24 00:17:59",{"source":8,"skillName":5,"commands":50,"prompt":60,"zipAvailable":61},[51,54,57],{"tool":52,"command":53},"npx","npx skills add microsoft\u002Fgithub-copilot-modernization --skill assessment-report-converter",{"tool":55,"command":56},"bunx","bunx skills add microsoft\u002Fgithub-copilot-modernization --skill assessment-report-converter",{"tool":58,"command":59},"pnpm","pnpm dlx skills add microsoft\u002Fgithub-copilot-modernization --skill assessment-report-converter","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fgithub-copilot-modernization\u002Fblob\u002Fmain\u002Fplugins\u002Fgithub-copilot-modernization\u002Fskills\u002Fassessment-report-converter\u002FSKILL.md",true,[63,69,73,77,81,85],{"path":64,"resourceType":65,"resourceTypeDesc":66,"sizeBytes":67,"sourceHtmlUrl":68},"scripts\u002Fassessment-report.schema.json","SCRIPT","脚本",8052,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fgithub-copilot-modernization\u002Fblob\u002Fmain\u002Fplugins\u002Fgithub-copilot-modernization\u002Fskills\u002Fassessment-report-converter\u002Fscripts\u002Fassessment-report.schema.json",{"path":70,"resourceType":65,"resourceTypeDesc":66,"sizeBytes":71,"sourceHtmlUrl":72},"scripts\u002Freport_tools.ps1",21703,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fgithub-copilot-modernization\u002Fblob\u002Fmain\u002Fplugins\u002Fgithub-copilot-modernization\u002Fskills\u002Fassessment-report-converter\u002Fscripts\u002Freport_tools.ps1",{"path":74,"resourceType":65,"resourceTypeDesc":66,"sizeBytes":75,"sourceHtmlUrl":76},"scripts\u002Freport_tools.sh",15864,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fgithub-copilot-modernization\u002Fblob\u002Fmain\u002Fplugins\u002Fgithub-copilot-modernization\u002Fskills\u002Fassessment-report-converter\u002Fscripts\u002Freport_tools.sh",{"path":78,"resourceType":65,"resourceTypeDesc":66,"sizeBytes":79,"sourceHtmlUrl":80},"scripts\u002Fsolution-mapping-schema.json",12198,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fgithub-copilot-modernization\u002Fblob\u002Fmain\u002Fplugins\u002Fgithub-copilot-modernization\u002Fskills\u002Fassessment-report-converter\u002Fscripts\u002Fsolution-mapping-schema.json",{"path":82,"resourceType":65,"resourceTypeDesc":66,"sizeBytes":83,"sourceHtmlUrl":84},"scripts\u002Fsolution-mapping.json",116109,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fgithub-copilot-modernization\u002Fblob\u002Fmain\u002Fplugins\u002Fgithub-copilot-modernization\u002Fskills\u002Fassessment-report-converter\u002Fscripts\u002Fsolution-mapping.json",{"path":86,"resourceType":65,"resourceTypeDesc":66,"sizeBytes":87,"sourceHtmlUrl":88},"tests\u002Ftest_report_tools.py",23716,"https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fgithub-copilot-modernization\u002Fblob\u002Fmain\u002Fplugins\u002Fgithub-copilot-modernization\u002Fskills\u002Fassessment-report-converter\u002Ftests\u002Ftest_report_tools.py",[90,102,111,120,129,137],{"skillNo":91,"name":92,"nameZh":6,"description":93,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":94,"categories":95,"keywords":99,"publishedAt":35},"01M69B5DBRA354JMQRZP3D1X3X","verify-test-baseline","Generate and run post-migration tests from the frozen baseline specification.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fgithub-copilot-modernization\u002Fblob\u002Fmain\u002Fplugins\u002Fgithub-copilot-modernization\u002Fskills\u002Fverify-test-baseline\u002FSKILL.md",[96],{"categoryNo":97,"categoryName":98,"taxonomyType":21,"taxonomyTypeDesc":22},"01JBSEEDCAT000000000000008","测试与质量",[100,101],"test","verify test baseline",{"skillNo":103,"name":104,"nameZh":6,"description":105,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":106,"categories":107,"keywords":108,"publishedAt":35},"01M69B5BJWPCN41J5ZAR2XFMBR","team-request","How team members request infrastructure connection info and handle secrets in team mode","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fgithub-copilot-modernization\u002Fblob\u002Fmain\u002Fplugins\u002Fgithub-copilot-modernization\u002Fskills\u002Fteam-request\u002FSKILL.md",[],[109,110],"team request","infrastructure",{"skillNo":112,"name":113,"nameZh":6,"description":114,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":115,"categories":116,"keywords":117,"publishedAt":35},"01M69B58TZ7SNTPB6C508M5HWX","team-charters","Provides role charters (mission, ownership, core principles, quality bar) for a multi-agent coding team. Each charter defines the role's mission, ownership scope, core principle (boundary constraints), and quality bar. Most roles also include communication rules. Consumed by the coordinator during task decomposition to assign work to the correct role.\nTriggers: \"look up role charter\", \"what does the architect own\", \"check role boundaries\", \"find team roles\", \"which role handles X\", \"list agent charters\", \"role responsibilities\".\nNOT for: task decomposition (use breaking-down-tasks), implementation (use implementing-code), architecture analysis (use analyzing-architecture).","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fgithub-copilot-modernization\u002Fblob\u002Fmain\u002Fplugins\u002Fgithub-copilot-modernization\u002Fskills\u002Fteam-charters\u002FSKILL.md",[],[118,119],"team charters","architecture",{"skillNo":121,"name":122,"nameZh":6,"description":123,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":124,"categories":125,"keywords":126,"publishedAt":35},"01M69B56JVPKCS2WWVSCZXHEZJ","struts-to-spring","Migration guideline for transforming Apache Struts 2 applications to Spring Boot 3.x and Spring MVC.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fgithub-copilot-modernization\u002Fblob\u002Fmain\u002Fplugins\u002Fgithub-copilot-modernization\u002Fskills\u002Fstruts-to-spring\u002FSKILL.md",[],[127,128],"struts to spring","boot",{"skillNo":130,"name":131,"nameZh":6,"description":132,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":133,"categories":134,"keywords":135,"publishedAt":35},"01M69B54QQ6K8SDZ8MJYEKBYHJ","spring-boot-scaffolding","Reference guide for creating a new Spring Boot project during rewrite migration.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fgithub-copilot-modernization\u002Fblob\u002Fmain\u002Fplugins\u002Fgithub-copilot-modernization\u002Fskills\u002Fspring-boot-scaffolding\u002FSKILL.md",[],[128,136],"spring boot scaffolding",{"skillNo":138,"name":139,"nameZh":6,"description":140,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":12,"repositoryNo":13,"starsCount":14,"licenseSpdxId":15,"licenseFileUrl":6,"sourceHtmlUrl":141,"categories":142,"keywords":143,"publishedAt":35},"01M69B52T5G9CZWHYXWP22WEKY","sharing-learnings","How to record project learnings so that later-phase workers and future agent runs can consume them.","https:\u002F\u002Fgithub.com\u002Fmicrosoft\u002Fgithub-copilot-modernization\u002Fblob\u002Fmain\u002Fplugins\u002Fgithub-copilot-modernization\u002Fskills\u002Fsharing-learnings\u002FSKILL.md",[],[144],"sharing learnings"]