# Source Command Megaminx Tpu Merge

> Pull the latest cayleypy-tpu-beam-smoke kernel output, aggregate per-rank JSONs, merge with current best, verify, and prep for submission.

- Skill: `erlemar/source-command-megaminx-tpu-merge` (Agent Skill)
- Install (CLI): `npx skillmds@latest add erlemar/source-command-megaminx-tpu-merge`
- Raw SKILL.md: https://api.skillmd.com/api/skills/erlemar/source-command-megaminx-tpu-merge/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: Erlemar (https://skillmd.com/u/erlemar)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/erlemar/source-command-megaminx-tpu-merge

---


# source-command-megaminx-tpu-merge

Use this skill when the user asks to run the migrated source command `megaminx-tpu-merge`.

## Command Template

# /megaminx-tpu-merge

Bundles the recurring TPU post-completion ritual we run after every kernel
finishes (or hits Kaggle's ~9h kill). Used 5+ times across v15 → v19a-fix.

## Usage

- `/megaminx-tpu-merge <out_name>` — pulls Kaggle output, aggregates ranks,
  merges with current best, writes `megaminx/submissions/<out_name>.csv`.
- `/megaminx-tpu-merge merge_tpu_v19b` — typical use after v19b kernel.
- `/megaminx-tpu-merge merge_tpu_v19b base=megaminx/submissions/...csv` —
  explicit base CSV.

## Defaults

- Kernel slug: `artgor/cayleypy-tpu-beam-smoke`.
- Working dir: `C:\Users\<user>\AppData\Local\Temp\<out_name>`.
- Base: latest `merge_*.csv` in `megaminx/submissions/` (smallest move count).
- Output: `megaminx/submissions/<out_name>.csv`.
- Standalone (TPU-only) output: `megaminx/submissions/<out_name>_standalone.csv`.

## Execution

### 1. Pull kernel output

```bash
export KAGGLE_API_TOKEN=$KAGGLE_API_TOKEN PYTHONUTF8=1 PYTHONIOENCODING=utf-8
mkdir -p /tmp/<out_name>
.venv/Scripts/kaggle.exe kernels output artgor/cayleypy-tpu-beam-smoke -p /tmp/<out_name> 2>&1 | tail -3
```

If `KernelWorkerStatus.RUNNING`: ABORT — kernel hasn't finished yet.

### 2. Aggregate per-rank JSONs + map to move names

NOTE: MINGW `/tmp/<dir>` maps to `C:\Users\<user>\AppData\Local\Temp\<dir>` —
Python on Windows needs the Windows-native path.

```bash
PYTHONUTF8=1 .venv/Scripts/python.exe -c "
import json, glob, csv, sys
sys.path.insert(0, 'megaminx/src'); sys.path.insert(0, 'src')
from megaminx.puzzle import Megaminx
puz = Megaminx.load('megaminx/data/puzzle_info.json')
MOVE_NAMES = list(puz.move_names)

base = r'C:\Users\<user>\AppData\Local\Temp\<out_name>'
records = []
for path in sorted(glob.glob(base + r'\rank_*_final.json')) + sorted(glob.glob(base + r'\rank_*_partial.json')):
    with open(path) as f:
        records.extend(json.load(f))

# Dedupe (final supersedes partial; same data anyway)
seen = set(); unique = []
for r in records:
    key = (r['pid'], r['rot_idx'])
    if key in seen: continue
    seen.add(key); unique.append(r)

# Group by pid, take min over verified rotations
by_pid = {}
for r in unique:
    if not r.get('verify_ok'): continue
    by_pid.setdefault(r['pid'], []).append(r)
print(f'records: {len(unique)}  pids covered: {len(by_pid)}')

best_per_pid = {pid: min(recs, key=lambda r: r['path_len'])['path_idx_orig']
                for pid, recs in by_pid.items()}

# Write standalone TPU CSV
with open('megaminx/submissions/<out_name>_standalone.csv', 'w', newline='') as f:
    w = csv.writer(f); w.writerow(['initial_state_id', 'path'])
    for pid in range(1001):
        if pid in best_per_pid:
            names = [MOVE_NAMES[m] for m in best_per_pid[pid]]
            w.writerow([pid, '.'.join(names)])
        else:
            w.writerow([pid, ''])
print(f'wrote standalone CSV')
"
```

Report: total records, pids covered, distribution of rotations-per-pid (helps
sanity check that K coverage is reasonable).

### 3. Merge with current best

```bash
PYTHONUTF8=1 .venv/Scripts/python.exe -c "
import csv
from collections import Counter

def load_paths(p):
    return {int(r['initial_state_id']): r['path'].split('.') if r['path'] else []
            for r in csv.DictReader(open(p))}

best = load_paths('<base>')
tpu = load_paths('megaminx/submissions/<out_name>_standalone.csv')

bucket_wins = Counter(); bucket_savings = Counter()
wins = 0; total_savings = 0
for pid in range(1001):
    bp = best.get(pid, [])
    tp = tpu.get(pid, [])
    if tp and len(tp) < len(bp):
        wins += 1; total_savings += len(bp) - len(tp)
        bucket_wins[pid // 100] += 1
        bucket_savings[pid // 100] += len(bp) - len(tp)

base_total = sum(len(v) for v in best.values())
print(f'current best: {base_total} moves')
print(f'TPU wins: {wins} pids, saved {total_savings} moves')
print()
print('per-bucket wins:')
for b in sorted(bucket_wins.keys()):
    print(f'  bucket {b}: {bucket_wins[b]:3} wins, {bucket_savings[b]:5} saved')

with open('megaminx/submissions/<out_name>.csv', 'w', newline='') as f:
    w = csv.writer(f); w.writerow(['initial_state_id', 'path'])
    for pid in range(1001):
        bp = best.get(pid, [])
        tp = tpu.get(pid, [])
        chosen = tp if (tp and len(tp) < len(bp)) else bp
        w.writerow([pid, '.'.join(chosen)])
print(f'wrote merge CSV (delta -{total_savings})')
"
```

### 4. Verify merged

```bash
PYTHONUTF8=1 .venv/Scripts/python.exe -c "
import sys
sys.path.insert(0, 'megaminx/src'); sys.path.insert(0, 'src')
from megaminx.puzzle import Megaminx
from cayley.verify import verify_submission
from pathlib import Path
puz = Megaminx.load(Path('megaminx/data/puzzle_info.json'))
r = verify_submission(puz, Path('megaminx/data/test.csv'),
                      Path('megaminx/submissions/<out_name>.csv'))
print(f'verify: {r.n_valid}/{r.n_total} valid, total {r.total_moves:,}')
sys.exit(0 if r.all_valid else 1)
"
```

Anything other than `1001/1001 valid`: STOP. Investigate before submitting.

### 5. Report and suggest next step

Print:
```
TPU merge ready: megaminx/submissions/<out_name>.csv = <new_total> (delta -<savings> vs <base_total>)
Suggested next: /megaminx-submit <out_name>.csv "<description>"
```

DO NOT submit automatically.

## Gotchas

- **Windows path conversion**: MINGW `/tmp/foo` ≠ Windows `C:\Users\...\Temp\foo`.
  Use Python on the Windows path explicitly. `cygpath -w /tmp/foo` works.
- **Empty path rows in standalone CSV** are expected for pids the kernel didn't
  reach. The merge with current best fills them in.
- **Partial pids (fewer than K rotations completed)** are still useful — take
  min over what's available. Often 3/4 rotations is enough.
- **`verify_ok=False` rotations** happen rarely; the conjugation-translated path
  failed CPU verification. Skip those when taking min; other rotations cover
  the pid.

## Cost reference

Aggregation + merge typically takes 30s-2min depending on JSON size:

| kernel size | aggregate | merge |
|---|---|---|
| 5 pids smoke | ~2s | ~1s |
| 500 pids partial | ~30s | ~5s |
| 1001 pids full | ~1min | ~10s |

