Implement the following plan:
Plan: github.com/git-pkgs/platforms Go module
Context
Every package ecosystem names the same platforms differently. An ARM64 Mac is darwin/arm64 to Go, darwin-arm64 to Node, aarch64-apple-darwin to Rust, arm64-darwin to RubyGems, and macosx_11_0_arm64 to Python. Any tool working across ecosystems maintains its own translation table. This module provides a shared one, modeled after how PURL solved the "which package" identity problem across ecosystems.
The approach is data-first: JSON mapping files define the relationships between ecosystems, and thin Go code reads them. The blog post at /Users/andrew/code/nesbitt.io/_posts/2026-02-17-platform-strings.md provides the research and comparison tables that seed the data.
Ecosystems
Seven ecosystem types: go, node, rust, rubygems, python, debian, llvm
Canonical naming
Internal representation uses the most "neutral" names (matching uname/kernel/chip vendor conventions):
- Arch:
x86_64 (not amd64/x64), aarch64 (not arm64), i686, arm, riscv64, s390x, ppc64, ppc64le, loong64, mips, mips64, mipsle, mips64le
- OS:
linux, darwin, windows, freebsd, netbsd, openbsd, android, ios, aix, solaris, dragonfly, illumos, plan9
- Vendor (optional):
apple, pc, unknown
- ABI (optional):
gnu, musl, msvc, mingw, eabi, eabihf
- OSVersion (optional): e.g.,
11.0 for macOS
- LibCVersion (optional): e.g.,
2.17 for manylinux
File structure
platforms/
go.mod
LICENSE
README.md
SPEC.md
platform.go # Platform struct, Ecosystem type, constants
data.go # go:embed + sync.Once loading + index building
parse.go # Parse(eco, string) -> Platform
format.go # Format(eco, Platform) -> string
translate.go # Translate, Normalize convenience functions
platform_test.go # Platform struct tests
parse_test.go # Table-driven parse tests
format_test.go # Table-driven format tests
translate_test.go # Cross-ecosystem round-trip tests
data_test.go # JSON consistency/integrity tests
data/
arches.json # Canonical arch -> per-ecosystem aliases
oses.json # Canonical OS -> per-ecosystem aliases
platforms.json # Full platform string mappings per ecosystem
Follows monorepo conventions from purl and vers modules: go:embed for data, sync.Once for loading, typed error structs, table-driven tests, no external dependencies.
Data files
data/arches.json
Maps each canonical arch to its name in each ecosystem. Values can be a string, an array (multiple aliases, first is preferred for formatting), or null (unsupported).
json
data/oses.json
Same structure for OS names.
json
data/platforms.json
Pre-computed full platform strings for common platforms. Handles cases that can't be composed mechanically (Python manylinux with libc versions, Rust triples with vendor/ABI, RubyGems context-dependent arch names). Array values mean multiple recognized aliases; first is preferred for formatting.
json
Core types
go
Public API
go
Translate is Parse then Format. Normalize is Parse then Format to the same ecosystem.
Parse strategy
- Check pre-computed
platforms.json index for exact match
- If not found, decompose using ecosystem-specific splitting rules:
- go: split
/ -> os/arch
- node: split
- -> os-arch
- rust: split
- -> arch-vendor-os[-abi]
- rubygems: split
- -> arch-os[-abi]
- python: regex for
manylinux_M_m_arch, musllinux_M_m_arch, macosx_M_m_arch, os_arch
- debian: split
- -> arch-os-abi
- llvm: split
- -> arch-vendor-os[-abi]
- Resolve components via arch/OS reverse lookup indices
- Return
ErrUnknownPlatform if neither works
Format strategy
- Check pre-computed index for exact Platform match
- If not found, compose from component mappings using ecosystem-specific rules
- Return
ErrNoMapping if ecosystem doesn't support the arch or OS
Edge cases
- RubyGems arm64 vs aarch64: Uses
arm64-darwin but aarch64-linux. Handled by platforms.json pre-computed entries.
- Python manylinux:
manylinux_2_17_x86_64 encodes glibc version into LibCVersion. Parsing extracts it; formatting re-embeds it.
- Go/Node lack ABI: Translating from Go to Rust defaults to
gnu ABI when unspecified.
- Rust vendor defaults: When translating from ecosystems without vendor:
apple for darwin, pc for windows, unknown otherwise.
Implementation order
go.mod, LICENSE
data/arches.json, data/oses.json, data/platforms.json (data first)
platform.go (types)
data.go (embed + load + indices)
data_test.go (verify data integrity)
parse.go + parse_test.go
format.go + format_test.go
translate.go + translate_test.go
SPEC.md
README.md
Verification
go vet ./... passes
go test ./... passes
- Tests cover every row from the blog post comparison table (8 ecosystems x 4 platforms)
- Round-trip tests: parse from ecosystem A, format to ecosystem B, parse back from B, compare
- Data consistency tests: no duplicate strings within an ecosystem, all arches/OSes referenced in platforms.json exist in arches/oses.json
If you need specific details from before exiting plan mode (like exact code snippets, error messages, or content you generated), read the full transcript at: /Users/andrew/.claude/projects/-Users-andrew-code-git-pkgs-platforms/018decf6-c2cc-4607-b19c-90479f05fd85.jsonl