Add precision support audit tooling (#728)

* Add precision support audit tooling

Introduces two slash commands for auditing data-type support across ROCm
libraries between releases:

- /precision-check: scopes to libraries changed in the manifest diff
- /precision-check-delta: scopes to libraries whose source file SHA changed
  between releases (faster for routine release-over-release use)

precision_fetch.py fetches raw source files (RST, C headers) and YAML
snapshots from GitHub to /tmp/. The slash commands read them directly and
do semantic comparison — no regex parsers. Auto-updates the YAML for
unambiguous missing entries; flags ambiguous findings (macro expansion,
combination tables, support level mismatches, typedef-only sources) for
human review.

manifest_diff.py fetches and diffs ROCm manifests between two versions,
with branch-first and tag fallback for pre-GA use.

Also includes:
- .claude/settings.json: pre-approved permissions so the commands run
  without repeated prompts
- tools/autotag/precision-update-log/: gitignored directory for local
  audit logs
- README updates documenting the commands, prerequisites, and how to
  extend coverage to new libraries

* Fix markdown issues

* Move precision support scripts to tools/precision-support/

* Fix precision-check command path after script move to tools/precision-support

---------

Co-authored-by: Istvan Kiss <istvan.kiss@amd.com>
This commit is contained in:
pmoutsias-amd
2026-04-17 15:08:20 +02:00
committed by GitHub
co-authored by Istvan Kiss
parent 2e7ccf4637
commit f5c3167c0a
9 changed files with 1029 additions and 0 deletions
+112
View File
@@ -0,0 +1,112 @@
Precision support delta check — SHA-filtered. Arguments: $ARGUMENTS = "previous_version current_version" (e.g. 7.1.1 7.2.0).
Only checks libraries where the source file itself changed between the two releases (SHA comparison). Libraries with an unchanged source file are skipped automatically — no manifest diff is performed.
**Output rule:** Do not write any text until all tool calls are complete. When you are ready to respond, write exactly: "X of N libraries need verification." then the summary table, then detail blocks, then the "Changes written:" block. Nothing else. No narration, no transitions, no status updates, no reasoning.
## Step 1 — Fetch source files
```bash
cd ~/projects/ROCm-internal/tools/autotag && python3 precision_fetch.py -t "$GITHUB_TOKEN" --previous PREVIOUS --current CURRENT --sha-filter
```
Replace PREVIOUS and CURRENT from $ARGUMENTS.
## Step 2 — Read library list
Read `/tmp/precision_libs.txt`.
## Step 3 — Parse each library
For each library in the list:
- Source file exists (`/tmp/precision_{lib}_source.txt`): read it and `/tmp/precision_{lib}_yaml.txt`, then compare.
- URL file exists (`/tmp/precision_{lib}_url.txt`): read it — this is the GitHub link to the source file, used in the detail block Source field.
- Skip file exists (`/tmp/precision_{lib}_skip.txt`): note the reason.
**Do not use GitHub MCP tools, WebFetch, or any network call.** All content is already in `/tmp/`. Use only the Read tool on those files.
You are the parser. Read the source file directly — do not infer from filenames or prior knowledge.
- RST files: look for list-tables, type listings, and support level indicators.
- C headers: look for enums and macros that define supported types.
- AMD/NVIDIA split tables: use the AMD column only.
## Step 4 — Known mismatches (do not flag)
These entries are **specific** — they exempt only the named type for the named library. They do not mark the entire library as clean. Continue checking all other types in every library.
- **hipBLAS float16**: YAML=⚠️ correct — only AXPY and Dot support it.
- **hipBLAS bfloat16**: YAML=⚠️ correct — only Dot supports it.
- **rocBLAS float16**: same partial-support pattern as hipBLAS — do not flag.
- **rocBLAS bfloat16**: same partial-support pattern as hipBLAS — do not flag.
- **MIGraphX int8**: YAML=⚠️ correct — quantization-only via `quantize_int8()`.
- **rocFFT float16**: YAML already includes float16 ✅ — do not flag as missing.
**Do not dismiss any other gaps on your own judgment.** If a type appears in the source but not in the YAML, flag it — even if you think it might be intentional or covered by convention. That call belongs to the human reviewer, not you.
## Step 5 — Classify and update (silent)
For each finding, decide **auto-update** or **flag**:
**Default: auto-update.** If a type is absent from YAML and appears in the source, add it — unless one of the following exceptions applies.
**Flag for human review if ANY are true:**
- Found only via macro expansion (e.g., `MIGRAPHX_SHAPE_VISIT_TYPES`).
- Found only in a combination table (Ti/To/Tc triplets) — not a canonical per-type list.
- Finding is a support level mismatch (✅ vs ⚠️) — the type is already in YAML but at the wrong level.
- Source contains only typedef declarations with no support table or ✅ indicators of any kind.
- Type is explicitly marked AMD ❌ in an AMD/NVIDIA split table.
For auto-update findings, add the missing types to `~/projects/ROCm-internal/docs/data/reference/precision-support/precision-support.yaml`. Match the existing format exactly (type + support fields).
Then write a log to `~/projects/ROCm-internal/tools/autotag/precision-update-log/PREVIOUS-CURRENT-YYYYMMDD-HHMMSS.md` (replace PREVIOUS/CURRENT from $ARGUMENTS, timestamp from `date +%Y%m%d-%H%M%S`). Always create a new file — never read or overwrite an existing log. Log format:
```markdown
# Precision support audit: ROCm PREVIOUS → CURRENT
**Date:** YYYY-MM-DD
## Auto-updated
(table of types added, or "None")
## Flagged for human review
(table of flags)
## Clean / skipped
(table)
```
## Step 6 — Output
Write your response now. Start with: "X of N libraries need verification."
Then the summary table. **This MUST be a markdown table — no lists, no separators, no other format.** One row per library, all libraries included:
| Library | Finding | Action | Source |
|---------|---------|--------|--------|
| ... | ... | ... | filename.ext |
Use the URL from `/tmp/precision_{lib}_url.txt`. In the Source column, write only the filename (last path segment, plain text — no markdown link). For skipped libraries, leave Source blank.
Then, **for flagged libraries only**, a detail block:
<!-- markdownlint-disable MD036 -->
**{Library}**
<!-- markdownlint-enable MD036 -->
- Finding: ...
- Details: ...
- Source: [filename](url) — full clickable link using the URL from `/tmp/precision_{lib}_url.txt`.
- Recommended action: ...
Clean and skipped libraries appear only in the table — no detail block.
End with:
```text
Changes written:
- {library}: {types added}
(or "None" if no auto-updates)
```
+110
View File
@@ -0,0 +1,110 @@
Precision support delta check. Arguments: $ARGUMENTS = "previous_version current_version" (e.g. 7.1.1 7.2.0).
**Output rule:** Do not write any text until all tool calls are complete. When you are ready to respond, write exactly: "X of N libraries need verification." then the summary table, then detail blocks, then the "Changes written:" block. Nothing else. No narration, no transitions, no status updates, no reasoning.
## Step 1 — Fetch source files
```bash
cd ~/projects/ROCm-internal/tools/precision-support && python3 precision_fetch.py -t "$GITHUB_TOKEN" --previous PREVIOUS --current CURRENT
```
Replace PREVIOUS and CURRENT from $ARGUMENTS.
## Step 2 — Read library list
Read `/tmp/precision_libs.txt`.
## Step 3 — Parse each library
For each library in the list:
- Source file exists (`/tmp/precision_{lib}_source.txt`): read it and `/tmp/precision_{lib}_yaml.txt`, then compare.
- URL file exists (`/tmp/precision_{lib}_url.txt`): read it — this is the GitHub link to the source file, used in the detail block Source field.
- Skip file exists (`/tmp/precision_{lib}_skip.txt`): note the reason.
**Do not use GitHub MCP tools, WebFetch, or any network call.** All content is already in `/tmp/`. Use only the Read tool on those files.
You are the parser. Read the source file directly — do not infer from filenames or prior knowledge.
- RST files: look for list-tables, type listings, and support level indicators.
- C headers: look for enums and macros that define supported types.
- AMD/NVIDIA split tables: use the AMD column only.
## Step 4 — Known mismatches (do not flag)
These entries are **specific** — they exempt only the named type for the named library. They do not mark the entire library as clean. Continue checking all other types in every library.
- **hipBLAS float16**: YAML=⚠️ correct — only AXPY and Dot support it.
- **hipBLAS bfloat16**: YAML=⚠️ correct — only Dot supports it.
- **rocBLAS float16**: same partial-support pattern as hipBLAS — do not flag.
- **rocBLAS bfloat16**: same partial-support pattern as hipBLAS — do not flag.
- **MIGraphX int8**: YAML=⚠️ correct — quantization-only via `quantize_int8()`.
- **rocFFT float16**: YAML already includes float16 ✅ — do not flag as missing.
**Do not dismiss any other gaps on your own judgment.** If a type appears in the source but not in the YAML, flag it — even if you think it might be intentional or covered by convention. That call belongs to the human reviewer, not you.
## Step 5 — Classify and update (silent)
For each finding, decide **auto-update** or **flag**:
**Default: auto-update.** If a type is absent from YAML and appears in the source, add it — unless one of the following exceptions applies.
**Flag for human review if ANY are true:**
- Found only via macro expansion (e.g., `MIGRAPHX_SHAPE_VISIT_TYPES`).
- Found only in a combination table (Ti/To/Tc triplets) — not a canonical per-type list.
- Finding is a support level mismatch (✅ vs ⚠️) — the type is already in YAML but at the wrong level.
- Source contains only typedef declarations with no support table or ✅ indicators of any kind.
- Type is explicitly marked AMD ❌ in an AMD/NVIDIA split table.
For auto-update findings, add the missing types to `~/projects/ROCm-internal/docs/data/reference/precision-support/precision-support.yaml`. Match the existing format exactly (type + support fields).
Then write a log to `~/projects/ROCm-internal/tools/precision-support/precision-update-log/PREVIOUS-CURRENT-YYYYMMDD-HHMMSS.md` (replace PREVIOUS/CURRENT from $ARGUMENTS, timestamp from `date +%Y%m%d-%H%M%S`). Always create a new file — never read or overwrite an existing log. Log format:
```markdown
# Precision support audit: ROCm PREVIOUS → CURRENT
**Date:** YYYY-MM-DD
## Auto-updated
(table of types added, or "None")
## Flagged for human review
(table of flags)
## Clean / skipped
(table)
```
## Step 6 — Output
Write your response now. Start with: "X of N libraries need verification."
Then the summary table. **This MUST be a markdown table — no lists, no separators, no other format.** One row per library, all libraries included:
| Library | Finding | Action | Source |
|---------|---------|--------|--------|
| ... | ... | ... | filename.ext |
Use the URL from `/tmp/precision_{lib}_url.txt`. In the Source column, write only the filename (last path segment, plain text — no markdown link). For skipped libraries, leave Source blank.
Then, **for flagged libraries only**, a detail block:
<!-- markdownlint-disable MD036 -->
**{Library}**
<!-- markdownlint-enable MD036 -->
- Finding: ...
- Details: ...
- Source: [filename](url) — full clickable link using the URL from `/tmp/precision_{lib}_url.txt`.
- Recommended action: ...
Clean and skipped libraries appear only in the table — no detail block.
End with:
```text
Changes written:
- {library}: {types added}
(or "None" if no auto-updates)
```
+12
View File
@@ -0,0 +1,12 @@
{
"permissions": {
"allow": [
"Bash(cd ~/projects/ROCm-internal/tools/autotag && python3 precision_fetch.py*)",
"Read(/tmp/precision_*)",
"Edit(docs/data/reference/precision-support/precision-support.yaml)",
"Write(tools/autotag/precision-update-log/*)",
"Write(precision-update-log/*)",
"Bash(date +%Y%m%d-%H%M%S)"
]
}
}
+1
View File
@@ -19,3 +19,4 @@ __pycache__/
docs/contribute/index.md
docs/about/release-notes.md
docs/release/changelog.md
.claude/settings.local.json
@@ -42,6 +42,58 @@ python3 tag_script.py -t $GITHUB_ACCESS_TOKEN --no-release --no-pulls --compile_
* Copy over the first part of the changelog and replace the old release notes in RELEASE.md.
## Precision support audit (`/precision-check`, `/precision-check-delta`)
Audits data-type support across ROCm libraries between two releases. Uses a hybrid
approach: `precision_fetch.py` fetches raw source files and YAML snapshots from
GitHub to `/tmp/`, then Claude reads and compares them semantically via a slash
command — no regex parsers.
### Commands
| Command | When to use |
|---------|-------------|
| `/precision-check PREV CURR` | Full audit — checks all libraries that changed in the manifest |
| `/precision-check-delta PREV CURR` | SHA-filtered — only checks libraries whose source file itself changed |
Use `/precision-check-delta` for routine release-to-release audits. Use
`/precision-check` for a full sweep regardless of source file changes.
### Prerequisites
* [Claude Code](https://claude.ai/claude-code) installed and running in this repo.
* `$GITHUB_TOKEN` set to a GitHub Personal Access Token with read access to the ROCm org. Add it to your shell profile or pass it inline:
```sh
export GITHUB_TOKEN=your_token_here
```
### Running an audit
Open Claude Code in this repo and run:
```sh
/precision-check 7.1.1 7.2.0
# or
/precision-check-delta 7.1.1 7.2.0
```
Claude will:
1. Run `precision_fetch.py` to download source files and YAML snapshots to `/tmp/`.
2. Read and compare each library's source against `precision-support.yaml`.
3. Auto-update the YAML for clear missing entries.
4. Flag ambiguous findings (macro expansion, combination tables, support level mismatches) for human review.
5. Write a timestamped log to `tools/autotag/precision-update-log/` (gitignored — local only).
Commit any YAML changes. The YAML being audited lives at `docs/data/reference/precision-support/precision-support.yaml`.
### Adding new libraries to precision support
* Add an entry to `MANIFEST_TO_TAG` in `precision_fetch.py` mapping the manifest component name to the YAML tag.
* Add an entry to `SOURCE_CONFIG` in `precision_fetch.py` with the org, repo, and path to the source file. Add a `note` field if the library should be permanently skipped.
* Update `MONOREPO_LIBS` in `precision_fetch.py` if the library lives inside a monorepo (e.g. `rocm-libraries`).
## Adding new libraries/repositories
* Add the name or group of the repository (retrieved in default.xml in the ROCm project root) to: included_names or included_groups to auto_tag.py.
+261
View File
@@ -0,0 +1,261 @@
#!/usr/bin/env python3
"""Compare two ROCm default.xml manifests to detect component additions,
removals, and version changes between releases or RCs.
Usage:
python3 manifest_diff.py previous.xml current.xml
python3 manifest_diff.py 7.1.1 7.2.0 # fetch from GitHub by tag
python3 manifest_diff.py 7.2.0 7.2.1 # e.g. RC comparison
The script accepts either local file paths or ROCm version strings.
When given version strings, it fetches default.xml from the ROCm/ROCm
GitHub repo at the corresponding tag (rocm-X.Y.Z).
Output:
A report of added, removed, and version-changed components, plus
a summary count.
"""
import argparse
import re
import sys
import xml.etree.ElementTree as ET
from dataclasses import dataclass
from urllib.request import urlopen, Request
from urllib.error import URLError
_GITHUB_RAW = (
"https://raw.githubusercontent.com/ROCm/ROCm/release/rocm-rel-{branch}/default.xml"
)
# ---------------------------------------------------------------------------
# Manifest parsing
# ---------------------------------------------------------------------------
@dataclass
class Project:
name: str
revision: str # effective revision (project-level override or default)
path: str # repo checkout path (may differ from name)
groups: str # groups attribute if present
def _extract_version(revision: str) -> str:
"""Extract a human-readable version from a revision string.
refs/tags/rocm-7.2.0 → 7.2.0
refs/tags/v1.2.3 → v1.2.3
refs/heads/develop → develop (branch)
abc1234 → abc1234 (commit SHA)
"""
# Named ROCm release tag
m = re.search(r"refs/tags/rocm-(.+)", revision)
if m:
return m.group(1)
# Generic tag
m = re.search(r"refs/tags/(.+)", revision)
if m:
return m.group(1)
# Branch
m = re.search(r"refs/heads/(.+)", revision)
if m:
return f"[branch: {m.group(1)}]"
# Bare SHA or anything else
return revision
def parse_manifest(path_or_content: str, from_string: bool = False) -> dict[str, Project]:
"""Parse a manifest XML file (or string) and return {name: Project}."""
if from_string:
root = ET.fromstring(path_or_content)
else:
root = ET.parse(path_or_content).getroot()
default_el = root.find("default")
default_revision = default_el.get("revision", "") if default_el is not None else ""
projects: dict[str, Project] = {}
for el in root.iterfind("project"):
name = el.get("name", "")
if not name:
continue
revision = el.get("revision") or default_revision
path = el.get("path", name)
groups = el.get("groups", "")
projects[name] = Project(name=name, revision=revision, path=path, groups=groups)
return projects
# ---------------------------------------------------------------------------
# Fetching from GitHub
# ---------------------------------------------------------------------------
def _looks_like_version(s: str) -> bool:
return bool(re.match(r"^\d+\.\d+", s))
_GITHUB_RAW_TAG = (
"https://raw.githubusercontent.com/ROCm/ROCm/refs/tags/rocm-{version}/default.xml"
)
def fetch_manifest(source: str) -> dict[str, Project]:
"""Load a manifest from a local file path or a ROCm version string.
When given a version string, tries the release branch first (release/rocm-rel-X.Y),
then falls back to the release tag (rocm-X.Y.Z) for older releases.
"""
if _looks_like_version(source):
branch = ".".join(source.split(".")[:2])
branch_url = _GITHUB_RAW.format(branch=branch)
tag_url = _GITHUB_RAW_TAG.format(version=source)
for url in (branch_url, tag_url):
print(f" Fetching {url}", file=sys.stderr)
try:
req = Request(url, headers={"User-Agent": "rocm-manifest-diff/1.0"})
with urlopen(req, timeout=30) as resp:
return parse_manifest(resp.read().decode(), from_string=True)
except URLError as e:
if hasattr(e, "code") and e.code == 404:
continue
raise SystemExit(f"Cannot fetch manifest for ROCm {source}:\n {e}")
raise SystemExit(f"Cannot fetch manifest for ROCm {source}: not found at branch or tag.")
else:
return parse_manifest(source)
# ---------------------------------------------------------------------------
# Diff logic
# ---------------------------------------------------------------------------
@dataclass
class Change:
name: str
prev_version: str = ""
curr_version: str = ""
def diff_manifests(
prev: dict[str, Project],
curr: dict[str, Project],
) -> tuple[list[str], list[str], list[Change], list[str]]:
"""Return (added, removed, version_changed, unchanged) component lists."""
prev_names = set(prev)
curr_names = set(curr)
added = sorted(curr_names - prev_names)
removed = sorted(prev_names - curr_names)
version_changed: list[Change] = []
unchanged: list[str] = []
for name in sorted(prev_names & curr_names):
pv = _extract_version(prev[name].revision)
cv = _extract_version(curr[name].revision)
if pv != cv:
version_changed.append(Change(name, pv, cv))
else:
unchanged.append(name)
return added, removed, version_changed, unchanged
# ---------------------------------------------------------------------------
# Report
# ---------------------------------------------------------------------------
def print_report(
prev_label: str,
curr_label: str,
added: list[str],
removed: list[str],
version_changed: list[Change],
unchanged: list[str],
show_unchanged: bool = False,
) -> None:
print(f"\nManifest diff: {prev_label}{curr_label}\n")
if added:
print(f"ADDED ({len(added)}):")
for name in added:
print(f" + {name}")
print()
if removed:
print(f"REMOVED ({len(removed)}):")
for name in removed:
print(f" - {name}")
print(
" Note: removals may reflect migration into rocm-libraries or\n"
" rocm-systems rather than a component drop. Verify before\n"
" updating release notes."
)
print()
if version_changed:
col = max(len(c.name) for c in version_changed)
print(f"VERSION CHANGED ({len(version_changed)}):")
for c in version_changed:
print(f" {c.name:<{col}} {c.prev_version}{c.curr_version}")
print()
if show_unchanged and unchanged:
print(f"UNCHANGED ({len(unchanged)}):")
for name in unchanged:
print(f" {name}")
print()
total = len(added) + len(removed) + len(version_changed) + len(unchanged)
print(
f"Summary: {len(added)} added, {len(removed)} removed, "
f"{len(version_changed)} version changed, {len(unchanged)} unchanged "
f"({total} total)"
)
if not added and not removed and not version_changed:
print("\nNo component changes detected.")
# ---------------------------------------------------------------------------
# Entry point
# ---------------------------------------------------------------------------
def main() -> None:
parser = argparse.ArgumentParser(
description="Diff two ROCm manifest files to detect component changes."
)
parser.add_argument(
"previous",
help="Previous manifest — local file path or ROCm version (e.g. 7.1.1)",
)
parser.add_argument(
"current",
help="Current manifest — local file path or ROCm version (e.g. 7.2.0)",
)
parser.add_argument(
"--unchanged", action="store_true",
help="Also list components with no version change",
)
args = parser.parse_args()
prev = fetch_manifest(args.previous)
curr = fetch_manifest(args.current)
added, removed, version_changed, unchanged = diff_manifests(prev, curr)
print_report(
prev_label=args.previous,
curr_label=args.current,
added=added,
removed=removed,
version_changed=version_changed,
unchanged=unchanged,
show_unchanged=args.unchanged,
)
if __name__ == "__main__":
main()
@@ -0,0 +1 @@
*.md
+480
View File
@@ -0,0 +1,480 @@
#!/usr/bin/env python3
"""Fetch precision support source files and YAML for Claude to compare.
Writes raw content to /tmp/ for the Claude precision-check command.
Claude reads the files and does the comparison — no regex parsers used.
Usage (manifest-scoped — full audit):
python3 precision_fetch.py -t $GITHUB_TOKEN --previous 7.1.1 --current 7.2.0
Usage (SHA-filtered — only libraries whose source file changed):
python3 precision_fetch.py -t $GITHUB_TOKEN --previous 7.1.1 --current 7.2.0 --sha-filter
Usage (explicit library list):
python3 precision_fetch.py -t $GITHUB_TOKEN --version 7.2.0 --libs hipblas,hipsparselt
"""
import argparse
import base64
import json
import sys
from pathlib import Path
from urllib.error import HTTPError, URLError
from urllib.request import Request, urlopen
try:
import yaml
except ImportError:
sys.exit("pyyaml is required: pip install pyyaml")
from manifest_diff import fetch_manifest, diff_manifests
# ---------------------------------------------------------------------------
# Monorepo expansion
# ---------------------------------------------------------------------------
# Some manifest entries are monorepos covering multiple precision-tracked
# libraries. When one of these appears in the manifest diff, expand it to
# all tracked sub-libraries rather than treating it as a single component.
MONOREPO_LIBS: dict[str, list[str]] = {
"rocm-libraries": [
"hipBLAS", "hipBLASLt", "hipFFT", "hipRAND",
"hipSOLVER", "hipSPARSE", "hipSPARSELt", "hipTensor", "hipCUB",
"rocBLAS", "rocFFT", "rocRAND", "rocSOLVER", "rocSPARSE",
"rocWMMA", "rocPRIM", "rocThrust", "Tensile",
],
}
# ---------------------------------------------------------------------------
# Mapping: manifest component name → YAML library tag
# ---------------------------------------------------------------------------
MANIFEST_TO_TAG: dict[str, str] = {
"hipBLAS": "hipblas",
"hipBLASLt": "hipblaslt",
"hipFFT": "hipfft",
"hipRAND": "hiprand",
"hipSOLVER": "hipsolver",
"hipSPARSE": "hipsparse",
"hipSPARSELt": "hipsparselt",
"hipTensor": "hiptensor",
"hipCUB": "hipcub",
"rocBLAS": "rocblas",
"rocFFT": "rocfft",
"rocRAND": "rocrand",
"rocSOLVER": "rocsolver",
"rocSPARSE": "rocsparse",
"rocWMMA": "rocwmma",
"rocPRIM": "rocprim",
"rocThrust": "rocthrust",
"composable_kernel": "composable-kernel",
"MIOpen": "miopen",
"AMDMIGraphX": "migraphx",
"rccl": "rccl",
"Tensile": "tensile",
}
# ---------------------------------------------------------------------------
# Source config: YAML tag → GitHub fetch details
# ---------------------------------------------------------------------------
# Each entry defines where to fetch the authoritative source file for a library.
# org: GitHub org (default "ROCm")
# repo: GitHub repo name
# path: Path to the source file within the repo
# note: Set if the library is skipped or has no parseable source
SOURCE_CONFIG: dict[str, dict] = {
"hipblas": {
"org": "ROCm",
"repo": "rocm-libraries",
"path": "projects/hipblas/docs/reference/data-type-support.rst",
},
"hipblaslt": {
"org": "ROCm",
"repo": "rocm-libraries",
"path": "projects/hipblaslt/docs/reference/data-type-support.rst",
},
"hipfft": {
"org": "ROCm",
"repo": "rocm-libraries",
"path": "projects/hipfft/docs/reference/hipfft-api-usage.rst",
},
"hiprand": {
"org": "ROCm",
"repo": "rocm-libraries",
"path": "projects/hiprand/docs/api-reference/data-type-support.rst",
},
"hipsolver": {
"org": "ROCm",
"repo": "rocm-libraries",
"path": "projects/hipsolver/docs/reference/precision.rst",
},
"hipsparse": {
"org": "ROCm",
"repo": "rocm-libraries",
"path": "projects/hipsparse/docs/reference/precision.rst",
},
"hipsparselt": {
"org": "ROCm",
"repo": "rocm-libraries",
"path": "projects/hipsparselt/docs/reference/data-type-support.rst",
},
"hiptensor": {
"org": "ROCm",
"repo": "rocm-libraries",
"path": "projects/hiptensor/docs/api-reference/api-reference.rst",
},
"hipcub": {
"org": "ROCm",
"repo": "rocm-libraries",
"path": "projects/hipcub/docs/api-reference/data-type-support.rst",
"note": "No data-type-support page exists — hipCUB is header-only and type-agnostic",
},
"rocblas": {
"org": "ROCm",
"repo": "rocm-libraries",
"path": "projects/rocblas/docs/reference/data-type-support.rst",
},
"rocfft": {
"org": "ROCm",
"repo": "rocm-libraries",
"path": "projects/rocfft/library/include/rocfft/rocfft.h",
},
"rocrand": {
"org": "ROCm",
"repo": "rocm-libraries",
"path": "projects/rocrand/docs/api-reference/data-type-support.rst",
},
"rocsolver": {
"org": "ROCm",
"repo": "rocm-libraries",
"path": "projects/rocsolver/docs/reference/precision.rst",
},
"rocsparse": {
"org": "ROCm",
"repo": "rocm-libraries",
"path": "projects/rocsparse/docs/reference/precision.rst",
},
"rocwmma": {
"org": "ROCm",
"repo": "rocm-libraries",
"path": "projects/rocwmma/docs/api-reference/api-reference-guide.rst",
},
"rocprim": {
"org": "ROCm",
"repo": "rocm-libraries",
"path": "projects/rocprim/docs/reference/data-type-support.rst",
},
"rocthrust": {
"org": "ROCm",
"repo": "rocm-libraries",
"path": "projects/rocthrust/docs/data-type-support.rst",
"note": "No data-type-support page exists — rocThrust is a template library and type-agnostic",
},
"composable-kernel": {
"org": "ROCm",
"repo": "composable_kernel",
"path": "docs/reference/Composable_Kernel_supported_scalar_types.rst",
},
"miopen": {
"org": "ROCm",
"repo": "MIOpen",
"path": "docs/reference/datatypes.rst",
},
"migraphx": {
"org": "ROCm",
"repo": "AMDMIGraphX",
"path": "src/api/include/migraphx/migraphx.h",
},
"rccl": {
"org": "ROCm",
"repo": "rccl",
"path": "src/nccl.h.in",
},
"tensile": {
"org": "ROCm",
"repo": "rocm-libraries",
"path": "projects/hipblaslt/tensilelite/Tensile/docs/src/reference/precision-support.rst",
"note": "Tensile has no docs folder — it is an internal kernel generator, not a user-facing library",
},
}
# ---------------------------------------------------------------------------
# GitHub helpers
# ---------------------------------------------------------------------------
_GH_API = "https://api.github.com"
def _gh_request(token: str, url: str) -> dict:
req = Request(url, headers={
"Authorization": f"Bearer {token}",
"Accept": "application/vnd.github+json",
"X-GitHub-Api-Version": "2022-11-28",
})
with urlopen(req, timeout=30) as resp:
return json.loads(resp.read())
def _version_to_ref(version: str) -> str:
"""Convert a ROCm version string to a release branch ref.
e.g. '7.2.0''release/rocm-rel-7.2'
"""
major_minor = ".".join(version.split(".")[:2])
return f"release/rocm-rel-{major_minor}"
def fetch_yaml(token: str, version: str) -> dict:
"""Fetch precision-support.yaml from the ROCm/ROCm repo.
Tries the release branch first, falls back to the release tag.
"""
path = "docs/data/reference/precision-support/precision-support.yaml"
for ref in _refs_for_version(version):
url = f"{_GH_API}/repos/ROCm/ROCm/contents/{path}?ref={ref}"
try:
data = _gh_request(token, url)
return yaml.safe_load(base64.b64decode(data["content"]).decode())
except HTTPError as e:
if e.code in (401, 403):
sys.exit(f"GitHub authentication failed (HTTP {e.code}) — check your token.")
continue
except URLError as e:
sys.exit(f"Network error: {e}")
sys.exit(f"Cannot fetch precision-support.yaml for ROCm {version}: not found at branch or tag.")
def _refs_for_version(version: str) -> list[str]:
"""Return refs to try in order: release branch first, tag as fallback."""
return [_version_to_ref(version), f"rocm-{version}"]
def fetch_file_sha(token: str, org: str, repo_name: str, path: str, version: str) -> str | None:
"""Return the blob SHA of a file at the given ROCm version, or None if not found.
Tries the release branch first, falls back to the release tag.
"""
for ref in _refs_for_version(version):
url = f"{_GH_API}/repos/{org}/{repo_name}/contents/{path}?ref={ref}"
try:
data = _gh_request(token, url)
return data.get("sha")
except HTTPError as e:
if e.code in (401, 403):
sys.exit(f"GitHub authentication failed (HTTP {e.code}) — check your token.")
continue
except URLError as e:
sys.exit(f"Network error fetching SHA for {path}: {e}")
return None
def fetch_source_file(token: str, org: str, repo_name: str, path: str, version: str) -> str | None:
"""Fetch a source file from GitHub at the given ROCm version.
Tries the release branch first, falls back to the release tag.
Returns the file content as a string, or None if not found at either ref.
"""
for ref in _refs_for_version(version):
url = f"{_GH_API}/repos/{org}/{repo_name}/contents/{path}?ref={ref}"
try:
data = _gh_request(token, url)
return base64.b64decode(data["content"]).decode()
except HTTPError as e:
if e.code in (401, 403):
sys.exit(f"GitHub authentication failed (HTTP {e.code}) — check your token.")
continue
except URLError as e:
sys.exit(f"Network error fetching {path}: {e}")
return None
def load_yaml_types(yaml_data: dict) -> dict[str, dict[str, str]]:
"""Return {tag: {type: support}} from precision-support.yaml."""
result: dict[str, dict[str, str]] = {}
for group in yaml_data.get("library_groups", []):
for lib in group.get("libraries", []):
tag = lib["tag"]
result[tag] = {dt["type"]: dt["support"] for dt in lib.get("data_types", [])}
return result
# ---------------------------------------------------------------------------
# Scoping
# ---------------------------------------------------------------------------
def sha_filter_scope(token: str, previous: str, current: str) -> list[str]:
"""Return sorted list of library tags where the source file changed between two ROCm versions.
Checks all tracked libraries — no manifest diff. Skips libraries where the
source file SHA is identical at both tags, writing a skip file so Claude knows why.
"""
print(f"SHA-filtering all tracked libraries {previous}{current}...", file=sys.stderr)
changed = []
for lib, config in SOURCE_CONFIG.items():
if config.get("note"):
continue # permanently skipped libraries (type-agnostic, internal, etc.)
org, repo, path = config["org"], config["repo"], config["path"]
sha_prev = fetch_file_sha(token, org, repo, path, previous)
sha_curr = fetch_file_sha(token, org, repo, path, current)
if sha_prev is None and sha_curr is None:
Path(f"/tmp/precision_{lib}_skip.txt").write_text(
f"Source file not found at either {previous} or {current}",
encoding="utf-8",
)
print(f" {lib}: not found at either version — skip", file=sys.stderr)
elif sha_prev == sha_curr:
Path(f"/tmp/precision_{lib}_skip.txt").write_text(
f"Source file unchanged between {previous} and {current} (SHA match)",
encoding="utf-8",
)
print(f" {lib}: unchanged — skip", file=sys.stderr)
else:
changed.append(lib)
print(f" {lib}: changed — include", file=sys.stderr)
print(f" {len(changed)} libraries with changed source files", file=sys.stderr)
return sorted(changed)
def scope_from_manifest(token: str, previous: str, current: str) -> list[str]:
"""Return sorted list of library tags that changed between two ROCm versions."""
print(f"Diffing manifests {previous}{current}...", file=sys.stderr)
prev_manifest = fetch_manifest(previous)
curr_manifest = fetch_manifest(current)
added, removed, version_changed, _ = diff_manifests(prev_manifest, curr_manifest)
changed_components = (
set(added)
| set(removed)
| {c.name for c in version_changed}
)
# Expand monorepo entries
for monorepo, sub_libs in MONOREPO_LIBS.items():
if monorepo in changed_components:
changed_components |= set(sub_libs)
tags = sorted(
MANIFEST_TO_TAG[name]
for name in changed_components
if name in MANIFEST_TO_TAG
)
print(f" {len(changed_components)} components changed → {len(tags)} tracked libraries", file=sys.stderr)
return tags
# ---------------------------------------------------------------------------
# Fetching
# ---------------------------------------------------------------------------
def fetch_library(token: str, lib: str, version: str) -> None:
"""Fetch source file and YAML entry for one library, write to /tmp/."""
config = SOURCE_CONFIG.get(lib)
if not config:
print(f" {lib}: no SOURCE_CONFIG entry — skipping", file=sys.stderr)
return
note = config.get("note")
if note:
Path(f"/tmp/precision_{lib}_skip.txt").write_text(note, encoding="utf-8")
print(f" {lib}: skipped — {note}", file=sys.stderr)
return
content = fetch_source_file(
token,
config["org"],
config["repo"],
config["path"],
version,
)
if content is None:
Path(f"/tmp/precision_{lib}_skip.txt").write_text(
f"Source file not found: {config['path']}", encoding="utf-8"
)
print(f" {lib}: source file not found", file=sys.stderr)
return
Path(f"/tmp/precision_{lib}_source.txt").write_text(content, encoding="utf-8")
ref = _version_to_ref(version)
gh_url = f"https://github.com/{config['org']}/{config['repo']}/blob/{ref}/{config['path']}"
Path(f"/tmp/precision_{lib}_url.txt").write_text(gh_url, encoding="utf-8")
print(f" {lib}: source written ({len(content):,} chars)", file=sys.stderr)
# ---------------------------------------------------------------------------
# Entry point
# ---------------------------------------------------------------------------
def main() -> None:
parser = argparse.ArgumentParser(
description="Fetch precision support source files for Claude to compare."
)
parser.add_argument("-t", "--token", required=True, help="GitHub personal access token")
mode = parser.add_mutually_exclusive_group(required=True)
mode.add_argument("--previous", help="Previous ROCm version (use with --current)")
mode.add_argument("--version", help="ROCm version for explicit --libs mode")
parser.add_argument("--current", help="Current ROCm version (use with --previous)")
parser.add_argument("--libs", help="Comma-separated library tags (use with --version)")
parser.add_argument(
"--sha-filter",
action="store_true",
help="Check all tracked libraries; skip those whose source file SHA is unchanged. "
"Use with --previous/--current. No manifest diff is performed.",
)
args = parser.parse_args()
# Resolve version and library list
if args.previous:
if not args.current:
sys.exit("--current is required when using --previous")
current_version = args.current
if args.sha_filter:
libs = sha_filter_scope(args.token, args.previous, args.current)
else:
libs = scope_from_manifest(args.token, args.previous, args.current)
else:
if args.sha_filter:
sys.exit("--sha-filter requires --previous and --current")
if not args.libs:
sys.exit("--libs is required when using --version")
current_version = args.version
libs = [lib.strip() for lib in args.libs.split(",")]
# Fetch YAML once
print(f"\nFetching precision-support.yaml for ROCm {current_version}...", file=sys.stderr)
yaml_data = fetch_yaml(args.token, current_version)
yaml_types = load_yaml_types(yaml_data)
# Write YAML entries for all tracked libraries (not just changed ones —
# Claude needs the full context for comparison)
for lib, types in yaml_types.items():
Path(f"/tmp/precision_{lib}_yaml.txt").write_text(
yaml.dump({lib: types}, allow_unicode=True, default_flow_style=False),
encoding="utf-8",
)
# Write manifest-scoped library list so Claude knows what to check
libs_file = Path("/tmp/precision_libs.txt")
libs_file.write_text("\n".join(libs), encoding="utf-8")
print(f"\nLibraries to check: {', '.join(libs)}", file=sys.stderr)
# Fetch source files
print("\nFetching source files...", file=sys.stderr)
for lib in libs:
fetch_library(args.token, lib, current_version)
print(f"\nDone. Files written to /tmp/precision_*", file=sys.stderr)
print(f"Library list: /tmp/precision_libs.txt", file=sys.stderr)
if __name__ == "__main__":
main()