{"owner":"inclusionAI","repo":"AReaL","hasSkills":true,"totalSkillsCount":56,"totalTokensCount":120702,"categories":["anthropic-skill","subagent-persona","command-prompt","root-instruction","claude-rule"],"hasMcp":false,"mcpConfig":null,"found":[".agents/skills/add-archon-model/SKILL.md",".agents/skills/add-dataset/SKILL.md",".agents/skills/add-reward/SKILL.md",".agents/skills/add-unit-tests/SKILL.md",".agents/skills/add-workflow/SKILL.md",".agents/skills/commit-conventions/SKILL.md",".agents/skills/create-pr/SKILL.md",".agents/skills/debug-distributed/SKILL.md",".agents/skills/review-pr/MIGRATION.md",".agents/skills/review-pr/SKILL.md",".agents/skills/review-pr/references/review-pr-domains-and-signals.md",".agents/skills/review-pr/references/review-pr-templates.md",".agents/skills/translate-doc-zh/SKILL.md",".agents/skills/upgrade-deps/CHECKLIST_MAINTENANCE.md",".agents/skills/upgrade-deps/SKILL.md",".agents/skills/upgrade-deps/checklists/_TEMPLATE.md",".agents/skills/upgrade-deps/checklists/mbridge.md",".agents/skills/upgrade-deps/checklists/megatron-bridge.md",".agents/skills/upgrade-deps/checklists/megatron-core.md",".agents/skills/upgrade-deps/checklists/peft.md",".agents/skills/upgrade-deps/checklists/sglang.md",".agents/skills/upgrade-deps/checklists/torchao.md",".agents/skills/upgrade-deps/checklists/transformers.md",".agents/skills/upgrade-deps/checklists/vllm.md",".claude/agents/algorithm-expert.md",".claude/agents/archon-engine-expert.md",".claude/agents/code-verifier.md",".claude/agents/fsdp-engine-expert.md",".claude/agents/launcher-scheduler-expert.md",".claude/agents/megatron-engine-expert.md",".claude/agents/planner.md",".claude/agents/simple-code-reviewer.md",".claude/commands/create-pr.md",".claude/commands/gen-commit-msg.md",".claude/commands/review-pr.md",".claude/commands/translate-doc-zh.md",".claude/rules/api-config.md",".claude/rules/code-style.md",".claude/rules/distributed.md",".claude/rules/testing.md",".claude/skills/add-archon-model/SKILL.md",".claude/skills/add-dataset/SKILL.md",".claude/skills/add-reward/SKILL.md",".claude/skills/add-unit-tests/SKILL.md",".claude/skills/add-workflow/SKILL.md",".claude/skills/commit-conventions/SKILL.md",".claude/skills/debug-distributed/SKILL.md",".opencode/skills/add-archon-model/SKILL.md",".opencode/skills/add-dataset/SKILL.md",".opencode/skills/add-reward/SKILL.md",".opencode/skills/add-unit-tests/SKILL.md",".opencode/skills/add-workflow/SKILL.md",".opencode/skills/commit-conventions/SKILL.md",".opencode/skills/debug-distributed/SKILL.md","AGENTS.md","CLAUDE.md"],"skills":{".agents/skills/add-archon-model/SKILL.md":"---\nname: add-archon-model\ndescription: Guide for adding a new model to the Archon engine. Use when user wants to add support for a new HuggingFace model architecture in ArchonEngine.\n---\n\n# Add Archon Model\n\nAdd support for a new HuggingFace model architecture in the Archon training engine.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a model to Archon?\"\n- User wants to support a new model family (e.g., Llama, Mistral, DeepSeek) in\n  ArchonEngine\n- User mentions adding a new `ModelSpec` or model type for Archon\n\n## Prerequisites\n\nBefore starting, ensure:\n\n- The target model is available on HuggingFace (has `config.json` with `model_type`)\n- You know the HuggingFace model ID (e.g., `meta-llama/Llama-3-8B`)\n- The model uses a standard transformer architecture (decoder-only)\n\n## Step-by-Step Guide\n\n### Step 1: Analyze the Target Model Architecture\n\nRead the HuggingFace model's source code to extract key architecture information.\n\n**Action**: Fetch and analyze the model's HuggingFace configuration and modeling files.\n\n1. Read the model's `config.json` (via `AutoConfig.from_pretrained`) to identify:\n\n   - `model_type` string (this is the key used for registry lookup)\n   - All architecture hyperparameters (hidden_size, num_layers, etc.)\n   - Any model-specific fields (e.g., `qk_norm`, `attention_bias`, MoE fields)\n\n1. Read the HuggingFace `modeling_*.py` source to identify:\n\n   - **Attention variant**: Does it have Q/K norm? Attention bias? Sliding window?\n     Multi-latent attention?\n   - **FFN variant**: SwiGLU (gate_proj + up_proj + down_proj)? GeGLU? Standard MLP?\n   - **MoE support**: Does it have MoE layers? What router type? Shared experts?\n   - **RoPE variant**: Standard RoPE? YaRN? NTK-aware scaling? What is the inv_freq\n     formula?\n   - **Normalization**: RMSNorm or LayerNorm? Pre-norm or post-norm? Elementwise affine?\n   - **Weight tying**: Does `tie_word_embeddings` appear in config?\n   - **State dict key names**: What are the HF weight key naming conventions?\n\n1. Summarize findings in a checklist like:\n\n```\nTarget model: <name>\nHF model_type: \"<model_type>\" (and variants like \"<model_type>_moe\" if applicable)\nAttention: [standard GQA / with QK norm / with bias / sliding window / ...]\nFFN: [SwiGLU / GeGLU / standard MLP / ...]\nMoE: [no / yes - num_experts, top_k, shared_experts]\nRoPE: [standard / YaRN / NTK-aware / ...]\nNorm: [RMSNorm / LayerNorm] with [pre-norm / post-norm]\nWeight tying: [yes / no]\n```\n\n### Step 2: Select the Reference Model\n\nChoose the closest existing implementation as a starting point:\n\n| Target characteristics               | Reference | Why                                     |\n| ------------------------------------ | --------- | --------------------------------------- |\n| Dense-only, standard GQA, no QK norm | `qwen2`   | Simplest baseline, pure dense           |\n| Has QK norm, or has MoE support      | `qwen3`   | Supports QK norm + MoE + shared experts |\n\n**Action**: Copy the reference model directory as the starting point:\n\n```\nareal/experimental/models/archon/<model>/\n  __init__.py\n  spec.py\n  model/\n    args.py\n    model.py\n    rope.py\n    state_dict_adapter.py\n  infra/\n    parallelize.py\n```\n\n### Step 3: Implement `args.py`\n\nAdapt `<Model>ModelArgs` to match the target model's HuggingFace config fields.\n\n**Key changes from reference**:\n\n1. Update the `@dataclass` fields to match the target model's hyperparameters:\n\n   - Field names should use Archon conventions (`dim`, `n_layers`, `n_heads`,\n     `n_kv_heads`, `vocab_size`, `head_dim`, `hidden_dim`, `norm_eps`, `rope_theta`,\n     etc.)\n   - Default values should match the smallest variant of the target model\n   - Add model-specific fields (e.g., `attention_bias`, `qk_norm`, `sliding_window`)\n\n1. Update `from_hf_config()` to correctly map HuggingFace config attributes:\n\n   - Use `getattr(hf_config, \"field_name\", default)` for optional fields\n   - Handle variant-specific fields (e.g., MoE fields only present in MoE variants)\n   - The method must return an instance of the model args class\n\n**Critical**: Verify every field mapping against the HF model's `config.json`. Incorrect\nmappings here cause silent errors downstream.\n\n**Base class contract** (`BaseModelArgs`):\n\n```python\n@dataclass\nclass <Model>ModelArgs(BaseModelArgs):\n    # ... model-specific fields ...\n\n    @classmethod\n    def from_hf_config(\n        cls,\n        hf_config: PretrainedConfig,\n        is_critic: bool = False,\n        **kwargs,\n    ) -> <Model>ModelArgs:\n        # Map HF config fields to Archon model args\n        ...\n```\n\n### Step 4: Implement `model.py`\n\nAdapt the model architecture to match the target model.\n\n**Key components to adapt**:\n\n1. **Normalization** (`RMSNorm` or similar):\n\n   - Check if `elementwise_affine` is configurable\n   - Check the epsilon default value\n   - If the model uses `LayerNorm`, implement accordingly\n\n1. **Attention** module:\n\n   - Q/K/V projection: Check bias presence (`nn.Linear(..., bias=True/False)`)\n   - QK norm: Add `q_norm`/`k_norm` if the model has them, remove if it doesn't\n   - GQA: `n_kv_heads` \\< `n_heads` for grouped-query attention\n   - Ulysses SP: Keep the `set_cp_group` / `_sp_enabled` pattern from the reference\n   - Output projection: Check bias presence\n\n1. **FeedForward** module:\n\n   - SwiGLU: `w2(silu(w1(x)) * w3(x))` -- most common for modern LLMs\n   - Check bias in linear layers\n   - For MoE models: `MoE` module replaces `FeedForward` on designated layers\n\n1. **TransformerBlock**: Pre-norm (most modern LLMs) vs post-norm\n\n   - MoE layer detection via `_is_moe_layer()` if applicable\n\n1. **Top-level Model** (`<Model>Model(BaseArchonModel)`):\n\n   - `tok_embeddings`, `layers` (as `ModuleDict`), `norm`, `output`/`score`\n   - `init_weights()`: Match initialization scheme from HF\n   - `init_buffers()`: RoPE cache + MoE buffers\n   - `forward()`: Must follow `BaseArchonModel` signature:\n     `(tokens, positions, cu_seqlens, max_seqlen, tree_attn_meta=None) -> Tensor`\n\n**Base class contract** (`BaseArchonModel`):\n\n```python\nclass <Model>Model(BaseArchonModel):\n    def forward(self, tokens, positions, cu_seqlens, max_seqlen, tree_attn_meta=None) -> torch.Tensor: ...\n    def init_weights(self) -> None: ...\n    def init_buffers(self, buffer_device) -> None: ...\n```\n\n### Step 5: Implement `rope.py`\n\nHandle the rotary position embedding variant.\n\n**Options**:\n\n1. **Standard RoPE** (same as qwen2/qwen3): Re-export from qwen2:\n\n   ```python\n   from areal.experimental.models.archon.qwen2.model.rope import (\n       apply_rotary_emb,\n       precompute_rope_cache,\n       repeat_kv,\n       reshape_for_broadcast,\n       rotate_half,\n   )\n   ```\n\n1. **Custom RoPE** (YaRN, NTK-aware, etc.): Implement custom `precompute_rope_cache()`\n   and `apply_rotary_emb()` functions. The key difference is usually in how `inv_freq`\n   is computed (scaling factors, interpolation, etc.).\n\n### Step 6: Implement `state_dict_adapter.py`\n\nMap between HuggingFace and Archon weight key names.\n\n**This is the most error-prone step.** The adapter must correctly handle:\n\n1. **Key name mapping** (`from_hf_map` dict):\n\n   - Embedding: `model.embed_tokens.weight` -> `tok_embeddings.weight`\n   - Attention: `model.layers.{}.self_attn.q_proj.weight` ->\n     `layers.{}.attention.wq.weight`\n   - FFN: `model.layers.{}.mlp.gate_proj.weight` -> `layers.{}.feed_forward.w1.weight`\n   - Norms: `model.layers.{}.input_layernorm.weight` ->\n     `layers.{}.attention_norm.weight`\n   - Output: `lm_head.weight` -> `output.weight`\n   - Skip keys (set to `None`): `rotary_emb.inv_freq` (computed at runtime)\n   - Model-specific keys: bias terms, QK norm weights, etc.\n\n1. **Reverse mapping** (`to_hf_map`): Auto-generated from `from_hf_map`\n\n1. **MoE expert weights** (if applicable): 3D\\<->2D conversion for expert weights. Copy\n   the MoE handling from qwen3 if the model has MoE.\n\n1. **Weight tying**: Skip `output.weight` during `to_hf()` if `tie_word_embeddings=True`\n\n**Verification approach**: After implementation, the adapter should satisfy:\n\n```python\n# Roundtrip: archon -> hf -> archon preserves all keys\nhf_sd = adapter.to_hf(archon_sd)\nroundtrip_sd = adapter.from_hf(hf_sd)\nassert set(roundtrip_sd.keys()) == set(archon_sd.keys())\n```\n\n**Base class contract** (`BaseStateDictAdapter`):\n\n```python\nclass <Model>StateDictAdapter(BaseStateDictAdapter):\n    def from_hf(self, hf_state_dict) -> dict[str, Any]: ...\n    def to_hf(self, archon_state_dict) -> dict[str, Any]: ...\n    def convert_single_to_hf(self, name, tensor) -> list[tuple[str, torch.Tensor]]: ...\n```\n\n### Step 7: Implement `parallelize.py`\n\nDefine the parallelization strategy for the model.\n\n**The parallelize function** applies parallelism in this order:\n\n1. TP (Tensor Parallelism) -- shard attention/FFN across devices\n1. EP (Expert Parallelism) -- for MoE models only\n1. CP (Context Parallelism / Ulysses SP) -- sequence parallelism\n1. AC (Activation Checkpointing) -- memory optimization\n1. torch.compile -- compilation optimization\n1. FSDP (Fully Sharded Data Parallelism) -- data parallelism\n\n**Key adaptations by model architecture**:\n\n- **Attention with QK norm**: wq/wk use `use_local_output=False` (DTensor output for\n  norm), add `SequenceParallel(sequence_dim=2)` for q_norm/k_norm\n- **Attention without QK norm**: wq/wk/wv all use `use_local_output=True`\n- **Attention with bias**: Bias terms follow the same parallel plan as their weights\n- **MoE layers**: Separate TP plan for MoE input/output, router gate, and expert\n  weights. Copy from qwen3's `apply_moe_ep_tp()` and `apply_non_moe_tp()`\n- **Dense-only models**: Simpler plan without MoE handling. Copy from qwen2\n\n**Function signature** (must match `ParallelizeFn` protocol):\n\n```python\ndef parallelize_<model>(\n    model: nn.Module,\n    parallel_dims: ArchonParallelDims,\n    param_dtype: torch.dtype = torch.bfloat16,\n    reduce_dtype: torch.dtype = torch.float32,\n    loss_parallel: bool = True,\n    cpu_offload: bool = False,\n    reshard_after_forward_policy: str = \"default\",\n    ac_config: ActivationCheckpointConfig | None = None,\n    enable_compile: bool = True,\n) -> nn.Module:\n```\n\n### Step 8: Create `spec.py` and Register\n\nAssemble the `ModelSpec` and register it.\n\n```python\nfrom areal.experimental.models.archon.model_spec import ModelSpec, register_model_spec\nfrom areal.experimental.models.archon.pipeline_parallel import pipeline_llm\nfrom areal.experimental.models.archon.<model>.infra.parallelize import parallelize_<model>\nfrom areal.experimental.models.archon.<model>.model.args import <Model>ModelArgs\nfrom areal.experimental.models.archon.<model>.model.model import <Model>Model\nfrom areal.experimental.models.archon.<model>.model.state_dict_adapter import (\n    <Model>StateDictAdapter,\n)\n\n<MODEL>_SPEC = ModelSpec(\n    name=\"<Model>\",\n    model_class=<Model>Model,\n    model_args_class=<Model>ModelArgs,\n    state_dict_adapter_class=<Model>StateDictAdapter,\n    parallelize_fn=parallelize_<model>,\n    supported_model_types=frozenset({\"<model_type>\"}),  # From HF config.json\n    pipelining_fn=pipeline_llm,\n)\n\n# Auto-register when module is imported\nregister_model_spec(<MODEL>_SPEC)\n\n__all__ = [\"<MODEL>_SPEC\"]\n```\n\n**Note**: `supported_model_types` should include all HF `model_type` strings that this\nimplementation handles (e.g., `{\"qwen3\", \"qwen3_moe\"}` for Qwen3).\n\n### Step 9: Register in `__init__.py`\n\nAdd the import to `areal/experimental/models/archon/__init__.py`:\n\n```python\nfrom areal.experimental.models.archon.<model> import spec as <model>_spec  # noqa: F401\n```\n\nThis triggers auto-registration when the module is imported.\n\n### Step 10: Verify and Test\n\nVerification should be done in stages, adapting based on available hardware and the test\npatterns in `tests/experimental/archon/`.\n\n**Before writing tests**, examine the existing test files to understand current\npatterns:\n\n```\ntests/experimental/archon/\n  conftest.py             -- Pytest configuration (version checks)\n  utils.py                -- Shared utilities (model loading, comparison)\n  test_qwen3_args.py      -- Args unit tests (CPU-only)\n  test_state_dict_adapter.py  -- State dict roundtrip tests\n  test_weight_sync.py     -- Weight completeness tests (meta device)\n  test_forward.py         -- Forward precision comparison (single GPU)\n  ...\n```\n\n**Test stages** (write tests appropriate for the model's complexity):\n\n#### Stage 1: Args Tests (CPU-only, always write these)\n\nTest `from_hf_config()` with mock HuggingFace configs:\n\n```python\n# Pattern: Create mock PretrainedConfig, verify args mapping\nfrom unittest.mock import MagicMock\n\ndef test_args_from_hf_config():\n    hf_config = MagicMock()\n    hf_config.hidden_size = 4096\n    hf_config.num_hidden_layers = 32\n    # ... set all required fields\n    args = <Model>ModelArgs.from_hf_config(hf_config)\n    assert args.dim == 4096\n    assert args.n_layers == 32\n```\n\n#### Stage 2: State Dict Adapter Tests (CPU-only)\n\nTest key mapping roundtrip:\n\n```python\ndef test_state_dict_roundtrip():\n    # Create adapter with mock config\n    adapter = <Model>StateDictAdapter(mock_config)\n    # Create fake archon state dict with expected keys\n    archon_sd = {\"tok_embeddings.weight\": torch.randn(vocab, dim), ...}\n    # Roundtrip\n    hf_sd = adapter.to_hf(archon_sd)\n    roundtrip = adapter.from_hf(hf_sd)\n    assert set(roundtrip.keys()) == set(archon_sd.keys())\n```\n\n#### Stage 3: Weight Completeness (meta device, CPU-only)\n\nVerify all model parameters have HF mappings:\n\n```python\ndef test_weight_completeness():\n    # Create model on meta device\n    with torch.device(\"meta\"):\n        model = <Model>Model(args)\n    adapter = <Model>StateDictAdapter(hf_config)\n    # Check every archon param has a HF mapping\n    for name, _ in model.named_parameters():\n        hf_pairs = adapter.convert_single_to_hf(name, torch.empty(0))\n        assert len(hf_pairs) > 0, f\"No HF mapping for {name}\"\n```\n\n#### Stage 4: Forward Precision (single GPU, if available)\n\nCompare Archon model output against HuggingFace reference:\n\n```python\n@pytest.mark.skipif(not torch.cuda.is_available(), reason=\"Requires CUDA\")\ndef test_forward_matches_hf():\n    # Load both HF and Archon models\n    # Run forward on same input\n    # Compare logits within tolerance\n```\n\n**Important**: Do NOT hardcode the test categories. Inspect the existing test files in\n`tests/experimental/archon/` and follow the same patterns, fixtures, and markers. Adapt\ntest scope to the model's specific features (e.g., add MoE-specific tests only if the\nmodel has MoE).\n\n## Reference Implementations\n\n| Model | Directory                                 | Features                                                |\n| ----- | ----------------------------------------- | ------------------------------------------------------- |\n| Qwen2 | `areal/experimental/models/archon/qwen2/` | Dense, attention bias, no QK norm                       |\n| Qwen3 | `areal/experimental/models/archon/qwen3/` | Dense + MoE, QK norm, no attention bias, shared experts |\n\n## Architecture Decision Map\n\n| Feature             | qwen2    | qwen3                      | What to check in target model                            |\n| ------------------- | -------- | -------------------------- | -------------------------------------------------------- |\n| Attention bias      | Yes      | No                         | `attention_bias` in HF config                            |\n| QK norm             | No       | Yes                        | `qk_norm` in HF config or QKNorm module in modeling file |\n| MoE                 | No       | Yes                        | `num_experts`/`num_local_experts` in HF config           |\n| Shared experts      | No       | Yes                        | `num_shared_experts` in HF config                        |\n| Decoder sparse step | No       | Yes                        | `decoder_sparse_step` in HF config                       |\n| Weight tying        | Both     | Both                       | `tie_word_embeddings` in HF config                       |\n| RoPE                | Standard | Standard (re-export qwen2) | Check inv_freq formula in HF modeling code               |\n\n## Common Mistakes\n\n- Not mapping all HF keys in `state_dict_adapter.py` (causes silent weight drops)\n- Wrong `from_hf_config()` field mapping (uses wrong HF config attribute name)\n- Forgetting to handle `None` keys in `from_hf_map` (keys to skip like\n  `rotary_emb.inv_freq`)\n- Missing MoE expert weight 3D\\<->2D conversion when model has MoE\n- Wrong TP plan for attention with/without QK norm (`use_local_output` must match)\n- Forgetting to add import line in `areal/experimental/models/archon/__init__.py`\n- Not including all `model_type` variants in `supported_model_types` frozenset\n- Using `print` instead of `areal.utils.logging.getLogger()`\n\n## File Checklist\n\nAfter completion, verify all files exist and are consistent:\n\n- [ ] `areal/experimental/models/archon/<model>/__init__.py`\n- [ ] `areal/experimental/models/archon/<model>/spec.py` -- ModelSpec + register\n- [ ] `areal/experimental/models/archon/<model>/model/args.py` -- ModelArgs +\n  from_hf_config\n- [ ] `areal/experimental/models/archon/<model>/model/model.py` -- Model + Attention +\n  FFN\n- [ ] `areal/experimental/models/archon/<model>/model/rope.py` -- RoPE (or re-export)\n- [ ] `areal/experimental/models/archon/<model>/model/state_dict_adapter.py` -- Key\n  mapping\n- [ ] `areal/experimental/models/archon/<model>/infra/parallelize.py` -- Parallel\n  strategy\n- [ ] `areal/experimental/models/archon/__init__.py` -- Import line added\n- [ ] `tests/experimental/archon/test_<model>_*.py` -- Tests\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nCanonical location: .agents/skills/add-archon-model/SKILL.md\nMirrors: .opencode/skills/add-archon-model/SKILL.md, .claude/skills/add-archon-model/SKILL.md\nInvocation: $add-archon-model (Codex) / /add-archon-model (OpenCode, Claude Code)\n\n## Purpose\n\nSemi-automated guide for adding new model architectures to the Archon training engine.\nUnlike simpler skills (add-reward, add-dataset), this skill actively guides the agent to:\n1. Analyze HuggingFace source code to extract architecture details\n2. Select the closest reference implementation (qwen2 or qwen3)\n3. Generate code skeletons adapted to the target architecture\n4. Create appropriate tests based on existing test patterns\n\n## How to Update\n\n### When New Reference Models Are Added\n1. Add to \"Reference Implementations\" table\n2. Update \"Architecture Decision Map\" with new feature columns\n3. Update Step 2 (reference selection) with new options\n\n### When Base Classes Change\n1. Update contract signatures in Steps 3, 4, 6, 7\n2. Update file checklist if new files are required\n\n### When ModelSpec Changes\n1. Update Step 8 with new ModelSpec fields\n2. Update spec.py template\n\n### When Test Patterns Change\n1. Update Step 10 with new test patterns\n2. Do NOT hardcode test categories -- keep it flexible\n\n### Important Design Decisions\n- This skill is SEMI-AUTOMATED: Claude should read HF source and generate code,\n  not just provide templates for the user to fill in manually\n- The skill references existing test files rather than hardcoding test categories,\n  ensuring it stays current as the test suite evolves\n- Reference model selection (qwen2 vs qwen3) is based on MoE and QK norm presence\n\n================================================================================\n-->\n",".agents/skills/add-dataset/SKILL.md":"---\nname: add-dataset\ndescription: Guide for adding a new dataset loader to AReaL. Use when user wants to add a new dataset.\n---\n\n# Add Dataset\n\nAdd a new dataset loader to AReaL.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a dataset?\"\n- User wants to integrate a new dataset\n- User mentions creating a dataset loader\n\n## Step-by-Step Guide\n\n### Step 1: Create Dataset File\n\nCreate `areal/dataset/<name>.py`:\n\n```python\nfrom datasets import Dataset, load_dataset\n\n\ndef get_<name>_sft_dataset(\n    path: str,\n    split: str,\n    tokenizer,\n    max_length: int | None = None,\n) -> Dataset:\n    \"\"\"Load dataset for SFT training.\n\n    Args:\n        path: Path to dataset (HuggingFace hub or local path)\n        split: Dataset split (train/validation/test)\n        tokenizer: Tokenizer for processing\n        max_length: Maximum sequence length (optional)\n\n    Returns:\n        HuggingFace Dataset with processed samples\n    \"\"\"\n    dataset = load_dataset(path=path, split=split)\n\n    def process(sample):\n        # Tokenize the full sequence (prompt + response)\n        seq_token = tokenizer.encode(\n            sample[\"question\"] + sample[\"answer\"] + tokenizer.eos_token\n        )\n        prompt_token = tokenizer.encode(sample[\"question\"])\n        # Loss mask: 0 for prompt, 1 for response\n        loss_mask = [0] * len(prompt_token) + [1] * (len(seq_token) - len(prompt_token))\n        return {\"input_ids\": seq_token, \"loss_mask\": loss_mask}\n\n    dataset = dataset.map(process).remove_columns([\"question\", \"answer\"])\n\n    if max_length is not None:\n        dataset = dataset.filter(lambda x: len(x[\"input_ids\"]) <= max_length)\n\n    return dataset\n\n\ndef get_<name>_rl_dataset(\n    path: str,\n    split: str,\n    tokenizer,\n    max_length: int | None = None,\n) -> Dataset:\n    \"\"\"Load dataset for RL training.\n\n    Args:\n        path: Path to dataset\n        split: Dataset split\n        tokenizer: Tokenizer for length filtering\n        max_length: Maximum sequence length\n\n    Returns:\n        HuggingFace Dataset with prompts and answers for reward computation\n    \"\"\"\n    dataset = load_dataset(path=path, split=split)\n\n    def process(sample):\n        messages = [\n            {\n                \"role\": \"user\",\n                \"content\": sample[\"question\"],\n            }\n        ]\n        return {\"messages\": messages, \"answer\": sample[\"answer\"]}\n\n    dataset = dataset.map(process).remove_columns([\"question\"])\n\n    if max_length is not None:\n\n        def filter_length(sample):\n            content = sample[\"messages\"][0][\"content\"]\n            tokens = tokenizer.encode(content)\n            return len(tokens) <= max_length\n\n        dataset = dataset.filter(filter_length)\n\n    return dataset\n```\n\n### Step 2: Register in __init__.py\n\nUpdate `areal/dataset/__init__.py`:\n\n```python\n# Add to VALID_DATASETS\nVALID_DATASETS = [\n    # ... existing datasets\n    \"<name>\",\n]\n\n# Add to _get_custom_dataset function\ndef _get_custom_dataset(name: str, ...):\n    # ... existing code\n    elif name == \"<name>\":\n        from areal.dataset.<name> import get_<name>_sft_dataset, get_<name>_rl_dataset\n        if dataset_type == \"sft\":\n            return get_<name>_sft_dataset(path, split, max_length, tokenizer)\n        else:\n            return get_<name>_rl_dataset(path, split, max_length, tokenizer)\n```\n\n### Step 3: Add Config (Optional)\n\nIf the dataset needs special configuration, add to `areal/api/cli_args.py`:\n\n```python\n@dataclass\nclass TrainDatasetConfig:\n    # ... existing fields\n    <name>_specific_field: Optional[str] = None\n```\n\n### Step 4: Add Tests\n\nCreate `tests/test_<name>_dataset.py`:\n\n```python\nimport pytest\nfrom areal.dataset.<name> import get_<name>_sft_dataset, get_<name>_rl_dataset\n\ndef test_sft_dataset_loads(tokenizer):\n    dataset = get_<name>_sft_dataset(\"path/to/data\", split=\"train\", tokenizer=tokenizer)\n    assert len(dataset) > 0\n    assert \"input_ids\" in dataset.column_names\n    assert \"loss_mask\" in dataset.column_names\n\ndef test_rl_dataset_loads(tokenizer):\n    dataset = get_<name>_rl_dataset(\"path/to/data\", split=\"train\", tokenizer=tokenizer)\n    assert len(dataset) > 0\n    assert \"messages\" in dataset.column_names\n    assert \"answer\" in dataset.column_names\n```\n\n## Reference Implementations\n\n| Dataset    | File                               | Description              |\n| ---------- | ---------------------------------- | ------------------------ |\n| GSM8K      | `areal/dataset/gsm8k.py`           | Math word problems       |\n| Geometry3K | `areal/dataset/geometry3k.py`      | Geometry problems        |\n| CLEVR      | `areal/dataset/clevr_count_70k.py` | Visual counting          |\n| HH-RLHF    | `areal/dataset/hhrlhf.py`          | Helpfulness/Harmlessness |\n| TORL       | `areal/dataset/torl_data.py`       | Tool-use RL              |\n\n## Required Fields\n\n### SFT Dataset\n\n```python\n{\n    \"messages\": [\n        {\"role\": \"user\", \"content\": \"...\"},\n        {\"role\": \"assistant\", \"content\": \"...\"},\n    ]\n}\n```\n\n### RL Dataset\n\n```python\n{\n    \"messages\": [\n        {\"role\": \"user\", \"content\": \"...\"},\n    ],\n    \"answer\": \"ground_truth_for_reward\",\n    # Optional metadata for reward function\n}\n```\n\n## Common Mistakes\n\n- Returning `List[Dict]` instead of HuggingFace `Dataset`\n- Using Python loops instead of `dataset.map()`/`filter()`\n- Missing `\"messages\"` field for RL datasets\n- Wrong message format (should be list of dicts with `role` and `content`)\n- Not registering in `__init__.py`\n",".agents/skills/add-reward/SKILL.md":"---\nname: add-reward\ndescription: Guide for adding a new reward function to AReaL. Use when user wants to create a reward function.\n---\n\n# Add Reward\n\nAdd a new reward function to AReaL.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a reward function?\"\n- User wants to implement custom rewards\n- User mentions reward computation\n\n## Step-by-Step Guide\n\n### Step 1: Create Reward File\n\nCreate `areal/reward/<name>.py`:\n\n```python\nfrom typing import Any\n\nfrom areal.utils import logging\n\nlogger = logging.getLogger(\"MyReward\")\n\n\ndef <name>_reward_fn(\n    prompt: str,\n    completions: str,\n    prompt_ids,\n    completion_ids,\n    answer: str | None = None,\n    **kwargs: Any,\n) -> float:\n    \"\"\"Compute reward for a single completion.\n\n    Args:\n        prompt: Prompt string\n        completions: Completion string (model output)\n        prompt_ids: Tokenized prompt IDs\n        completion_ids: Tokenized completion IDs\n        answer: Ground truth answer from dataset (optional)\n        **kwargs: Additional data from dataset\n\n    Returns:\n        Reward value (float), typically 0.0 or 1.0\n    \"\"\"\n    try:\n        # Extract answer from completion\n        extracted = _extract_answer(completions)\n\n        # Compare with ground truth\n        if answer is not None and extracted == str(answer):\n            return 1.0\n        return 0.0\n    except Exception:\n        logger.warning(\"Exception in reward computation\", exc_info=True)\n        return 0.0\n\n\ndef _extract_answer(completion: str) -> str:\n    \"\"\"Extract the answer from a completion string.\n\n    Implement your extraction logic here.\n    \"\"\"\n    # Example: Extract content from \\boxed{}\n    import re\n\n    match = re.search(r\"\\\\boxed\\{([^}]+)\\}\", completion)\n    if match:\n        return match.group(1).strip()\n    return completion.strip()\n```\n\n### Step 2: Register in __init__.py\n\nUpdate `areal/reward/__init__.py`:\n\n```python\n# Add to VALID_REWARD_FN\nVALID_REWARD_FN = [\n    # ... existing reward functions\n    \"<name>\",\n]\n\n# Add to get_reward_fn function\ndef get_reward_fn(name: str, **kwargs):\n    # ... existing code\n    elif name == \"<name>\":\n        from areal.reward.<name> import <name>_reward_fn\n        return <name>_reward_fn\n```\n\n### Step 3: Handle Blocking Operations\n\nIf your reward function uses blocking operations (e.g., API calls, model inference), the\nworkflow will wrap it with `AsyncRewardWrapper`:\n\n```python\n# In your workflow\nfrom areal.reward import AsyncRewardWrapper\n\nself.reward_fn = AsyncRewardWrapper(reward_fn)\n\n# Then call it asynchronously\nrewards = await self.reward_fn(prompt, completions, **data)\n```\n\n### Step 4: Add Tests\n\nCreate `tests/test_<name>_reward.py`:\n\n```python\nimport pytest\nfrom areal.reward.<name> import <name>_reward_fn\n\ndef test_reward_correct_answer():\n    reward = <name>_reward_fn(\n        prompt=\"What is 2+2?\",\n        completions=\"The answer is \\\\boxed{4}\",\n        prompt_ids=None,\n        completion_ids=None,\n        answer=\"4\",\n    )\n    assert reward == 1.0\n\ndef test_reward_wrong_answer():\n    reward = <name>_reward_fn(\n        prompt=\"What is 2+2?\",\n        completions=\"The answer is \\\\boxed{5}\",\n        prompt_ids=None,\n        completion_ids=None,\n        answer=\"4\",\n    )\n    assert reward == 0.0\n```\n\n## Reference Implementations\n\n| Reward     | File                              | Description                  |\n| ---------- | --------------------------------- | ---------------------------- |\n| GSM8K      | `areal/reward/gsm8k.py`           | Math answer verification     |\n| Geometry3K | `areal/reward/geometry3k.py`      | Geometry answer verification |\n| CLEVR      | `areal/reward/clevr_count_70k.py` | Counting verification        |\n| MathVerify | `areal/reward/math_verify.py`     | General math verification    |\n\n## Function Signature\n\nAll reward functions must follow this signature:\n\n```python\ndef reward_fn(\n    prompt: str,               # Input prompt string\n    completions: str,          # Model completion string\n    prompt_ids,                # Tokenized prompt\n    completion_ids,            # Tokenized completion\n    **kwargs: Any,             # Additional data from dataset (e.g., answer)\n) -> float:                    # Reward value (typically 0.0 or 1.0)\n```\n\n**Note**: The reward function is called once per sample. Batching is handled by\n`AsyncRewardWrapper` in the workflow.\n\n## Key Requirements\n\n1. **Deterministic**: Same inputs should produce same outputs\n1. **Return float**: Output is a single float value per sample\n1. **No blocking in async context**: Use `AsyncRewardWrapper` if needed\n1. **Logging**: Use `areal.utils.logging`, not `print`\n1. **Handle exceptions**: Return 0.0 on error, don't raise\n\n## Common Mistakes\n\n- Returning a tensor instead of a float\n- Expecting batched inputs (reward is called per sample)\n- Non-deterministic behavior\n- Blocking operations without `AsyncRewardWrapper`\n- Raising exceptions instead of returning 0.0\n",".agents/skills/add-unit-tests/SKILL.md":"---\nname: add-unit-tests\ndescription: Guide for adding unit tests to AReaL. Use when user wants to add tests for new functionality or increase test coverage.\n---\n\n# Add Unit Tests\n\nAdd unit tests to AReaL following the project's testing conventions.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add tests?\"\n- User wants to increase test coverage\n- User needs to write tests for new functionality\n- User wants to understand AReaL testing patterns\n\n## Step-by-Step Guide\n\n### Step 1: Understand Test Types\n\nAReaL has two main test categories:\n\n| Test Type             | Purpose                            | Location Pattern                   | How It Runs                                |\n| --------------------- | ---------------------------------- | ---------------------------------- | ------------------------------------------ |\n| **Unit Tests**        | Test individual functions/modules  | `tests/test_<module>_<feature>.py` | Directly via pytest                        |\n| **Distributed Tests** | Test distributed/parallel behavior | `tests/torchrun/run_*.py`          | Via torchrun (called by pytest subprocess) |\n\n**Note**: All tests are invoked via pytest. Distributed tests use `torchrun` but are\nstill called from pytest test files.\n\n### Step 2: Create Test File Structure\n\nCreate test file with naming convention: `test_<module>_<feature>.py`\n\n```python\nimport pytest\nimport torch\n\n# Import the module to test\nfrom areal.dataset.gsm8k import get_gsm8k_sft_dataset\nfrom tests.utils import get_dataset_path  # Optional test utilities\n# For mocking tokenizer: from unittest.mock import MagicMock\n```\n\n### Step 3: Write Test Functions\n\nFollow Arrange-Act-Assert pattern:\n\n```python\ndef test_function_under_condition_returns_expected():\n    \"\"\"Test that function returns expected value under condition.\"\"\"\n    # Arrange\n    input_data = 5\n    expected_output = 10\n\n    # Act\n    result = function_under_test(input_data)\n\n    # Assert\n    assert result == expected_output\n```\n\n### Step 4: Add Pytest Markers and CI Strategy\n\nUse appropriate pytest markers:\n\n| Marker                                  | When to Use                                                  |\n| --------------------------------------- | ------------------------------------------------------------ |\n| `@pytest.mark.slow`                     | Test takes > 10 seconds (excluded from CI by default)        |\n| `@pytest.mark.ci`                       | Slow test that must run in CI (use with `@pytest.mark.slow`) |\n| `@pytest.mark.asyncio`                  | Async test functions                                         |\n| `@pytest.mark.skipif(cond, reason=...)` | Conditional skip                                             |\n| `@pytest.mark.parametrize(...)`         | Parameterized tests                                          |\n\n**CI Test Strategy**:\n\n- `@pytest.mark.slow`: Excluded from CI by default (CI runs `pytest -m \"not slow\"`)\n- `@pytest.mark.slow` + `@pytest.mark.ci`: Slow but must run in CI\n- No marker: Runs in CI (fast unit tests)\n\n```python\n@pytest.mark.asyncio\nasync def test_async_function():\n    result = await async_function()\n    assert result == expected\n\n@pytest.mark.skipif(not torch.cuda.is_available(), reason=\"CUDA not available\")\ndef test_gpu_feature():\n    tensor = torch.tensor([1, 2, 3], device=\"cuda\")\n    # ... assertions\n\n@pytest.mark.parametrize(\"batch_size\", [1, 4, 16])\ndef test_with_parameters(batch_size):\n    # Parameterized test\n\n@pytest.mark.slow\ndef test_slow_function():\n    # Excluded from CI by default\n\n@pytest.mark.slow\n@pytest.mark.ci\ndef test_slow_but_required_in_ci():\n    # Slow but must run in CI\n```\n\n### Step 5: Mock Distributed Environment\n\nFor unit tests that need distributed mocks:\n\n```python\nimport torch.distributed as dist\n\ndef test_distributed_function(monkeypatch):\n    monkeypatch.setattr(dist, \"get_rank\", lambda: 0)\n    monkeypatch.setattr(dist, \"get_world_size\", lambda: 2)\n    result = distributed_function()\n    assert result == expected\n```\n\n### Step 6: Handle GPU Dependencies\n\nAlways skip gracefully when GPU unavailable:\n\n```python\nCUDA_AVAILABLE = torch.cuda.is_available()\n\n@pytest.mark.skipif(not CUDA_AVAILABLE, reason=\"CUDA not available\")\ndef test_gpu_function():\n    tensor = torch.tensor([1, 2, 3], device=\"cuda\")\n    # ... assertions\n```\n\n## Key Requirements (Based on testing.md)\n\n### Mocking Distributed\n\n- Use `torch.distributed.fake_pg` for unit tests\n- Mock `dist.get_rank()` and `dist.get_world_size()` explicitly\n- Don't mock internals of FSDP/DTensor\n\n### GPU Test Constraints\n\n- **Always skip gracefully** when GPU unavailable\n- Clean up GPU memory: `torch.cuda.empty_cache()` in fixtures\n- Use smallest possible model/batch for unit tests\n\n### Assertions\n\n- Use `torch.testing.assert_close()` for tensor comparison\n- Specify `rtol`/`atol` explicitly for numerical tests\n- Avoid bare `assert tensor.equal()` - no useful error message\n\n## Reference Implementations\n\n| Test File                        | Description                            | Key Patterns                                      |\n| -------------------------------- | -------------------------------------- | ------------------------------------------------- |\n| `tests/test_utils.py`            | Utility function tests                 | Fixtures, parametrized tests                      |\n| `tests/test_examples.py`         | Integration tests with dataset loading | Dataset path resolution, success pattern matching |\n| `tests/test_fsdp_engine_nccl.py` | Distributed tests                      | Torchrun integration                              |\n\n## Common Mistakes\n\n- **Missing test file registration**: Ensure file follows `test_*.py` naming\n- **GPU dependency without skip**: Always use `@pytest.mark.skipif` for GPU tests\n- **Incorrect tensor comparisons**: Use `torch.testing.assert_close()` not\n  `assert tensor.equal()`\n- **Memory leaks in GPU tests**: Clean up with `torch.cuda.empty_cache()`\n- **Mocking too much**: Don't mock FSDP/DTensor internals\n- **Unclear test names**: Follow `test_<what>_<condition>_<expected>` pattern\n- **No docstrings**: Add descriptive docstrings to test functions\n\n## Integration with Other Skills\n\nThis skill complements other AReaL development skills:\n\n- **After `/add-dataset`**: Add tests for new dataset loaders\n- **After `/add-workflow`**: Add tests for new workflows\n- **After `/add-reward`**: Add tests for new reward functions\n- **With expert agents**: Reference this skill when planning test implementation\n\n## Running Tests\n\n```bash\n# First check GPU availability (many tests require GPU)\npython -c \"import torch; print('GPU available:', torch.cuda.is_available())\"\n\n# Run specific test file\nuv run pytest tests/test_<name>.py\n\n# Skip slow tests (CI default)\nuv run pytest -m \"not slow\"\n\n# Run with verbose output\nuv run pytest -v\n\n# Run distributed tests (requires torchrun and multi-GPU)\n# Note: Usually invoked via pytest test files\ntorchrun --nproc_per_node=2 tests/torchrun/run_<test>.py\n```\n",".agents/skills/add-workflow/SKILL.md":"---\nname: add-workflow\ndescription: Guide for adding a new RolloutWorkflow to AReaL. Use when user wants to create a new workflow.\n---\n\n# Add Workflow\n\nAdd a new RolloutWorkflow implementation to AReaL.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a workflow?\"\n- User wants to create a new RolloutWorkflow\n- User mentions implementing a custom rollout\n\n## Prerequisites\n\nBefore starting, ensure you understand:\n\n- The workflow's purpose and requirements\n- Input/output data format\n- Reward function to use\n\n## Step-by-Step Guide\n\n### Step 1: Create Workflow File\n\nCreate `areal/workflow/<name>.py`:\n\n```python\nimport uuid\nfrom typing import Any, Callable\n\nimport torch\n\nfrom areal.api.cli_args import GenerationHyperparameters\nfrom areal.api.engine_api import InferenceEngine\nfrom areal.api.io_struct import ModelRequest, ModelResponse\nfrom areal.api.reward_api import AsyncRewardWrapper\nfrom areal.api.workflow_api import RolloutWorkflow\nfrom areal.utils import logging\n\nlogger = logging.getLogger(\"MyWorkflow\")\n\n\nclass MyWorkflow(RolloutWorkflow):\n    \"\"\"Description of your workflow.\"\"\"\n\n    def __init__(\n        self,\n        gconfig: GenerationHyperparameters,\n        tokenizer,\n        reward_fn: Callable,\n    ):\n        self.gconfig = gconfig.new_with_stop_and_pad_token_ids(tokenizer)\n        self.tokenizer = tokenizer\n        self.async_reward_fn = AsyncRewardWrapper(reward_fn)\n\n    async def arun_episode(\n        self,\n        engine: InferenceEngine,\n        data: dict[str, Any],\n    ) -> dict[str, Any] | None | dict[str, InteractionWithTokenLogpReward]:\n        \"\"\"Run a single episode. MUST be async and non-blocking.\"\"\"\n\n        # 1. Prepare input_ids from data\n        input_ids = self.tokenizer.apply_chat_template(\n            data[\"messages\"],\n            tokenize=True,\n            add_generation_prompt=True,\n        )\n\n        # 2. Build ModelRequest\n        req = ModelRequest(\n            rid=uuid.uuid4().hex,\n            input_ids=list(input_ids),\n            gconfig=self.gconfig.new(n_samples=1),\n            tokenizer=self.tokenizer,\n        )\n\n        # 3. Generate completion (async)\n        resp: ModelResponse = await engine.agenerate(req)\n\n        # 4. Compute reward (async)\n        prompt_str = self.tokenizer.decode(input_ids)\n        completion_str = self.tokenizer.decode(resp.output_tokens)\n        reward = await self.async_reward_fn(\n            prompt_str,\n            completion_str,\n            resp.input_tokens,\n            resp.output_tokens,\n            **data,\n        )\n\n        # 5. Return results in expected format\n        return {\n            \"input_ids\": torch.tensor(resp.input_tokens),\n            \"output_ids\": torch.tensor(resp.output_tokens),\n            \"reward\": torch.tensor(reward),\n        }\n```\n\n### Step 2: Register in __init__.py\n\nAdd to `areal/workflow/__init__.py`:\n\n```python\nfrom areal.workflow.<name> import MyWorkflow\n\n__all__ = [\n    # ... existing exports\n    \"MyWorkflow\",\n]\n```\n\n### Step 3: Update Entry Script\n\nUpdate your training script to use the new workflow:\n\n```python\ntrainer.train(\n    workflow=\"areal.workflow.<name>.MyWorkflow\",\n    # ... other args\n)\n```\n\n### Step 4: Add Tests\n\nCreate `tests/test_<name>_workflow.py`:\n\n```python\nimport pytest\nfrom areal.workflow.<name> import MyWorkflow\n\n@pytest.mark.asyncio\nasync def test_workflow_basic():\n    # Test basic functionality\n    pass\n```\n\n## Reference Implementations\n\n| Workflow           | File                            | Description                |\n| ------------------ | ------------------------------- | -------------------------- |\n| MultiTurnWorkflow  | `areal/workflow/multi_turn.py`  | Multi-turn conversation    |\n| RLVRWorkflow       | `areal/workflow/rlvr.py`        | RL with verifiable rewards |\n| VisionRLVRWorkflow | `areal/workflow/vision_rlvr.py` | Vision + RLVR              |\n\n## Key Requirements\n\n1. **Async**: `arun_episode` must be `async def` and non-blocking\n1. **No sync I/O**: Use `aiofiles` for file operations\n1. **Wrap rewards**: Use `AsyncRewardWrapper` for reward functions\n1. **Tensor format**: Output tensors should be `[batch, seq_len, ...]`\n1. **Use helpers**: `concat_padded_tensors` for combining outputs\n\n## Common Mistakes\n\n- Using `open()` instead of `aiofiles.open()`\n- Forgetting to `await` async calls\n- Not wrapping reward function with `AsyncRewardWrapper`\n- Wrong tensor shape conventions\n",".agents/skills/commit-conventions/SKILL.md":"---\nname: commit-conventions\ndescription: AReaL commit message conventions. MUST load on every git commit -- provides Conventional Commits format with scope inference from file paths.\n---\n\n# Commit Conventions\n\nCommit message conventions and scope inference rules for the AReaL repository.\n\n## When to Use\n\n**ALWAYS load this skill when making any git commit in AReaL.** This includes:\n\n- Direct commits (`git commit`)\n- Commits during PR creation (`$create-pr` / `/create-pr`)\n- Commits delegated to sub-agents with this skill loaded\n- Any agent workflow that produces a commit\n\n## Commit Message Format\n\n```\n<type>(<scope>): <subject>\n\n<body>\n\n[Optional sections:]\nKey changes:\n- change 1\n- change 2\n\nRefs: #123, #456\n```\n\n## Type Selection\n\n| Type       | When to Use                      |\n| ---------- | -------------------------------- |\n| `feat`     | New feature or capability        |\n| `fix`      | Bug fix                          |\n| `docs`     | Documentation only               |\n| `gov`      | Governance or maintainer changes |\n| `style`    | Formatting/style-only changes    |\n| `refactor` | Code change without feature/fix  |\n| `perf`     | Performance improvement          |\n| `test`     | Adding or fixing tests           |\n| `build`    | Build system or dependencies     |\n| `ci`       | CI pipeline or workflow changes  |\n| `chore`    | Build, deps, config changes      |\n| `revert`   | Revert a previous commit         |\n\n## Scope Inference\n\nInfer scope from the **primary** changed file paths:\n\n| File Path Pattern                                            | Scope                          |\n| ------------------------------------------------------------ | ------------------------------ |\n| `areal/workflow/`                                            | `workflow`                     |\n| `areal/engine/`                                              | `engine`                       |\n| `areal/reward/`                                              | `reward`                       |\n| `areal/dataset/`                                             | `dataset`                      |\n| `areal/api/`                                                 | `api`                          |\n| `areal/utils/`                                               | `utils`                        |\n| `areal/infra/`                                               | `infra`                        |\n| `areal/trainer/`                                             | `trainer`                      |\n| `areal/models/`                                              | `models`                       |\n| `areal/experimental/`                                        | `experimental`                 |\n| `docs/`                                                      | `docs`                         |\n| `examples/`                                                  | `examples`                     |\n| `AGENTS.md`, `.agents/`, `.claude/`, `.codex/`, `.opencode/` | `agents`                       |\n| Multiple areas                                               | Omit scope or use broader term |\n\n## Rules\n\n- **Subject**: imperative mood, ~50-72 chars, no trailing period\n- **Body**: explain \"why\" not \"what\", wrap at 72 chars\n- **Key changes**: bullet list of main modifications (for complex commits with 3+ files)\n- **Refs**: reference issues/PRs if applicable\n\n## Examples\n\n**Single file fix:**\n\n```\nfix(reward): handle empty completion in gsm8k\n\nReturn 0 reward instead of raising exception when\ncompletion string is empty after extraction.\n```\n\n**Multi-file feature:**\n\n```\nfeat(engine): add CPU offload support to ArchonEngine\n\nEnable torch_memory_saver for model offloading during\nrollout phase to reduce GPU memory pressure.\n\nKey changes:\n- Add offload/onload methods to ArchonEngine\n- Integrate with weight update flow\n- Handle ROCm compatibility\n```\n\n**Docs only:**\n\n```\ndocs: update algorithm comparison table\n\nAdd SAPO and GSPO to the algorithm family documentation\nwith configuration examples.\n```\n\n**Agent/tooling changes:**\n\n```\nchore(agents): port review-pr command to OpenCode\n\nAdd OpenCode-native commands with task() category\ndelegation instead of hardcoded model names.\n\nKey changes:\n- Create .opencode/command/ with review-pr, create-pr\n- Replace hardcoded model routing with platform-native review routing\n- Add expert subagent consultation patterns\n```\n\n**Governance/maintainer changes:**\n\n```\ngov(agents): add maintainer ownership for service modules\n\nUpdate CODEOWNERS and maintainer references to reflect\ncurrent governance responsibilities.\n\nKey changes:\n- Add maintainers for agent_service and infra ownership\n- Align governance docs with updated reviewer responsibilities\n```\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nCanonical location: .agents/skills/commit-conventions/SKILL.md\nMirrors: .opencode/skills/commit-conventions/SKILL.md, .claude/skills/commit-conventions/SKILL.md\nInvocation: Automatically loaded on every git commit (all platforms)\n\n## Purpose\n\nProvides Conventional Commits format with AReaL-specific scope inference\nfrom file paths. Unlike other skills, this one is NOT user-triggered --\nit is loaded by the system/agent on every commit operation.\n\n## How to Update\n\n### When New Modules Are Added\n1. Add the file path pattern and scope to the \"Scope Inference\" table\n2. Keep table sorted by areal/ subpackages first, then top-level dirs\n\n### When Commit Types Change\n1. Update the \"Type Selection\" table\n2. Add/update examples to illustrate the new type\n\n### When Adding Examples\n1. Each example should demonstrate a distinct commit pattern\n2. Keep examples realistic -- use actual AReaL module names\n3. Show both subject-only and subject+body+key-changes variants\n\n### Important Design Decisions\n- This skill is ALWAYS loaded (not optional) -- keep it concise to\n  minimize token overhead on every commit\n- Scope inference is path-based, not content-based -- simpler and\n  more deterministic\n- \"Multiple areas\" -> omit scope rather than invent a new one\n\n================================================================================\n-->\n",".agents/skills/create-pr/SKILL.md":"---\nname: create-pr\ndescription: Rebase the current branch onto the latest base branch, squash local commits, generate a Conventional Commit message, and create or update the GitHub pull request.\n---\n\n# Create Pull Request\n\nUse this skill when the user asks to create or update a PR for the current branch.\n\n## Inputs\n\n- Optional `--draft`\n- Optional `--base <branch>` (default: `main`)\n\n## Preconditions\n\n1. Verify the current branch is not `main` or `master`.\n1. Check for uncommitted changes with `git status --short`.\n1. Ensure `gh` is available.\n1. If there are uncommitted changes, stop and ask the user whether to commit or stash\n   them first.\n\n## Workflow\n\n### Step 1: Check for an existing PR\n\n- Run `gh pr view --json number,title,url,state,isDraft`.\n- If a PR already exists, tell the user before rewriting history or force-pushing.\n\n### Step 2: Fetch and rebase\n\n```bash\ngit fetch origin <base>\ngit rebase origin/<base>\n```\n\n- If rebase conflicts occur, abort the rebase and stop.\n- Tell the user which files conflicted and ask them to resolve manually.\n\n### Step 3: Squash into one commit\n\n```bash\ngit reset --soft origin/<base>\n```\n\n- Load the `commit-conventions` skill before generating the commit message.\n- Infer `type` and `scope` from the staged diff.\n- Keep the commit subject imperative and under about 72 characters.\n\n### Step 4: Generate PR title and body\n\n- Use the squashed commit message style for the PR title.\n- Follow the repository PR template at `.github/PULL_REQUEST_TEMPLATE.md`.\n- Summarize user-facing changes, risk areas, test commands run, and skipped suites with\n  reasons.\n\n### Step 5: Push and create or update the PR\n\n- Push the branch.\n- If history was rewritten, confirm before force-pushing.\n- Create or update the PR with `gh pr create` or `gh pr edit`.\n- Respect `--draft` when requested.\n\n## Guardrails\n\n- Never create a PR from `main` or `master`.\n- Never silently force-push over an existing PR branch.\n- Never bypass `commit-conventions` for the squashed commit.\n- If `gh` authentication or remote permissions fail, stop and report the exact blocker.\n\n## Output\n\nReport:\n\n- Base branch used\n- Final commit message\n- PR title\n- PR URL, if creation or update succeeded\n- Any steps that were skipped or require user follow-up\n",".agents/skills/debug-distributed/SKILL.md":"---\nname: debug-distributed\ndescription: Guide for debugging distributed training issues in AReaL. Use when user encounters hangs, wrong results, OOM, or communication errors.\n---\n\n# Debug Distributed Training\n\nDebugging guide for distributed training issues in AReaL (FSDP2, TP, CP, EP).\n\n## When to Use\n\nThis skill is triggered when:\n\n- Training hangs or deadlocks\n- Results differ across ranks or are numerically wrong\n- OOM errors in distributed settings\n- NCCL/communication errors or device mesh issues\n\n## Debugging Principles\n\n### Minimal Reproduction\n\n**Always follow the minimal demo principle**: Reproduce with the least amount of code to\nnarrow down the issue faster.\n\n```python\n# Bad: Debug in full training loop\n# Good: Create minimal script\nimport torch\nimport torch.distributed as dist\n\ndist.init_process_group(\"nccl\")\nrank = dist.get_rank()\n\n# Reproduce the exact operation that fails\ntensor = torch.ones(10).cuda()\ndist.all_reduce(tensor)  # <-- Isolate the failing op\nprint(f\"Rank {rank}: {tensor}\")\n```\n\n**Reduction strategy:**\n\n1. Remove unrelated model components\n1. Use small tensor sizes\n1. Reduce world_size to minimum (e.g., 2 GPUs)\n1. Remove torch.compile if possible\n1. Disable activation checkpointing\n\n## Step-by-Step Debugging Guide\n\n### 1. Hang Debugging (Deadlocks, Synchronization)\n\n**Environment Variables for Debugging**:\n\n```bash\n# Full debug logging\nexport TORCH_DISTRIBUTED_DEBUG=DETAIL\nexport NCCL_DEBUG=INFO\nexport NCCL_DEBUG_SUBSYS=ALL\n\n# torch.compile debugging\nexport TORCH_LOGS=\"+dynamo,recompiles\"\nexport TORCHDYNAMO_VERBOSE=1\n```\n\n**Dump Call Stack with py-spy** (for hung processes):\n\n```bash\n# Find process IDs\nps aux | grep python\n\n# Dump call stack of specific rank\npy-spy dump --pid <PID>\n\n# Record flame graph for performance analysis\npy-spy record -o profile.svg --pid <PID> --duration 30\n```\n\n**Common Causes**:\n\n1. **Mismatched Collectives**: One rank calls `all_reduce`, another doesn't.\n1. **Wrong Process Group**: Using wrong group for collective.\n1. **Tensor Shape Mismatch**: Different shapes across ranks.\n\n**Debug Steps**:\n\n```python\n# Verify group membership\nmesh = parallel_dims.get_mesh(\"dp_shard_cp\")\ngroup = mesh.get_group()\nprint(f\"Rank {dist.get_rank()}: group size = {dist.get_world_size(group)}\")\n\n# Print shapes on all ranks\nprint(f\"Rank {dist.get_rank()}: tensor.shape = {tensor.shape}\")\ndist.barrier()\n```\n\n**Timeout Adjustment** (for debugging only):\n\n```python\nfrom areal.engine.core.distributed import patch_dist_group_timeout\nfrom datetime import timedelta\npatch_dist_group_timeout(timedelta(minutes=30))\n```\n\n### 2. Wrong Results (Gradient, Reduction Issues)\n\n**Check DTensor Placements**:\n\n```python\nfrom torch.distributed.tensor import DTensor\nif isinstance(param, DTensor):\n    print(f\"Param {name}: placements={param.placements}, mesh={param.device_mesh}\")\n```\n\n**Verify Gradient Reduction**:\n\n```python\nfor name, param in model.named_parameters():\n    if param.grad is not None:\n        print(f\"Rank {dist.get_rank()}: {name} grad_sum = {param.grad.sum().item()}\")\n```\n\n### 3. OOM Issues (Memory, Sharding)\n\n**Check Memory Usage**:\n\n```python\nprint(f\"Rank {dist.get_rank()}: \"\n      f\"allocated={torch.cuda.memory_allocated()/1e9:.2f}GB, \"\n      f\"reserved={torch.cuda.memory_reserved()/1e9:.2f}GB\")\n```\n\n**Check FSDP Coverage**:\n\n```python\nfor name, param in model.named_parameters():\n    is_dtensor = isinstance(param, DTensor)\n    print(f\"{name}: is_dtensor={is_dtensor}, shape={param.shape}\")\n```\n\n### 4. Communication Errors\n\n| Error                     | Cause                | Solution                           |\n| ------------------------- | -------------------- | ---------------------------------- |\n| `NCCL WARN Cuda failure`  | GPU communication    | Check NCCL version, GPU topology   |\n| `RuntimeError: Timed out` | Rank synchronization | Increase timeout, check code paths |\n| `Invalid device mesh`     | Mesh configuration   | Verify world_size = dp * tp * cp   |\n\n## Debugging Tools\n\n### Environment Variables Reference\n\n| Variable                          | Purpose                                |\n| --------------------------------- | -------------------------------------- |\n| `TORCH_DISTRIBUTED_DEBUG=DETAIL`  | Detailed distributed logging           |\n| `NCCL_DEBUG=INFO`                 | NCCL communication logging             |\n| `NCCL_DEBUG_SUBSYS=ALL`           | All NCCL subsystems                    |\n| `TORCH_LOGS=\"+dynamo,recompiles\"` | torch.compile logging                  |\n| `TORCHDYNAMO_VERBOSE=1`           | Dynamo verbose output                  |\n| `CUDA_LAUNCH_BLOCKING=1`          | Synchronous CUDA (slow, for debugging) |\n\n### py-spy for Call Stack Analysis\n\n```bash\n# Install\npip install py-spy\n\n# Dump call stack of hung process\npy-spy dump --pid <PID>\n\n# Dump all Python processes\npgrep -f python | xargs -I {} py-spy dump --pid {}\n\n# Record flame graph\npy-spy record -o profile.svg --pid <PID> --duration 30\n```\n\n### Rank-Conditional Printing\n\n```python\ndef print_all_ranks(msg):\n    for r in range(dist.get_world_size()):\n        if dist.get_rank() == r:\n            print(f\"[Rank {r}] {msg}\")\n        dist.barrier()\n```\n\n### Check Device Mesh\n\n```python\ndef debug_mesh(parallel_dims):\n    mesh = parallel_dims.world_mesh\n    for dim_name in mesh.mesh_dim_names:\n        submesh = parallel_dims.get_mesh(dim_name)\n        if submesh:\n            print(f\"Rank {dist.get_rank()}: {dim_name} size={submesh.size()}\")\n```\n\n### Validate Tensor Consistency\n\n```python\ndef check_tensor_consistency(tensor, name, group=None):\n    local_sum = tensor.sum().item()\n    tensor_sums = [None] * dist.get_world_size(group)\n    dist.all_gather_object(tensor_sums, local_sum, group=group)\n    if dist.get_rank() == 0 and len(set(tensor_sums)) > 1:\n        print(f\"WARNING: {name} inconsistent: {tensor_sums}\")\n```\n\n## Key Files Reference\n\n| Component       | File                                                          |\n| --------------- | ------------------------------------------------------------- |\n| Parallel Dims   | `areal/experimental/models/archon/parallel_dims.py`           |\n| Expert Parallel | `areal/experimental/models/archon/expert_parallel.py`         |\n| Ulysses (CP)    | `areal/experimental/models/archon/ulysses.py`                 |\n| FSDP/TP Apply   | `areal/experimental/models/archon/qwen2/infra/parallelize.py` |\n",".agents/skills/review-pr/MIGRATION.md":"# `/review-pr` Refactor & Update Plan (Codex / OpenCode / Claude)\n\nLast updated: 2026-03-31 Owner: AI workflow maintainers Scope:\n`.agents/skills/review-pr/*`, `.opencode/{command,data}/review-pr*`,\n`.claude/{commands,data}/review-pr*`\n\n______________________________________________________________________\n\n## 1. Goals and Non-Goals\n\n### Goals\n\n1. Rebuild `/review-pr` taxonomy and templates for better coverage of new AReaL modules.\n1. Keep template count manageable while preserving depth for high-risk PRs.\n1. Eliminate cross-platform drift through a single semantic source of truth.\n1. Reduce false positives by prioritizing path-based triggers.\n\n### Non-Goals\n\n1. Changing `/review-pr` into a file-mutating workflow (must remain read-only).\n1. Adding one-off niche templates for every micro-feature.\n1. Rewriting all platform command orchestration logic in one iteration.\n\n______________________________________________________________________\n\n## 2. Current State Summary (Problem Statement)\n\n### What works\n\n1. Existing workflow phases are clear (context -> analysis -> planning -> delegated\n   review -> summary).\n1. Platform-specific command wrappers already exist and are functional.\n1. Risk-first review behavior (CRITICAL/HIGH/MEDIUM/LOW) is already enforced.\n\n### What is broken or stale\n\n1. Domain/signal matrix lags behind repository evolution (`tree_attn`, `vllm_ext`,\n   service stack, `archon_weight_sync`).\n1. Taxonomy is overly fragmented in some areas and missing in others.\n1. Three-platform data files are near-duplicates and easy to drift.\n1. Trigger rules rely heavily on generic keywords in places where path triggers are\n   safer.\n\n______________________________________________________________________\n\n## 3. Target Information Architecture\n\nUse a **two-layer taxonomy**:\n\n- **L1 Domain (12 domains)**: template-level planning unit.\n- **L2 Signals (2-9 per domain)**: detection and checklist specialization unit.\n\nThis avoids both extremes: too many tiny templates or too coarse single-pass review.\n\n### 3.1 L1 Domains and L2 Signals\n\n1. **Distributed Runtime**\n   - L2: `archon_core`, `archon_parallel`, `process_group`, `fsdp_core`,\n     `megatron_core`, `collectives`, `mesh_dtensor`, `activation_checkpointing`,\n     `weight_sync`\n1. **Model Compute & Attention**\n   - L2: `tree_attn`, `sdpa_varlen`, `sp_cp_attention_mask`, `triton_kernel`,\n     `archon_model_family`, `archon_attention_stack`, `archon_moe_modeling`\n1. **Inference Backend & Serving**\n   - L2: `vllm_ext`, `remote_inference_backend`, `request_lifecycle`\n1. **Service Orchestration**\n   - L2: `service_routing_dataflow`, `session_consistency`\n1. **Workflow & Trainer Contract**\n   - L2: `workflow_engine_boundary`, `dataset_surface`, `async_contract`,\n     `weight_version_contract`\n1. **API & Config Compatibility**\n   - L2: `dataclass_schema`, `cli_compat`, `backward_compat`,\n     `project_dependency_config`\n1. **Numerics & Tensor Semantics**\n   - L2: `shape_dtype`, `numerical_stability`, `reward_surface`, `compile_dynamo`,\n     `mixed_precision_fp8`\n1. **Checkpoint & Recovery**\n   - L2: `dcp_consistency`, `optimizer_state`, `resume_compat`\n1. **Launcher & Infrastructure**\n   - L2: `launcher_resource_match`, `scheduler_contract`, `rpc_transport`,\n     `runtime_image_config`\n1. **Low-Risk Hygiene**\n   - L2: `tests_docs_config`, `logging_import_security`, `project_docs_metadata`\n1. **Harness & Agent Infrastructure**\n   - L2: `skill_definition`, `platform_command_data`, `agent_registry_config`\n1. **CI/CD & Release Automation**\n   - L2: `github_workflow_jobs`, `runner_provisioning`, `release_delivery`\n\n______________________________________________________________________\n\n## 4. Detection Strategy (Path-first)\n\n### 4.1 Trigger priority\n\n1. **Path trigger (primary)**\n1. **Keyword trigger (secondary, only to refine existing path hits)**\n1. **Linkage trigger (auto-add dependent checks)**\n\n### 4.2 Canonical path mapping (v1)\n\n1. `areal/models/tree_attn/**` -> Model Compute & Attention (HIGH/MEDIUM by touched\n   files)\n1. `areal/engine/vllm_ext/**` -> Inference Backend & Serving (HIGH)\n1. `areal/experimental/models/archon/**` + `areal/experimental/engine/archon_engine.py`\n   \\+ `areal/experimental/engine/archon_checkpoint.py` -> Distributed Runtime + Model\n   Compute & Attention (CRITICAL/HIGH)\n1. `areal/v2/agent_service/**` -> Service Orchestration (HIGH)\n1. `areal/v2/inference_service/**` -> Service Orchestration (HIGH)\n1. `areal/experimental/engine/archon_weight_sync.py` -> Distributed Runtime (CRITICAL)\n1. `areal/infra/rpc/**` -> Launcher & Infrastructure + Distributed Runtime (HIGH)\n1. `areal/workflow/**`, `areal/trainer/**` -> Workflow & Trainer Contract (HIGH/MEDIUM)\n1. `areal/api/**` -> API & Config Compatibility (MEDIUM)\n1. `areal/utils/{saver.py,recover.py,async_checkpoint.py}` + `*checkpoint*.py` ->\n   Checkpoint & Recovery (CRITICAL/HIGH)\n1. `areal/experimental/models/archon/activation_checkpoint.py` -> Distributed Runtime\n   (MEDIUM/HIGH)\n1. `areal/experimental/models/archon/compile.py` -> Numerics & Tensor Semantics +\n   Distributed Runtime (MEDIUM)\n1. `pyproject.toml`, `uv.lock` -> API & Config Compatibility (MEDIUM)\n1. `Dockerfile`, `.dockerignore` -> Launcher & Infrastructure (HIGH)\n1. `.agents/**`, `.claude/**`, `.opencode/**`, `.codex/**`, `AGENTS.md`, `CLAUDE.md` ->\n   Harness & Agent Infrastructure (MEDIUM/HIGH)\n1. `.github/workflows/**` -> CI/CD & Release Automation (HIGH/CRITICAL)\n1. `docs/build_all.sh`, `docs/generate_cli_docs.py`, `.github/PULL_REQUEST_TEMPLATE.md`,\n   `README.md`, `CONTRIBUTING.md` -> Low-Risk Hygiene (LOW/MEDIUM)\n\n### 4.3 Must-not-regress coverage (from current review-pr)\n\nThe migration must preserve the existing high-risk framework coverage already present\ntoday:\n\n1. `areal/experimental/models/archon/**` + `areal/experimental/engine/archon_engine.py`\n   - `areal/experimental/engine/archon_checkpoint.py` -> Distributed Runtime + Model\n     Compute & Attention\n1. `areal/engine/fsdp_utils/**` + `areal/engine/fsdp_engine.py` -> Distributed Runtime\n1. `areal/engine/megatron_engine.py` + `areal/engine/megatron_utils/**` -> Distributed\n   Runtime\n1. `areal/trainer/**` -> Workflow & Trainer Contract\n1. `areal/reward/**` -> Numerics & Tensor Semantics + Workflow & Trainer Contract\n1. `areal/dataset/**` -> Workflow & Trainer Contract + API & Config Compatibility\n\n### 4.4 Noise control rules\n\n1. No repo-wide standalone triggering for `current_platform`, `RTensor`, or `fp8`.\n1. These keywords only refine severity/checklists after domain has been path-selected.\n1. Cap task fan-out: max one primary template per domain, plus one general logic pass.\n\n______________________________________________________________________\n\n## 5. Template Strategy\n\nMaintain **12 domain templates** (one per L1), each with signal-specific checklists,\nplus **1 universal logic pass**.\n\n### Template inventory (v1)\n\n1. Distributed Runtime Review\n1. Model Compute & Attention Review\n1. Inference Backend & Serving Review\n1. Service Orchestration Review\n1. Workflow & Trainer Contract Review\n1. API & Config Compatibility Review\n1. Numerics & Tensor Semantics Review\n1. Checkpoint & Recovery Review\n1. Launcher & Infrastructure Review\n1. Low-Risk Hygiene Review\n1. Harness & Agent Infrastructure Review\n1. CI/CD & Release Automation Review\n\n### Mandatory universal pass\n\n- Always run one lightweight **General Logic & Boundary** pass for non-doc PRs.\n\n______________________________________________________________________\n\n## 6. Severity Mapping\n\n1. **CRITICAL**: distributed invariants, checkpoint correctness, core weight sync\n   safety.\n1. **HIGH**: service orchestration, inference backend runtime, trainer/workflow\n   contracts.\n1. **MEDIUM**: API compatibility, numerics/tensor semantics in bounded scope.\n1. **LOW**: docs/tests/config-only hygiene.\n\nRule: domain default severity can be escalated by L2 signal combinations (e.g.,\n`mesh_dtensor` + `weight_sync`).\n\n______________________________________________________________________\n\n## 7. Cross-Domain Linkage Rules (Auto-appended checks)\n\n1. `tree_attn` -> also append Numerics & Tensor Semantics checks.\n1. `archon_core` or `archon_parallel` -> also append Model Compute & Attention checks.\n1. `archon_model_family` or `archon_moe_modeling` -> also append Numerics & Tensor\n   Semantics checks.\n1. `reward_surface` -> also append Workflow & Trainer Contract checks.\n1. `compile_dynamo` -> also append Distributed Runtime checks.\n1. `vllm_ext` -> also append Launcher & Infrastructure checks.\n1. Service Orchestration changes -> also append Workflow & Trainer async-contract\n   checks.\n1. `archon_weight_sync` -> also append DTensor + process-group + checkpoint interaction\n   checks.\n1. RPC transport changes -> also append Distributed Runtime synchronization checks.\n1. `mixed_precision_fp8` + Distributed Runtime -> also append mesh + weight-sync\n   compatibility checks.\n1. `runtime_image_config` -> also append Inference Backend & Serving checks.\n1. `project_dependency_config` -> also append API & Config Compatibility checks.\n1. `github_workflow_jobs` or `release_delivery` -> also append Launcher & Infrastructure\n   checks.\n1. `skill_definition` or `platform_command_data` -> also append Low-Risk Hygiene checks.\n\n______________________________________________________________________\n\n## 8. Three-Platform Synchronization Model\n\n## 8.1 Source-of-truth\n\nSemantic content is authored only in:\n\n1. `.agents/skills/review-pr/references/review-pr-domains-and-signals.md`\n1. `.agents/skills/review-pr/references/review-pr-templates.md`\n\nCanonical semantic scope includes:\n\n1. taxonomy (L1/L2), path/linkage rules, and severity rules\n1. checklist bodies and change-analysis vocabulary\n\nWrapper-specific scope (non-canonical) includes:\n\n1. command syntax and frontmatter fields\n1. shell snippets, import/include syntax, and runtime-specific routing policies\n1. OpenCode-only and Claude-only execution options\n\n## 8.2 Derived outputs\n\nGenerated/derived files:\n\n1. `.opencode/data/review-pr-domains-and-signals.md`\n1. `.opencode/data/review-pr-templates.md`\n1. `.claude/data/review-pr-domains-and-signals.md`\n1. `.claude/data/review-pr-templates.md`\n\n## 8.3 Mechanical sync (definition)\n\n\"Mechanical sync\" means deterministic conversion, not manual copy/paste:\n\n1. Read canonical `.agents` files.\n1. Emit OpenCode and Claude data copies with the same generic review-depth vocabulary\n   (`comprehensive`, `targeted`, `basic`).\n1. Keep all runtime routing and platform execution choices in the wrapper command files\n   only.\n1. Preserve section order and checklist content exactly.\n\n______________________________________________________________________\n\n## 9. Implementation Plan\n\n### Phase A (Foundation)\n\n1. Replace the old review-pr taxonomy with L1/L2 domain/signal references in canonical\n   `.agents` references.\n1. Rebuild template file into 12 domain templates + universal logic pass.\n1. Add linkage rules and fan-out caps.\n\n### Phase B (Platform sync)\n\n1. Add sync script at `.agents/skills/review-pr/sync_review_pr_refs.py`.\n1. Regenerate `.opencode/data/*` and `.claude/data/*`.\n1. Add CI check: fail if derived files differ from regeneration output.\n\n### Phase C (Command layer alignment)\n\n1. Update `.opencode/command/review-pr.md` wording to reflect new domains/signals.\n1. Update `.claude/commands/review-pr.md` wording similarly.\n1. Keep orchestration differences platform-specific (task categories vs model routing).\n\n### Phase D (Validation)\n\n1. Run **classification lane** with `/review-pr --quick` against representative PRs\n   from:\n   - `tree_attn`\n   - `vllm_ext`\n   - `agent_service/inference_service`\n   - `archon_weight_sync`\n1. Measure in classification lane:\n   - expected detected domains/signals\n   - expected severity\n   - false positive rate\n   - missing high-risk findings\n1. Run **delegation lane** with full `/review-pr` (non-quick) on the same fixtures.\n1. Measure in delegation lane:\n   - false positive rate\n   - missing high-risk findings\n   - number of spawned review tasks per PR\n\n______________________________________________________________________\n\n## 10. Plan Review (Critical self-review)\n\n### Strengths\n\n1. Improves coverage for newly introduced high-risk modules.\n1. Shrinks maintenance overhead by moving to domain templates.\n1. Prevents cross-platform drift via deterministic derivation.\n\n### Risks\n\n1. Over-broad domains can dilute checklist quality.\n1. First migration may temporarily shift severity distributions.\n1. Command docs can lag behind taxonomy if sync discipline is not enforced.\n\n### Mitigations\n\n1. Keep L2 signals explicit and path-anchored.\n1. Use golden PR cases for pre/post comparison.\n1. Add CI consistency gate for derived platform data files.\n1. Require wrapper wording updates in the same migration PR as taxonomy changes.\n\n______________________________________________________________________\n\n## 11. Acceptance Criteria\n\n1. Canonical references express all 12 domains + L2 signals and linkage rules.\n1. Derived OpenCode/Claude data files regenerate with zero manual edits.\n1. No mixed old/new label vocabulary remains after regeneration.\n1. Representative PRs trigger expected domains:\n   - `tree_attn` PR -> Model Compute & Attention (+ Numerics linkage)\n   - `vllm_ext` PR -> Inference Backend & Serving (+ Launcher linkage)\n   - service-stack PR -> Service Orchestration (+ Workflow linkage)\n   - `archon_weight_sync` PR -> Distributed Runtime (CRITICAL)\n1. Legacy high-risk coverage remains intact (Archon/FSDP/Megatron/Reward/Dataset).\n1. Task fan-out remains bounded (no uncontrolled template explosion).\n\n______________________________________________________________________\n\n## 12. Out of Scope for v1 (defer)\n\n1. Fully unifying command orchestration syntax across OpenCode and Claude.\n1. Creating standalone domain types for every backend keyword.\n1. Automatically posting review comments to GitHub.\n\n______________________________________________________________________\n\n## 13. Recommended Next Action\n\nLand **Phase A + B + C** in one migration PR (taxonomy + sync tooling + wrapper\nalignment), then run **Phase D** validation immediately using fixed fixtures.\n",".agents/skills/review-pr/SKILL.md":"---\nname: review-pr\ndescription: Read-only pull request review workflow with risk analysis, targeted checklists, and Codex subagent consultation.\n---\n\n# Review Pull Request\n\nUse this skill when the user asks for a PR review of the current branch or a specific\nPR.\n\n## Inputs\n\n- Optional PR number\n- Optional `--quick` to stop after the change analysis phase\n\n## Hard Rules\n\n- Stay read-only.\n- Do not edit files, commit, push, rebase, or change GitHub state.\n- Do not run build, install, or test commands that mutate the environment.\n- Use `gh` for PR metadata and git diff retrieval.\n\n## Reference Files\n\n- `references/review-pr-domains-and-signals.md`\n- `references/review-pr-templates.md`\n\n## Workflow\n\n### Phase 1: Resolve PR context\n\n1. Use `gh pr view` to fetch PR title, body, state, draft status, and changed files.\n1. If no PR exists, stop and report that clearly.\n1. If the PR is closed, stop.\n1. Record the branch name and changed file list.\n\n### Phase 2: Change analysis\n\n1. Classify changed files using `references/review-pr-domains-and-signals.md`.\n1. Determine the highest overall risk level: `CRITICAL`, `HIGH`, `MEDIUM`, or `LOW`.\n1. Build a `CHANGE_ANALYSIS_REPORT` that lists:\n   - detected domains/signals\n   - risk level\n   - affected files\n   - related frameworks\n   - likely failure modes\n\nIf `--quick` is set, return the change analysis report and stop here.\n\n### Phase 3: Review planning\n\n1. Select the smallest useful set of review passes from\n   `references/review-pr-templates.md`.\n1. Split by risk area, not by file count.\n1. Always include at least one general logic pass.\n\n### Phase 4: Expert consultation\n\nConsult the matching Codex subagents registered in `.codex/config.toml` when relevant:\n\n- `archon-expert`\n- `fsdp-expert`\n- `megatron-expert`\n- `algorithm-expert`\n- `launcher-expert`\n\nIf the Codex runtime supports parallel subagent execution, run independent review passes\nin parallel. Otherwise, execute them serially.\n\n### Phase 5: Final review\n\nProduce findings first, ordered by severity:\n\n1. `CRITICAL`\n1. `HIGH`\n1. `MEDIUM`\n1. `LOW`\n\nFor every finding, include:\n\n- file path\n- line number when available\n- why it is a bug, regression, or risk\n- concrete fix direction\n\n## What to Ignore\n\n- Pure style nits with no correctness impact\n- Issues outside the changed scope unless the PR makes them worse\n- Failures that standard linters or formatters would already catch\n- Speculative concerns with no concrete trigger in the diff\n\n## Output Shape\n\nUse this structure:\n\n```markdown\nCHANGE_ANALYSIS_REPORT:\n- detected_domains: [...]\n- detected_signals: [...]\n- risk_level: ...\n- affected_files: [...]\n- related_frameworks: [...]\n- identified_risks: [...]\n\nFindings\n1. [severity] Title — path:line\n   - Problem: ...\n   - Fix: ...\n\nOpen Questions\n- ...\n\nResidual Risk\n- ...\n```\n",".agents/skills/review-pr/references/review-pr-domains-and-signals.md":"# PR Review: Domain & Signal Detection Reference\n\nThis file contains the canonical change-domain and signal detection tables for PR\nreview. Referenced by: `.agents/skills/review-pr/SKILL.md`\n\n______________________________________________________________________\n\n## Severity-to-Review-Depth Mapping\n\n- **CRITICAL**: use `comprehensive` review depth\n- **HIGH**: use `comprehensive` review depth\n- **MEDIUM**: use `targeted` review depth\n- **LOW**: use `basic` review depth\n\n______________________________________________________________________\n\n## L1 Domains and L2 Signals\n\n## Domain 1: Distributed Runtime (CRITICAL/HIGH)\n\n| L2 Signal                  | File Path Pattern                                                                              | Code Pattern                                                                                                                                                              |\n| -------------------------- | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `archon_core`              | `areal/experimental/engine/archon_engine.py`, `areal/experimental/engine/archon_checkpoint.py` | `ArchonEngine`, `ArchonCheckpointManager`, `archon`                                                                                                                       |\n| `archon_parallel`          | `areal/experimental/models/archon/`, `parallel_dims.py`, `parallelize.py`                      | `ArchonParallelDims`, `_build_mesh`, `apply_moe_ep_tp`, `apply_tp`, `apply_cp`, `ExpertTensorParallel`, `etp`, `parallelize_module`, `ColwiseParallel`, `RowwiseParallel` |\n| `process_group`            | `areal/engine/fsdp_utils/`, `areal/engine/megatron_utils/`, `areal/experimental/engine/`       | `new_group`, `ProcessGroup`, `dist.get_rank(`                                                                                                                             |\n| `fsdp_core`                | `areal/engine/fsdp_engine.py`, `areal/engine/fsdp_utils/`                                      | `FSDP`, `fully_shard`, `FullyShardedDataParallel`                                                                                                                         |\n| `megatron_core`            | `areal/engine/megatron_engine.py`, `areal/engine/megatron_utils/`                              | `MegatronEngine`, `pipeline`, `micro-batch`                                                                                                                               |\n| `collectives`              | `areal/engine/`, `areal/infra/rpc/`                                                            | `all_reduce`, `all_gather`, `reduce_scatter`, `all_to_all`, `broadcast`, `barrier`                                                                                        |\n| `mesh_dtensor`             | `areal/experimental/models/archon/`, `areal/engine/fsdp_utils/`                                | `DeviceMesh`, `DTensor`, `Shard(`, `Replicate(`, `distribute_tensor`                                                                                                      |\n| `activation_checkpointing` | `areal/experimental/models/archon/activation_checkpoint.py`, `areal/models/`, `areal/engine/`  | `activation_checkpoint`, `checkpoint_wrapper`, `selective_checkpoint`                                                                                                     |\n| `weight_sync`              | `areal/experimental/engine/archon_weight_sync.py`, `areal/api/engine_api.py`, `areal/engine/`  | `WeightUpdateMeta`, `set_version`, `update_weights`                                                                                                                       |\n\n## Domain 2: Model Compute & Attention (HIGH/MEDIUM)\n\n| L2 Signal                | File Path Pattern                                                                                                                                          | Code Pattern                                                                                           |\n| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |\n| `tree_attn`              | `areal/models/tree_attn/`                                                                                                                                  | `TreeAttention`, `tree_attn`, `TreeNode`, `tree`                                                       |\n| `sdpa_varlen`            | `attention/sdpa.py`, `attention/varlen.py`, `areal/models/tree_attn/`                                                                                      | `sdpa`, `flash_attn`, `varlen`, `causal_mask`                                                          |\n| `sp_cp_attention_mask`   | `areal/models/tree_attn/`, `areal/experimental/models/archon/attention/`                                                                                   | `SequenceParallel`, `context_parallel`, `mask`                                                         |\n| `triton_kernel`          | `areal/models/tree_attn/triton_kernel.py`                                                                                                                  | `triton`, `kernel`, `autotune`                                                                         |\n| `archon_model_family`    | `areal/experimental/models/archon/model_spec.py`, `areal/experimental/models/archon/qwen*/`                                                                | `ModelSpec`, `register_model_spec`, `supported_model_types`, `state_dict_adapter`, `rope`              |\n| `archon_attention_stack` | `areal/experimental/models/archon/attention/`, `areal/experimental/models/archon/ulysses.py`                                                               | `ulysses_slice_inputs`, `ulysses_gather_output`, `gather_seq_scatter_heads`, `sdpa`, `varlen`          |\n| `archon_moe_modeling`    | `areal/experimental/models/archon/moe/`, `areal/experimental/models/archon/expert_parallel.py`, `areal/experimental/models/archon/moe_weight_converter.py` | `TokenChoiceTopKRouter`, `RouterGateLinear`, `GroupedExperts`, `MoEWeightConverter`, `expert_parallel` |\n\n## Domain 3: Inference Backend & Serving (HIGH)\n\n| L2 Signal                  | File Path Pattern                                              | Code Pattern                                                     |\n| -------------------------- | -------------------------------------------------------------- | ---------------------------------------------------------------- |\n| `vllm_ext`                 | `areal/engine/vllm_ext/`                                       | `areal_vllm_server`, `vllm_worker_extension`, `pause_generation` |\n| `remote_inference_backend` | `areal/engine/vllm_remote.py`, `areal/engine/sglang_remote.py` | `vllm`, `sglang`, `OpenAI`, `request`, `response`                |\n| `request_lifecycle`        | `areal/engine/`, `areal/infra/launcher/`                       | `enqueue`, `dequeue`, `cancel`, `timeout`                        |\n\n## Domain 4: Service Orchestration (HIGH)\n\n| L2 Signal                  | File Path Pattern                                                                                                                                       | Code Pattern                                                     |\n| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |\n| `service_routing_dataflow` | `areal/v2/agent_service/gateway/`, `areal/v2/agent_service/router/`, `areal/v2/inference_service/data_proxy/`, `areal/v2/inference_service/controller/` | `route`, `gateway`, `router`, `DataProxy`, `controller`, `batch` |\n| `session_consistency`      | `areal/v2/agent_service/`, `areal/v2/inference_service/`                                                                                                | `session`, `affinity`, `history`, `state`                        |\n\n## Domain 5: Workflow & Trainer Contract (HIGH/MEDIUM)\n\n| L2 Signal                  | File Path Pattern                                                           | Code Pattern                                        |\n| -------------------------- | --------------------------------------------------------------------------- | --------------------------------------------------- |\n| `workflow_engine_boundary` | `areal/workflow/`, `areal/trainer/`, `areal/engine/`                        | `RolloutWorkflow`, `arun_episode`, `agenerate`      |\n| `dataset_surface`          | `areal/dataset/`                                                            | `DataLoader`, `IterableDataset`, `get_*_dataset`    |\n| `async_contract`           | `areal/workflow/`, `areal/v2/agent_service/`, `areal/v2/inference_service/` | `async def`, `await`, `aiofiles`, `asyncio`         |\n| `weight_version_contract`  | `areal/api/engine_api.py`, `areal/workflow/`, `areal/trainer/`              | `WeightUpdateMeta`, `set_version`, `weight version` |\n\n## Domain 6: API & Config Compatibility (MEDIUM)\n\n| L2 Signal                   | File Path Pattern                     | Code Pattern                                                                     |\n| --------------------------- | ------------------------------------- | -------------------------------------------------------------------------------- |\n| `dataclass_schema`          | `areal/api/`                          | `@dataclass`, `field(`, `__post_init__`                                          |\n| `cli_compat`                | `areal/api/cli_args.py`               | `Literal`, `help`, `default`                                                     |\n| `backward_compat`           | `areal/api/`, `areal/infra/launcher/` | `deprecated`, `compat`, `version`                                                |\n| `project_dependency_config` | `pyproject.toml`, `uv.lock`           | `requires-python`, `dependencies`, `optional-dependencies`, `build-system`, `uv` |\n\n## Domain 7: Numerics & Tensor Semantics (MEDIUM)\n\n| L2 Signal             | File Path Pattern                                                               | Code Pattern                                            |\n| --------------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------- |\n| `shape_dtype`         | `areal/engine/`, `areal/models/`, `areal/trainer/`                              | `.view(`, `.reshape(`, `dtype=`, `.contiguous(`         |\n| `numerical_stability` | `areal/engine/`, `areal/reward/`, `areal/utils/functional/`                     | `log(`, `softmax`, `eps=`, `.clamp(`, `nan`, `inf`      |\n| `reward_surface`      | `areal/reward/`                                                                 | `reward_fn`, `AsyncRewardWrapper`, `MathVerifyWorker`   |\n| `compile_dynamo`      | `areal/experimental/models/archon/compile.py`, `areal/models/`, `areal/engine/` | `torch.compile`, `_dynamo`, `mark_dynamic`, `fullgraph` |\n| `mixed_precision_fp8` | `areal/engine/megatron_utils/fp8/`, `areal/experimental/models/archon/`         | `fp8`, `bf16`, `fp16`, `mixed precision`                |\n\n## Domain 8: Checkpoint & Recovery (CRITICAL/HIGH)\n\n| L2 Signal         | File Path Pattern                                                   | Code Pattern                                    |\n| ----------------- | ------------------------------------------------------------------- | ----------------------------------------------- |\n| `dcp_consistency` | `areal/utils/async_checkpoint.py`, `areal/engine/**/checkpoint*.py` | `dcp.save`, `dcp.load`, `DistributedCheckpoint` |\n| `optimizer_state` | `areal/engine/fsdp_utils/checkpoint.py`, `areal/utils/saver.py`     | `optimizer state`, `state_dict`                 |\n| `resume_compat`   | `areal/utils/recover.py`, `areal/utils/saver.py`                    | `resume`, `load_state_dict`, `migration`        |\n\n## Domain 9: Launcher & Infrastructure (HIGH/MEDIUM)\n\n| L2 Signal                 | File Path Pattern                                            | Code Pattern                                              |\n| ------------------------- | ------------------------------------------------------------ | --------------------------------------------------------- |\n| `launcher_resource_match` | `areal/infra/launcher/`                                      | `LaunchConfig`, `RayLauncher`, `SlurmLauncher`            |\n| `scheduler_contract`      | `areal/infra/scheduler/`, `areal/scheduler/`                 | `Scheduler`, `placement`, `resource`                      |\n| `rpc_transport`           | `areal/infra/rpc/`, `areal/v2/inference_service/data_proxy/` | `RTensor`, `serialize`, `rpc`, `fetch`                    |\n| `runtime_image_config`    | `Dockerfile`, `.dockerignore`                                | `FROM`, `ARG`, `RUN`, `ENV`, `COPY`, `uv sync`, `VARIANT` |\n\n## Domain 10: Low-Risk Hygiene (LOW)\n\n| L2 Signal                 | File Path Pattern                                                                                                                                                       | Code Pattern                                                                                                 |\n| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |\n| `tests_docs_config`       | `tests/`, `docs/`, `*.md`, `*.yaml`, `*.json`, `*.toml`                                                                                                                 | -                                                                                                            |\n| `logging_import_security` | `areal/`, `examples/`                                                                                                                                                   | `getLogger`, `print(`, `import *`, `api_key`, `token`, `password`                                            |\n| `project_docs_metadata`   | `docs/build_all.sh`, `docs/generate_cli_docs.py`, `docs/en/`, `docs/zh/`, `README.md`, `CONTRIBUTING.md`, `.github/PULL_REQUEST_TEMPLATE.md`, `.github/ISSUE_TEMPLATE/` | `jupyter-book`, `generate_cli_docs`, `build_all`, `_build`, `checklist`, `template`, `contributing`, `usage` |\n\n## Domain 11: Harness & Agent Infrastructure (MEDIUM/HIGH)\n\n| L2 Signal               | File Path Pattern                                                             | Code Pattern                                                 |\n| ----------------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------ |\n| `skill_definition`      | `.agents/skills/**/SKILL.md`, `.agents/skills/**/references/`                 | `description:`, `## Workflow`, `## Reference Files`, `skill` |\n| `platform_command_data` | `.claude/commands/`, `.claude/data/`, `.opencode/command/`, `.opencode/data/` | `@.`, `/review-pr`, `/create-pr`, `data/`, `task(`           |\n| `agent_registry_config` | `.codex/config.toml`, `.codex/agents/`, `AGENTS.md`, `CLAUDE.md`              | `agents`, `skills`, `registry`, `subagent`, `config.toml`    |\n\n## Domain 12: CI/CD & Release Automation (HIGH/CRITICAL)\n\n| L2 Signal              | File Path Pattern                                                                                                          | Code Pattern                                              |\n| ---------------------- | -------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- |\n| `github_workflow_jobs` | `.github/workflows/*.yml`                                                                                                  | `jobs:`, `runs-on:`, `needs:`, `if:`, `workflow_dispatch` |\n| `runner_provisioning`  | `.github/workflows/bake-gcp-image.yml`, `.github/workflows/runner-heartbeat.yml`                                           | `gcp`, `runner`, `image`, `heartbeat`                     |\n| `release_delivery`     | `.github/workflows/build-docker-image.yml`, `.github/workflows/tag-release-image.yml`, `.github/workflows/deploy-docs.yml` | `docker`, `tag`, `release`, `pages`, `publish`            |\n\n______________________________________________________________________\n\n## Must-Not-Regress Core Coverage\n\nThe refactor must preserve these existing review surfaces:\n\n- Archon core: `areal/experimental/models/archon/`,\n  `areal/experimental/engine/archon_engine.py`\n- FSDP core: `areal/engine/fsdp_utils/`, `areal/engine/fsdp_engine.py`\n- Megatron core: `areal/engine/megatron_engine.py`, `areal/engine/megatron_utils/`\n- Reward: `areal/reward/`\n- Dataset: `areal/dataset/`\n- Trainer: `areal/trainer/`\n- Harness: `.agents/`, `.claude/`, `.opencode/`, `.codex/`\n- CI/CD and release: `.github/workflows/`, `Dockerfile`, `pyproject.toml`\n\n______________________________________________________________________\n\n## Cross-Domain Linkage Rules\n\n| Detected Signal                                | Auto-Linked Review                                  |\n| ---------------------------------------------- | --------------------------------------------------- |\n| `archon_core` or `archon_parallel`             | Model Compute & Attention checks                    |\n| `archon_model_family` or `archon_moe_modeling` | Numerics & Tensor Semantics checks                  |\n| `tree_attn`                                    | Numerics & Tensor Semantics checks                  |\n| `reward_surface`                               | Workflow & Trainer Contract checks                  |\n| `compile_dynamo`                               | Distributed Runtime checks                          |\n| `vllm_ext`                                     | Launcher & Infrastructure checks                    |\n| `service_routing_dataflow`                     | Workflow & Trainer async-contract checks            |\n| `weight_sync`                                  | DTensor/process-group/checkpoint interaction checks |\n| `rpc_transport`                                | Distributed Runtime synchronization checks          |\n| `mixed_precision_fp8` + Distributed Runtime    | mesh + weight-sync compatibility checks             |\n| `runtime_image_config`                         | Inference Backend & Serving checks                  |\n| `project_dependency_config`                    | API & Config Compatibility checks                   |\n| `github_workflow_jobs` or `release_delivery`   | Launcher & Infrastructure checks                    |\n| `skill_definition` or `platform_command_data`  | Low-Risk Hygiene checks                             |\n\n______________________________________________________________________\n\n## Risk Identification Guidance\n\n### Distributed Runtime Risks\n\n- Archon mesh construction or parallel-dims mismatch\n- EP/TP/CP application order errors in Archon parallelization\n- Activation checkpoint placement violating TP/CP/FSDP ordering assumptions\n- Archon engine lifecycle drift around distributed setup and checkpoint boundaries\n- Collective call order mismatch across ranks\n- Wrong process-group scope in rank-sensitive logic\n- Mesh dimension mismatch and invalid DTensor placement\n- Weight version drift between rollout and training workers\n\n### Model Compute & Attention Risks\n\n- Attention mask inconsistency under TP/SP/CP paths\n- Tree attention index/routing mismatch\n- Archon model-family registration or per-family wiring drift\n- Archon MoE router/expert behavior diverging from weight-conversion expectations\n- Archon Ulysses slicing/gather semantics mismatching attention layout assumptions\n- Kernel assumptions violating dtype/shape invariants\n- Sequence packing alignment errors\n\n### Service Orchestration Risks\n\n- Session affinity or history drift across gateway/router/data proxy\n- Async message handling holes and dropped tasks\n- Controller/worker lifecycle desynchronization\n\n### Inference Backend & Serving Risks\n\n- Request lifecycle inconsistencies (enqueue/cancel/timeout)\n- Worker state transitions leaving requests stranded\n- Backend extension hooks drifting from runtime expectations\n\n### Workflow & Trainer Contract Risks\n\n- Workflow-engine contract drift across async boundaries\n- Weight version handshake mismatch between rollout and train\n- Trainer lifecycle transition inconsistencies\n\n### API & Config Compatibility Risks\n\n- Breaking config/schema changes without migration path\n- Dataclass or CLI default changes altering behavior silently\n- Missing validation for newly introduced fields\n- Dependency or build-system pin changes breaking supported environments\n\n### Numerics & Tensor Semantics Risks\n\n- Silent shape/dtype mismatch under distributed paths\n- Unstable numerical operations in loss/reward logic\n- torch.compile or dynamo guard changes breaking graph assumptions\n- Mixed-precision interaction regressions\n\n### Checkpoint & Recovery Risks\n\n- Partial-rank checkpoint participation\n- Incompatible state key evolution\n- Resume path breaking optimizer/model synchronization\n\n### Launcher & Infrastructure Risks\n\n- Resource assignment mismatching parallel strategy assumptions\n- RPC transport metadata loss (shape/dtype/device)\n- Startup/shutdown ordering races across processes\n- Runtime image or build-arg drift from supported inference/training variants\n\n### Low-Risk Hygiene Risks\n\n- Docs/config drift from actual runtime behavior\n- Logging or import hygiene regressions\n- Sensitive data exposure in logs or config\n- Documentation/build scripts or project templates drifting from actual workflow\n\n### Harness & Agent Infrastructure Risks\n\n- Skill and command docs drifting from actual platform behavior\n- Cross-platform data files falling out of sync with canonical references\n- Agent registry/config changes breaking expert routing or command discovery\n\n### CI/CD & Release Automation Risks\n\n- Workflow trigger or job dependency changes skipping required validation\n- Runner provisioning drift causing flaky or non-reproducible CI\n- Release or docs deployment jobs publishing the wrong artifacts\n",".agents/skills/review-pr/references/review-pr-templates.md":"# PR Review: Domain Templates Reference\n\nThis file contains canonical domain templates for PR review. Referenced by:\n`.agents/skills/review-pr/SKILL.md`\n\n______________________________________________________________________\n\n## Template Selection Rules\n\n1. Select templates by detected L1 domains and L2 signals.\n1. Use at most one primary template per domain.\n1. Always include **General Logic & Boundary** for non-doc/config-only PRs.\n1. Apply cross-domain linkage checks from `review-pr-domains-and-signals.md`.\n\n______________________________________________________________________\n\n## Universal Template\n\n### General Logic & Boundary\n\n```\nApplicable: Any non-doc/config-only change\nChecklist:\n- Boundary condition correctness (empty inputs, singleton, max-size)\n- Conditional logic correctness (branch inversion, short-circuit mistakes)\n- Error-path behavior (exceptions propagated with actionable context)\n- Return-value consistency across code paths\n- No newly introduced hidden behavior changes\n```\n\n______________________________________________________________________\n\n## Domain 1 Template: Distributed Runtime Review \\[comprehensive\\]\n\n```\nApplicable signals: archon_core, archon_parallel, process_group, fsdp_core, megatron_core, collectives, mesh_dtensor, activation_checkpointing, weight_sync\nChecklist:\n- Archon engine and checkpoint lifecycle remain aligned with distributed runtime assumptions\n- FSDP and Megatron engine invariants still match process-group, sharding, and pipeline assumptions\n- Archon parallel-dims and mesh construction still match downstream placement logic\n- Process-group creation/usage/cleanup is rank-consistent\n- Collective operations are called by all required ranks in consistent order\n- DeviceMesh dimensions and DTensor placements are correct for each path\n- Activation checkpoint placement remains compatible with parallel and sharding order requirements\n- Local/global tensor conversion boundaries are explicit and correct\n- Weight version propagation and update ordering are deterministic\n- No debug-only barriers left in hot path\n```\n\n## Domain 2 Template: Model Compute & Attention Review \\[comprehensive\\]\n\n```\nApplicable signals: tree_attn, sdpa_varlen, sp_cp_attention_mask, triton_kernel, archon_model_family, archon_attention_stack, archon_moe_modeling\nChecklist:\n- Attention mask semantics preserved under TP/SP/CP\n- Archon model-family registration and per-family wiring remain internally consistent\n- Archon attention/Ulysses slicing and gather paths preserve layout assumptions\n- Archon MoE router, grouped experts, and weight-conversion interfaces remain aligned\n- Tree attention index/order invariants are maintained\n- Kernel assumptions on dtype/shape/contiguity are satisfied\n- No silent behavior change in sequence packing/unpacking\n- Tensor layouts remain compatible with downstream modules\n```\n\n## Domain 3 Template: Inference Backend & Serving Review \\[comprehensive\\]\n\n```\nApplicable signals: vllm_ext, remote_inference_backend, request_lifecycle\nChecklist:\n- Request lifecycle (enqueue, execution, cancellation, timeout) is coherent\n- Worker state transitions are safe under concurrency\n- Backend-specific extension points stay API-compatible\n- Error handling does not strand in-flight requests\n- Versioning/weight-update interactions are explicit and safe\n```\n\n## Domain 4 Template: Service Orchestration Review \\[comprehensive\\]\n\n```\nApplicable signals: service_routing_dataflow, session_consistency\nChecklist:\n- Gateway/router/data-proxy routing rules are deterministic\n- Session affinity and history consistency are preserved\n- Controller/worker coordination has no lost-update window\n- Async boundaries avoid blocking operations in critical paths\n- Failure/retry behavior does not duplicate or drop work\n```\n\n## Domain 5 Template: Workflow & Trainer Contract Review \\[comprehensive\\]\n\n```\nApplicable signals: workflow_engine_boundary, dataset_surface, async_contract, weight_version_contract\nChecklist:\n- RolloutWorkflow and Engine interfaces remain contract-compatible\n- Dataset/output structure still matches workflow and trainer consumption expectations\n- Async flow uses await consistently and avoids sync I/O in async paths\n- Weight update/version handshake is preserved end-to-end\n- Trainer lifecycle transitions are valid for all execution branches\n- Call ordering assumptions across trainer/workflow/engine are unchanged or justified\n```\n\n## Domain 6 Template: API & Config Compatibility Review \\[targeted\\]\n\n```\nApplicable signals: dataclass_schema, cli_compat, backward_compat, project_dependency_config\nChecklist:\n- Public API signature and default value changes are intentional and compatible\n- Dataclass validation remains complete and informative\n- CLI options preserve expected compatibility semantics\n- New fields include safe defaults or explicit migration handling\n- Breaking changes are documented and scoped\n- Dependency and build-system changes remain compatible with supported environments\n```\n\n## Domain 7 Template: Numerics & Tensor Semantics Review \\[targeted\\]\n\n```\nApplicable signals: shape_dtype, numerical_stability, reward_surface, compile_dynamo, mixed_precision_fp8\nChecklist:\n- Tensor shape/dtype transitions are explicit and internally consistent\n- Numerical stability is protected (log/division/softmax/clamp paths)\n- Reward-side numerical behavior remains consistent with workflow consumption expectations\n- torch.compile / dynamo assumptions still hold for dynamic shapes and distributed execution\n- Mixed-precision behavior is correct for forward + backward + reduce paths\n- In-place and view/reshape operations do not corrupt gradient flow\n- Device placement and dtype combinations remain legal across code paths\n```\n\n## Domain 8 Template: Checkpoint & Recovery Review \\[comprehensive\\]\n\n```\nApplicable signals: dcp_consistency, optimizer_state, resume_compat\nChecklist:\n- Save/load requires and enforces all-rank participation where needed\n- State dict naming/structure is stable or migration-safe\n- Optimizer state sharding/gather behavior is consistent\n- Resume path restores model + optimizer + version state coherently\n- Async checkpoint behavior preserves ordering and durability assumptions\n```\n\n## Domain 9 Template: Launcher & Infrastructure Review \\[targeted\\]\n\n```\nApplicable signals: launcher_resource_match, scheduler_contract, rpc_transport, runtime_image_config\nChecklist:\n- Resource assignment matches declared parallel strategy assumptions\n- Scheduler decisions preserve required placement/affinity constraints\n- RPC serialization/deserialization keeps shape/dtype/device semantics\n- Transport retries/timeouts do not violate idempotency expectations\n- Cross-process startup/shutdown ordering is robust\n- Runtime image and build configuration remain aligned with supported variants\n```\n\n## Domain 10 Template: Low-Risk Hygiene Review \\[basic\\]\n\n```\nApplicable signals: tests_docs_config, logging_import_security, project_docs_metadata\nChecklist:\n- Tests/docs/config edits are internally consistent and non-misleading\n- Logging follows project conventions and avoids sensitive leakage\n- No wildcard imports or obvious dependency hygiene regressions\n- No accidental secrets/keys/tokens introduced\n- Docs build scripts and project templates stay aligned with real contributor workflow\n```\n\n## Domain 11 Template: Harness & Agent Infrastructure Review \\[targeted\\]\n\n```\nApplicable signals: skill_definition, platform_command_data, agent_registry_config\nChecklist:\n- Canonical skills and derived platform data remain structurally aligned\n- Command docs still point to the correct data files and execution model\n- Agent registry/config changes preserve command discovery and expert routing\n- Cross-platform mirrors are regenerated after canonical changes\n```\n\n## Domain 12 Template: CI/CD & Release Automation Review \\[comprehensive\\]\n\n```\nApplicable signals: github_workflow_jobs, runner_provisioning, release_delivery\nChecklist:\n- Workflow triggers, job dependencies, and permissions still enforce required validation\n- Runner/image provisioning remains reproducible and compatible with job expectations\n- Release, docker, and docs deployment jobs publish the intended artifacts only\n- CI changes do not silently skip tests, formatting, or release gates\n```\n\n______________________________________________________________________\n\n## Signal-Specific Add-On Checklists\n\nUse these only when corresponding L2 signals are detected.\n\n### `tree_attn` Add-On \\[comprehensive\\]\n\n```\n- Node/edge indexing is deterministic and shape-safe\n- Tree traversal order matches attention mask semantics\n- FSDP/Megatron/Archon variant modules remain behaviorally aligned\n```\n\n### `vllm_ext` Add-On \\[comprehensive\\]\n\n```\n- Server and worker extension hooks still match upstream expectations\n- Request pause/resume/cancel semantics remain coherent\n- Integration-specific monkey-patches are scoped and guarded\n```\n\n### `archon_model_family` Add-On \\[comprehensive\\]\n\n```\n- ModelSpec registration stays unique and complete for supported model types\n- Per-family model/args/spec/state-adapter wiring remains consistent\n- Pipelining hooks and model-part boundaries stay compatible with runtime assumptions\n```\n\n### `archon_moe_modeling` Add-On \\[comprehensive\\]\n\n```\n- Router top-k, gating dtype, and expert grouping semantics remain coherent\n- GroupedExperts layout and token reordering assumptions still match expert execution\n- MoE weight conversion paths stay consistent with runtime sharding expectations\n```\n\n### `service_routing_dataflow` Add-On \\[comprehensive\\]\n\n```\n- Route selection and fallback ordering are deterministic\n- Data proxy transformations preserve payload integrity\n- Session-key partitioning logic is collision-safe\n```\n\n### `remote_inference_backend` Add-On \\[targeted\\]\n\n```\n- Remote backend request/response semantics remain consistent across supported engines\n- Backend-specific transport options do not change lifecycle expectations silently\n- Shared request payload assumptions remain compatible across remote backends\n```\n\n### `weight_sync` Add-On \\[comprehensive\\]\n\n```\n- Versioned updates are monotonic and race-safe\n- Broadcast/all-gather points are aligned with consumer expectations\n- Local caching behavior cannot serve stale weights indefinitely\n```\n\n### `activation_checkpointing` Add-On \\[targeted\\]\n\n```\n- Checkpoint wrappers are applied in a parallelism-safe order\n- Selective checkpoint policies still cover the intended modules only\n- Activation recompute paths do not break sharding or sequence-parallel assumptions\n```\n\n### `reward_surface` Add-On \\[targeted\\]\n\n```\n- AsyncRewardWrapper-facing reward interfaces remain contract-compatible\n- Reward outputs keep expected shape, dtype, and per-sample semantics\n- Workflow assumptions about reward timing and batching remain valid\n```\n\n### `compile_dynamo` Add-On \\[targeted\\]\n\n```\n- torch.compile and dynamo guards still tolerate expected dynamic-shape inputs\n- fullgraph and mark_dynamic choices remain compatible with distributed execution paths\n- Compile-specific changes do not silently alter runtime fallback behavior\n```\n\n### `rpc_transport` Add-On \\[targeted\\]\n\n```\n- RTensor conversion is reversible and metadata-complete\n- Batch fetch/request framing preserves ordering and boundaries\n- Retry logic does not replay non-idempotent actions incorrectly\n```\n\n### `runtime_image_config` Add-On \\[targeted\\]\n\n```\n- Docker base image and build args still match supported backend variants\n- Layer ordering preserves expected cache and dependency behavior\n- Image contents remain aligned with runtime assumptions documented in the repo\n```\n\n### `project_dependency_config` Add-On \\[targeted\\]\n\n```\n- Python/version constraints and extras remain internally consistent\n- Lockfile changes match the intended dependency update scope\n- Build backend/tooling changes do not break install or publish workflows\n```\n\n### `github_workflow_jobs` Add-On \\[comprehensive\\]\n\n```\n- Workflow triggers and job graph still run required validation paths\n- Required secrets/permissions are scoped correctly\n- Matrix or conditional changes do not silently skip critical jobs\n```\n\n### `project_docs_metadata` Add-On \\[basic\\]\n\n```\n- Docs build entrypoints and contributor-facing metadata remain mutually consistent\n- Public templates and contributor instructions still match the actual workflow\n- Build/preview guidance still points to the supported commands\n```\n\n### `skill_definition` / `platform_command_data` Add-On \\[targeted\\]\n\n```\n- Canonical and derived review-pr data files stay in sync after edits\n- Command/import paths remain correct after file moves or renames\n- Wrapper-specific routing stays out of canonical reference files\n```\n",".agents/skills/translate-doc-zh/SKILL.md":"---\nname: translate-doc-zh\ndescription: Translate an English document under `docs/en/` into the matching Chinese document under `docs/zh/`.\n---\n\n# Translate Docs EN to ZH\n\nUse this skill when the user asks to translate a document from `docs/en/` to `docs/zh/`.\n\n## Input\n\n- A markdown file path under `docs/en/`\n\n## Workflow\n\n### Step 1: Validate the source path\n\n1. Confirm the file exists.\n1. Confirm it is under `docs/en/`.\n1. Confirm it ends with `.md`.\n1. If any check fails, stop and ask the user for a valid `docs/en/...md` path.\n\n### Step 2: Resolve the target path\n\n- Source: `docs/en/<path>.md`\n- Target: `docs/zh/<path>.md`\n\n### Step 3: Choose translation mode\n\n- If the Chinese file already exists, update only the changed parts while preserving the\n  rest.\n- If the Chinese file does not exist, translate the full document.\n\n## Translation Rules\n\n- Preserve technical terms such as FSDP, FSDP2, GRPO, PPO, DAPO, MoE, LLM, RL, RLVR,\n  Codex, Claude Code, OpenCode, Megatron, Archon, SGLang, vLLM, PyTorch, HuggingFace,\n  and Transformers.\n- Do not translate file paths, code blocks, CLI flags, or literal configuration keys.\n- Preserve Markdown structure, tables, and fenced code blocks.\n- Use concise, professional Chinese terminology.\n\n## Error Handling\n\n- If the target directory does not exist, create it before writing the translated file.\n- If the source document is partially translated already, preserve sections that do not\n  need updates.\n",".agents/skills/upgrade-deps/CHECKLIST_MAINTENANCE.md":"# Checklist Maintenance Guide\n\nThis document is the single reference for maintaining the per-package API checklists\nunder `checklists/`. It covers two complementary maintenance activities:\n\n- **Structural validation** — ensuring every AReaL import site is documented in the\n  checklist. Performed in SKILL.md → **Step 0.5** before any dependency changes.\n- **Content update** — ensuring documented API signatures and code snippets match the\n  post-upgrade state. Performed in SKILL.md → **Step 6e** after code changes are\n  applied.\n\nBoth activities share the same checklist format. This guide also covers creating new\nchecklists and writing good API catalog entries.\n\n______________________________________________________________________\n\n## 1. Checklist Structure Reference\n\nEach checklist file lives at `checklists/<package-name>.md` and follows the format\ndefined in `checklists/_TEMPLATE.md`. The key sections are:\n\n| Section                  | Purpose                                                                                                                                                                                                            |\n| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| **YAML frontmatter**     | `package`, `github` (org/repo), `branch_template`, `upstream_paths` — metadata used by the API audit (Step 6a) to clone the correct upstream tag and locate source files.                                          |\n| **Affected Files**       | Three-tier table (Primary / Secondary / Tertiary) listing every AReaL file that imports or uses the package, with a summary of what it imports.                                                                    |\n| **API Usage Catalog**    | Numbered entries documenting each distinct API surface (function, class, HTTP endpoint, schema contract). Each entry includes the upstream source path, the AReaL call-site code, and specific Check instructions. |\n| **Version-Guarded Code** | List of AReaL code locations that have version-specific conditionals for this package (e.g., `if version < \"0.4.10\": ...`).                                                                                        |\n\n______________________________________________________________________\n\n## 2. Import Patterns by Package\n\nUse these patterns to discover all AReaL usages of each focused package. Grep across\n`areal/`, `tests/`, and `examples/`.\n\n| Package           | Import path(s)    | Grep patterns                                    | Notes                                                                                 |\n| ----------------- | ----------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------- |\n| `megatron-core`   | `megatron.core`   | `from megatron.core`, `import megatron.core`     |                                                                                       |\n| `megatron-bridge` | `megatron.bridge` | `from megatron.bridge`, `import megatron.bridge` |                                                                                       |\n| `mbridge`         | `mbridge`         | `from mbridge`, `import mbridge`                 | Pinned to git commit; also check `mbridge.core.*` submodule imports                   |\n| `transformers`    | `transformers`    | `from transformers`, `import transformers`       | Exclude `flash_attn.bert_padding` false positives                                     |\n| `sglang`          | `sglang`          | `from sglang`, `import sglang`                   | Also scan `sglang_remote.py` for HTTP endpoint paths and JSON field names             |\n| `vllm`            | `vllm`            | `from vllm`, `import vllm`                       | Also scan `vllm_remote.py` and `vllm_ext/` for HTTP endpoints and JSON fields         |\n| `peft`            | `peft`            | `from peft`, `import peft`                       | Also check for indirect PEFT schema dicts (`\"r\"`, `\"lora_alpha\"`, `\"target_modules\"`) |\n| `torchao`         | `torchao`         | `from torchao`, `import torchao`                 | All usage goes through `prototype.*` path                                             |\n\n### Edge cases to watch for\n\n- **HTTP-only integrations** (sglang, vllm): The primary integration is REST-based, not\n  Python imports. Grep for endpoint path strings (e.g., `\"/generate\"`,\n  `\"/update_weights_from_distributed\"`) and JSON field names in request/response parsing\n  code.\n- **Indirect / schema-only usage**: Some files reference a package's conventions without\n  importing it directly (e.g., PEFT config dicts in `areal/api/io_struct.py`). These\n  should be listed in Affected Files and may warrant an API catalog entry if the schema\n  contract is non-trivial.\n- **Conditional / lazy imports**: Imports inside `if TYPE_CHECKING:` blocks or inside\n  function bodies. These are still real dependencies — include them.\n- **String references**: Config enums or registry keys that name a package (e.g.,\n  `\"sglang\"` in `SGLangConfig`). These rarely need catalog entries but should appear in\n  Affected Files if the file also has substantive usage.\n- **Test stubs**: Files like `tests/experimental/archon/conftest.py` that conditionally\n  stub out a package. List in Tertiary Affected Files.\n\n______________________________________________________________________\n\n## 3. Structural Validation Procedure\n\n> **Referenced by:** SKILL.md → Step 0.5\n\nThis procedure checks whether a checklist documents ALL current AReaL import sites and\ncall patterns. Run it before upgrading a package to ensure the API audit (Step 6) has\ncomplete coverage.\n\n### 3.1 Discover all usages\n\nFor each focused package being validated:\n\n1. Grep the AReaL codebase using the patterns from § 2. Search across `areal/`,\n   `tests/`, and `examples/`.\n1. For HTTP-based packages, also scan the relevant remote engine files for endpoint\n   paths and JSON field names.\n1. Exclude false positives: comments, docstrings, unrelated substrings (e.g.,\n   `flash_attn.bert_padding` is not a `transformers` import).\n1. Collect results as `{file_path, imported_names[]}` for each hit.\n\n### 3.2 Compare against Affected Files tables\n\nParse the checklist's Primary / Secondary / Tertiary tables and compare against the\ndiscovered files. Build three lists:\n\n- **MISSING** — files found in the codebase that are not listed in any tier.\n- **STALE** — files listed in the checklist that no longer import the package (file\n  deleted, import removed, or moved to a different file).\n- **CHANGED** — files listed in the checklist whose actual imports differ from what the\n  \"Imports / Usage\" column documents.\n\n### 3.3 Classify new files into tiers\n\nUse these rules (consistent with existing checklists):\n\n| Tier          | Directories                                                                                                                                                               | Rationale                                                                |\n| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |\n| **Primary**   | `areal/engine/`, `areal/experimental/engine/`, `areal/experimental/models/`                                                                                               | Engine layer — most likely to break on API changes, highest blast radius |\n| **Secondary** | `areal/models/`, `areal/workflow/`, `areal/infra/`, `areal/api/`, `areal/utils/`, `areal/trainer/`, `areal/dataset/`, `areal/reward/`, `areal/experimental/` (non-engine) | Model / infra / workflow layer — important but lower blast radius        |\n| **Tertiary**  | `tests/`, `examples/`, `docs/`, tool scripts                                                                                                                              | Tests and examples — breakage is contained, lower priority               |\n\nWhen a file spans concerns (e.g., a test that exercises engine internals), classify by\nthe file's location, not its content.\n\n### 3.4 Determine new API catalog entries\n\nFor each MISSING or CHANGED file, examine the specific imports and call sites:\n\n- If the usage pattern is **already covered** by an existing catalog entry (same\n  function/class, same call pattern) → just add the file to the Affected Files table. No\n  new catalog entry needed.\n- If the usage involves a function/class **NOT in the catalog**, or uses it with\n  **significantly different parameters** → create a new API catalog entry (see § 6 for\n  format).\n\n\"Significantly different\" means: different keyword arguments, different return value\nconsumption, or a different code path that could break independently.\n\n### 3.5 Update the checklist\n\n1. Add missing files to the appropriate tier table with their imports summary.\n1. Remove stale file entries. If uncertain whether a removal is correct, add a\n   `<!-- TODO: verify removal -->` comment instead of deleting.\n1. Update the \"Imports / Usage\" column for CHANGED files.\n1. Add new API catalog entries following the format in § 6.\n1. Renumber catalog entries sequentially (1, 2, 3, ...).\n1. Update the entry count in SKILL.md's **Checklist File Status** table at the bottom.\n\n### 3.6 Report format\n\nAfter completing validation, report changes before proceeding:\n\n```text\nChecklist validation for <package>:\n- Added N files to Affected Files (P primary, S secondary, T tertiary)\n- Added M new API catalog entries: [brief list]\n- Removed K stale file entries: [brief list]\n- Updated J existing entries with changed imports\n- No changes needed (if clean)\n```\n\n______________________________________________________________________\n\n## 4. Content Update Procedure\n\n> **Referenced by:** SKILL.md → Step 6e\n\nThis procedure updates a checklist's API signatures and code snippets AFTER an upgrade\nhas been applied and code changes (Step 6d) are complete. The goal is to keep the\nchecklist accurate for the next upgrade cycle.\n\n### 4.1 Update API signatures\n\nFor each catalog entry where the upstream API signature changed at the new version:\n\n1. Update the **Source** path if the upstream file was moved or renamed.\n1. Note any new parameters, removed parameters, or type changes in the **Check**\n   instructions — these become the baseline for the next upgrade.\n1. If a function was removed and replaced, update the entry to reference the\n   replacement.\n\n### 4.2 Update call-site code snippets\n\nIf Step 6d modified an AReaL call site to accommodate the new version:\n\n1. Replace the code snippet in the corresponding catalog entry with the updated code.\n1. Update the line number references if they shifted.\n1. Ensure the snippet includes enough context to understand the parameters used and the\n   return value consumed.\n\n### 4.3 Update version-guarded code\n\n- **Remove** entries for version guards that were cleaned up (the guarded version is now\n  below the minimum supported version).\n- **Add** entries for any new version guards introduced during the upgrade.\n- **Update** threshold versions in existing entries.\n\n### 4.4 Update frontmatter\n\n- Add or remove entries in `upstream_paths` if upstream source files were moved,\n  renamed, or deleted.\n- Update `branch_template` if the upstream project changed its tagging convention (e.g.,\n  from `v${VERSION}` to `release/${VERSION}`).\n\n### 4.5 Update SKILL.md status table\n\nUpdate the entry count and description in the **Checklist File Status** table at the\nbottom of SKILL.md to reflect the current state of the checklist.\n\n______________________________________________________________________\n\n## 5. Creating a New Checklist\n\nWhen a new package becomes \"focused\" (added to SKILL.md's Focused Packages table):\n\n1. **Copy the template.** Copy `checklists/_TEMPLATE.md` to\n   `checklists/<package-name>.md`.\n\n1. **Fill in YAML frontmatter:**\n\n   - `package`: the pip package name (e.g., `sglang`)\n   - `github`: the GitHub org/repo (e.g., `sgl-project/sglang`)\n   - `branch_template`: how to construct the git tag from a version string (e.g.,\n     `v${VERSION}`)\n   - `upstream_paths`: list of source paths in the upstream repo that are most relevant\n     to AReaL's usage\n\n1. **Run structural validation.** Follow the full procedure in § 3 to populate the\n   Affected Files tables and API Usage Catalog.\n\n1. **Update SKILL.md.** Add the package to:\n\n   - The **Focused Packages** table (Architecture section)\n   - The **Package Impact Matrix** (with correct scope and pyproject locations)\n   - The **Checklist File Status** table (bottom of file)\n\n1. **Commit** the new checklist alongside the SKILL.md update.\n\n______________________________________________________________________\n\n## 6. API Catalog Entry Format\n\nEach entry documents one distinct API surface — a function, class, HTTP endpoint, or\nschema contract. Use this format:\n\n````markdown\n### N. `fully.qualified.FunctionOrClass`\n\n**Source:** `upstream-repo/path/to/file.py`\n\nCalled in `areal/path/to/file.py` (description, lines N–M):\n\n```python\n# Paste the ACTUAL call site code — enough context to understand\n# the parameters used and the return value consumed.\nactual_call(param1=..., param2=...)\n```\n\n**Check:** [What to verify — be specific.]\n````\n\n### Guidelines\n\n- **One entry per distinct API surface.** A function, class, HTTP endpoint, or schema\n  contract gets one entry. If the same function is called in multiple files with the\n  same pattern, list all call sites in a single entry.\n- **Multiple call sites.** When the same API is used in several files, list the primary\n  call site with a full code snippet, then note additional call sites with a brief\n  reference: \"Also called in `areal/path/to/other.py`.\"\n- **HTTP endpoints.** Document the endpoint path, HTTP method, request payload fields,\n  and response fields that AReaL parses. These are API surfaces even though they are not\n  Python imports.\n- **Indirect / schema usage.** If AReaL constructs a dict that must conform to a\n  package's schema (e.g., PEFT config dicts), document the schema contract and note that\n  it is indirect.\n- **Check instructions.** Be specific and actionable:\n  - Good: \"Confirm `bias` still accepts the string `\"none\"` (not renamed to an enum).\"\n  - Bad: \"Check if anything changed.\"\n- **Code snippets.** Include enough surrounding code to show parameter names, types, and\n  how the return value is consumed. Strip unrelated code. Include line numbers for\n  reference.\n\n______________________________________________________________________\n\n## 7. When NOT to Add a Catalog Entry\n\nNot every import needs its own API catalog entry. Skip a dedicated entry when:\n\n- **Type-only imports.** A class imported solely for type annotations or `isinstance`\n  checks (e.g., `from transformers import PretrainedConfig` used only as\n  `config: PretrainedConfig`). List the file in Affected Files but skip the catalog\n  unless the type annotation relies on specific attributes that could change.\n- **Re-exports / pass-throughs.** A file that re-exports a symbol without calling it.\n- **Standard patterns unlikely to break.** Accessing `__version__`, basic enum values,\n  or other stable public API that has never changed across versions.\n- **Test files duplicating Primary/Secondary coverage.** If a test file uses the exact\n  same API pattern already documented in a Primary or Secondary entry, list it in\n  Tertiary Affected Files but skip a separate catalog entry. Exception: if the test\n  exercises a unique code path or parameter combination, add an entry.\n\nWhen in doubt, **add the entry**. A slightly verbose checklist is better than a gap that\ncauses a missed breaking change.\n",".agents/skills/upgrade-deps/SKILL.md":"---\nname: upgrade-deps\ndescription: Upgrade focused runtime dependencies in AReaL. First validates and updates per-package API checklists for structural completeness, then updates pyproject files, resolves conflicts, locks, updates the Dockerfile, and audits API compatibility against the checklists.\n---\n\n## Usage\n\n```text\n/upgrade-deps <package==version> [<package==version> ...]\n```\n\n**Arguments:** One or more pinned package versions, e.g.,\n`/upgrade-deps megatron-core==0.17.0 sglang==0.5.10 vllm==0.18.0 transformers==4.58.0`.\n\nIf a package is omitted, its current version can be preserved or upgraded, depending on\nthe resolution of `uv lock`.\n\n______________________________________________________________________\n\n## Architecture\n\n### Dual-Manifest Model\n\nAReaL maintains **two pyproject files** because SGLang and vLLM pin\nmutually-incompatible `torch` / `torchao` versions:\n\n| File                  | Inference backend | Lock file      |\n| --------------------- | ----------------- | -------------- |\n| `pyproject.toml`      | SGLang (default)  | `uv.lock`      |\n| `pyproject.vllm.toml` | vLLM              | `uv.vllm.lock` |\n\nBoth share the same core dependencies, megatron extras, and dev group. They diverge only\nin inference backend extras and `torch`/`torchao` version constraints.\n\nThe `Dockerfile` builds both variants from a single file using `ARG VARIANT` (`sglang`\nor `vllm`). The base image, torch install, and flash-attn wheels are all\nvariant-specific.\n\n### Focused Packages\n\nThe following packages are **focused** — their API usage in AReaL is cataloged, and any\nversion change triggers the API compatibility audit (Step 6):\n\n| Package           | Import path       |\n| ----------------- | ----------------- |\n| `megatron-core`   | `megatron.core`   |\n| `megatron-bridge` | `megatron.bridge` |\n| `mbridge`         | `mbridge`         |\n| `transformers`    | `transformers`    |\n| `sglang`          | `sglang`          |\n| `vllm`            | `vllm`            |\n| `peft`            | `peft`            |\n| `torchao`         | `torchao`         |\n\n`torch` is tracked in the Package Impact Matrix below for scope and Docker awareness,\nbut is **not** a focused package — it does not receive API auditing.\n\n### Package Impact Matrix\n\nEvery entry below has a **variant scope** that determines which files to edit, which\nlock files to regenerate, and whether the Dockerfile needs review. All focused packages\nplus `torch` (tracked for Docker impact only, not API-audited) are listed.\n\n| Package           | Scope            | pyproject.toml locations                                       | pyproject.vllm.toml locations                                  | Docker impact |\n| ----------------- | ---------------- | -------------------------------------------------------------- | -------------------------------------------------------------- | ------------- |\n| `sglang`          | sglang-only      | `[optional-deps].sglang`                                       | —                                                              | Base image    |\n| `vllm`            | vllm-only        | —                                                              | `[optional-deps].vllm`                                         | No            |\n| `megatron-core`   | shared           | `[optional-deps].megatron` + `[tool.uv].override-dependencies` | `[optional-deps].megatron` + `[tool.uv].override-dependencies` | No            |\n| `megatron-bridge` | shared           | `[optional-deps].megatron`                                     | `[optional-deps].megatron`                                     | No            |\n| `mbridge`         | shared           | `[optional-deps].megatron` (git pin)                           | `[optional-deps].megatron` (git pin)                           | No            |\n| `transformers`    | shared           | `[project].dependencies`                                       | `[project].dependencies`                                       | No            |\n| `peft`            | shared           | `[project].dependencies`                                       | `[project].dependencies`                                       | No            |\n| `torch`           | shared-divergent | `[project].dependencies` (≥2.9.1)                              | `[project].dependencies` (≥2.10.0)                             | Stage 1 torch |\n| `torchao`         | shared-divergent | `[tool.uv].override-dependencies` (==0.15.0)                   | `[project].dependencies` (==0.16.0)                            | No            |\n\n**Scope rules:**\n\n- **sglang-only / vllm-only**: Edit and lock only the affected variant.\n- **shared**: Edit both files; lock both variants.\n- **shared-divergent**: The two variants intentionally use different versions of the\n  same package. If the user supplies a single target version, **ask which variant(s) to\n  apply it to** rather than assuming convergence. If the user supplies two targets\n  (e.g., `torch==2.9.1@sglang torch==2.10.0@vllm`), apply each accordingly.\n\n### Upgrade Families\n\nSome packages are tightly coupled and should be upgraded together:\n\n| Family    | Members                                  | Reason                                                    |\n| --------- | ---------------------------------------- | --------------------------------------------------------- |\n| megatron  | `megatron-core`, `megatron-bridge`       | megatron-bridge wraps megatron-core; tightly coupled APIs |\n| inference | `sglang` or `vllm` + `torch` + `torchao` | Inference backends pin specific torch/torchao versions    |\n\nWhen the user upgrades one member, **check the checklist of all family members** for\nrequired co-upgrades and warn if a co-upgrade is needed but not requested.\n\n### Per-Package API Checklists\n\nEach focused package has a dedicated markdown file under `checklists/` that documents\nhow AReaL uses that package's APIs:\n\n```\n.agents/skills/upgrade-deps/\n├── SKILL.md\n└── checklists/\n    ├── megatron-core.md\n    ├── megatron-bridge.md\n    ├── vllm.md\n    ├── sglang.md\n    ├── transformers.md\n    ├── peft.md\n    └── torchao.md\n```\n\nEach checklist file MUST contain the following sections:\n\n````markdown\n---\npackage: <pip-package-name>\ngithub: <org/repo>                    # e.g., NVIDIA/Megatron-LM\nbranch_template: \"v${VERSION}\"        # how to construct the git branch/tag from version\nupstream_paths:                        # source paths to cross-reference\n  - path/to/relevant/file.py\n  - path/to/relevant/module/\n---\n\n## Affected Files\n\n### Primary (most likely to break)\n| File | Imports / Usage |\n| ---- | --------------- |\n| ...  | ...             |\n\n### Secondary\n...\n\n### Tertiary (tests, infra)\n...\n\n## API Usage Catalog\n\nFor each function/class, verify the call signature against the upstream source.\nFocus on: removed params, renamed params, new required params, changed return types,\nmoved/renamed modules.\n\n### 1. `module.submodule.function_or_class`\n\n**Source:** `upstream_path/file.py`\n\nCalled in `areal/path/to/file.py:LINE`:\n\\```python\nactual_call_site_code()\n\\```\n\n**Check:** [what to verify]\n\n### 2. ...\n\n## Version-Guarded Code (if any)\n- [file:line] description of version guard and when it can be removed\n````\n\nTo populate a checklist, catalog AReaL's usage of the package by grepping for imports\nand call sites, then fill in the template sections. Each checklist should be\nself-contained — all API signatures, file paths, and upstream references must be\nrecorded directly in the checklist file.\n\n______________________________________________________________________\n\n## Workflow\n\n### Step 0: Parse Input and Record Baseline\n\n1. Parse arguments. Each argument is `<package==version>` or, for shared-divergent\n   packages, `<package==version@variant>` (e.g., `torchao==0.16.0@vllm`).\n1. Validate that each package name is a recognized focused package (see Focused Packages\n   table). If unrecognized, warn but proceed — it may be a transitive dependency the\n   user wants to pin.\n1. For **shared-divergent** packages (`torch`, `torchao`): if the user supplies a single\n   version without a `@variant` qualifier, **ask which variant(s) to apply it to**\n   before proceeding.\n1. Record the current **resolved** versions of ALL focused packages from the applicable\n   lock file(s) as the baseline snapshot. Use `uv.lock` for sglang-scoped and shared\n   packages; `uv.vllm.lock` for vllm-scoped and shared packages. Packages scoped to a\n   single variant appear only in that variant's lock file. This baseline will be used\n   later to detect which packages actually changed — including transitive bumps caused\n   by `uv lock` resolution, not just explicit pin changes.\n\n### Step 0.5: Validate and Update Checklists\n\nBefore modifying any dependencies, verify that the API checklists for the packages being\nupgraded are structurally complete — i.e., they document all current AReaL import sites\nand call patterns. Stale checklists lead to missed breaking changes in Step 6.\n\nFor each **focused package explicitly requested** in the command that has a checklist\nfile under `checklists/`:\n\n1. **Discover all usages.** Grep the AReaL codebase (`areal/`, `tests/`, `examples/`)\n   for all imports matching the package's import path(s). See the Import Patterns table\n   in `CHECKLIST_MAINTENANCE.md` § 2 for package-specific grep patterns. For HTTP-based\n   integrations (sglang, vllm), also scan request-building code for endpoint paths and\n   JSON field names.\n\n1. **Compare against the checklist.** Diff the discovered files against the Affected\n   Files tables (Primary / Secondary / Tertiary) in the checklist. Identify:\n\n   - **Missing** — files that import the package but are not listed in any tier.\n   - **Stale** — files listed in the checklist that no longer import the package.\n   - **Changed** — files whose actual imports differ from what's documented.\n\n1. **Classify and update.** For each discrepancy, follow the Structural Validation\n   Procedure in `CHECKLIST_MAINTENANCE.md` § 3:\n\n   - Add missing files to the appropriate tier (Primary / Secondary / Tertiary).\n   - Add new API Usage Catalog entries for genuinely new call patterns.\n   - Remove stale entries.\n   - Renumber catalog entries if needed.\n\n1. **Update the Checklist File Status table** at the bottom of this file if entry counts\n   changed.\n\n1. **Report** changes before proceeding:\n\n   ```text\n   Checklist validation for <package>:\n   - Added N files to Affected Files (P primary, S secondary, T tertiary)\n   - Added M new API catalog entries: [brief list]\n   - Removed K stale entries: [brief list]\n   - No changes needed (if clean)\n   ```\n\nIf a requested focused package does **not** have a checklist file, create one from\n`checklists/_TEMPLATE.md` using the full procedure in `CHECKLIST_MAINTENANCE.md` § 5.\n\n**Scope note:** This step validates only the packages explicitly named in the command.\nPackages that are transitively bumped are identified later in Step 5; their checklists\nare validated at that point using the same procedure before the API audit in Step 6.\n\n### Step 1: Update Pyproject Files\n\nFor each requested package, using the Package Impact Matrix:\n\n1. Determine the **variant scope** (sglang-only, vllm-only, shared, shared-divergent).\n1. Edit the version pin in **all declaration locations** for each affected pyproject\n   file. A single package may appear in multiple locations:\n   - `[project].dependencies`\n   - `[project.optional-dependencies].<extra>`\n   - `[tool.uv].override-dependencies`\n1. For **upgrade families**: if `megatron-core` is being upgraded, also check whether\n   `megatron-bridge` needs a corresponding version bump. Warn if it does but was not\n   included in the command.\n\n### Step 2: Lock Dependencies (Variant-Aware)\n\nRegenerate lock files for each **affected variant**. A variant is affected if any of its\npyproject file's dependencies were modified. If conflicts arise during locking, do NOT\nattempt to resolve them in this step — just report them and defer resolution to Step 3.\n\n**SGLang variant** (if `pyproject.toml` was modified):\n\n```bash\nbash scripts/uv_lock.sh\n```\n\n**vLLM variant** (if `pyproject.vllm.toml` was modified):\n\n```bash\nbash scripts/uv_lock.sh\n```\n\nIf `uv lock` fails for either variant:\n\n1. Read the error message carefully.\n1. If it's a version conflict, proceed to Step 3 to resolve, then **return here to\n   re-lock**.\n1. Do NOT modify dependencies without user approval to make the lock succeed.\n\n**Validation:** After locking, verify both lock files exist and are non-empty.\n\n### Step 3: Resolve Dependency Conflicts\n\nResolve any conflicts from Step 2. After all conflicts are resolved, **return to Step 2\nand re-lock** the affected variant(s). Classify each conflict:\n\n**Auto-resolvable** — only AReaL's pin conflicts with an upstream package, and the\nupstream's required version is acceptable. Update AReaL's pin automatically.\n\n**Needs user input** — two upstream packages have mutual conflicts (e.g., sglang\nrequires `torch==2.9.1` but vllm requires `torch==2.10.0`). Summarize and ask the user.\n\nOutput format:\n\n```\nSummary\n\n---\nAuto-resolved (no action required):\n- <name>: <packageA> requires <versionA>, <packageB> requires <versionB>,\n  AReaL specified <oldVersion>, updated to <newVersion>\n- ...\n\n---\nConflicts (need user resolution):\n- <name>: <packageA> requires <versionA>, <packageB> requires <versionB>\n- ...\n```\n\nYou may use `override-dependencies` in `[tool.uv]` to force-pin versions where needed.\nRemember that `sglang` and `vllm` are **separate variants** maintained in different\npyproject files — they are never installed together in the same environment.\n\n### Step 4: Update Dockerfile (If Required)\n\nReview the Package Impact Matrix \"Docker impact\" column. Only proceed with Dockerfile\nchanges if an upgraded package has Docker impact. The most common triggers are:\n\n**Base image change** (triggered by `sglang` upgrade):\n\nThe Dockerfile base image is `lmsysorg/sglang:v{SGLANG_VERSION}-cu129-amd64-runtime`. If\n`sglang` was upgraded, update line 9 of the `Dockerfile`:\n\n```dockerfile\nFROM lmsysorg/sglang:v{NEW_SGLANG_VERSION}-cu129-amd64-runtime\n```\n\nVerify that the new base image tag exists on Docker Hub / GHCR before committing.\n\n**Torch version change** (triggered by `torch` upgrade):\n\nThe Dockerfile Stage 1 installs torch with variant-specific versions. Update the version\nmapping in the `RUN uv venv` command:\n\n```dockerfile\n&& if [ \"$VARIANT\" = \"vllm\" ]; then TORCH_VER=\"{VLLM_TORCH}\"; else TORCH_VER=\"{SGLANG_TORCH}\"; fi \\\n```\n\nAlso check flash-attn wheel compatibility — **both** flash-attn-2 and flash-attn-3\ninstalls use a `TORCH_TAG` that must match the torch major.minor version. Update both\noccurrences in the Dockerfile:\n\n```dockerfile\n# flash-attn-2 (first flash-attn RUN block)\n&& if [ \"$VARIANT\" = \"vllm\" ]; then TORCH_TAG=\"torch{VLLM_TORCH_MAJOR_MINOR}\"; else TORCH_TAG=\"torch{SGLANG_TORCH_MAJOR_MINOR}\"; fi \\\n\n# flash-attn-3 (second flash-attn RUN block — same TORCH_TAG pattern)\n&& if [ \"$VARIANT\" = \"vllm\" ]; then TORCH_TAG=\"torch{VLLM_TORCH_MAJOR_MINOR}\"; else TORCH_TAG=\"torch{SGLANG_TORCH_MAJOR_MINOR}\"; fi \\\n```\n\n**No Dockerfile changes needed** for: `megatron-core`, `megatron-bridge`,\n`transformers`, `peft`, `vllm`, `torchao`. These are installed via\n`uv pip install -r pyproject.toml` in Stage 3, which reads the updated pyproject\nautomatically.\n\n### Step 5: Identify Updated Focused Packages\n\nCompare the baseline snapshot (Step 0) against the **resolved** versions in the lock\nfiles (`uv.lock` and/or `uv.vllm.lock`) produced by Step 2 (or re-locked after Step 3\nconflict resolution). This catches not only explicitly requested upgrades but also\n**transitive version bumps** — e.g., upgrading `sglang` may pull in a newer\n`transformers` through dependency resolution.\n\nBuild a list of focused packages whose resolved version actually changed. This list\ndetermines which API checklists to audit in Step 6.\n\nThe focused packages to check are listed in the Focused Packages table (Architecture\nsection):\n\n- `megatron-core` (imports as `megatron.core`)\n- `megatron-bridge` (imports as `megatron.bridge`)\n- `transformers`\n- `sglang`\n- `vllm`\n- `peft`\n- `torchao`\n\nIf a package version did NOT change (even if it was in the user's input but resolved to\nthe same version), skip its API audit.\n\nFor any newly-identified package whose checklist was NOT already validated in Step 0.5,\nrun the same structural validation procedure (see `CHECKLIST_MAINTENANCE.md` § 3) on its\nchecklist before proceeding to Step 6.\n\n### Step 6: API Compatibility Audit\n\nFor each updated focused package that has a checklist file under `checklists/`:\n\n#### 6a. Clone upstream source\n\nRead the checklist frontmatter to get `github` and `branch_template`. Clone or checkout\nthe target version:\n\n```bash\nREPO_ROOT=$(pwd)\nPKG_DIR=\"${REPO_ROOT}/<package>-src\"\nVERSION=\"<target_version>\"\n# Validate VERSION to prevent command injection\nif [[ ! \"$VERSION\" =~ ^[a-zA-Z0-9._/-]+$ ]]; then\n  echo \"Error: Invalid version format: $VERSION\"; exit 1\nfi\nBRANCH=$(echo \"<branch_template>\" | sed \"s/\\${VERSION}/$VERSION/\")\nif [ ! -d \"$PKG_DIR\" ]; then\n  git clone --depth 1 --branch \"$BRANCH\" \"https://github.com/<github>.git\" \"$PKG_DIR\"\nelse\n  (cd \"$PKG_DIR\" && git fetch origin && git checkout \"$BRANCH\")\nfi\n```\n\nIf cloning fails (tag doesn't exist, etc.), report to the user immediately.\n\n#### 6b. Audit API signatures\n\nFor EACH entry in the checklist's API Usage Catalog:\n\n1. Open the upstream source file at the target version (paths listed in the checklist's\n   `upstream_paths` frontmatter).\n1. Compare the function/class signature against the current AReaL invocation.\n1. Flag any of:\n   - **Removed parameters** still passed by AReaL → must remove from call site\n   - **Renamed parameters** → must rename in call site\n   - **New required parameters** (no default) → must add to call site\n   - **New optional parameters** with useful defaults → document but skip\n   - **Changed return types** → must update consumers\n   - **Removed functions/classes** → must find replacement\n   - **Moved modules** → must update import paths\n   - **Changed method signatures** on returned objects → must update call sites\n1. Record findings per-file.\n\n#### 6c. Check version-guarded code\n\nIf the checklist has a \"Version-Guarded Code\" section, check whether any guards\nreference versions at or below the new target. If so, verify the upstream fix is present\nand note the dead code for cleanup.\n\n#### 6d. Apply code changes (if any)\n\nFor each flagged incompatibility:\n\n1. Update the call site in the affected AReaL file.\n1. **Preserve existing behavior** — do NOT refactor beyond what's required.\n1. If a function was removed, check the upstream migration guide or changelog.\n1. Priority order: engine layer → model layer → infra layer → test files.\n\nIf there are unresolvable breaking changes, **STOP and ask the user** before proceeding.\n\n#### 6e. Update checklist file\n\nUpdate the checklist file to reflect the post-upgrade state. Follow the Content Update\nProcedure in `CHECKLIST_MAINTENANCE.md` § 4:\n\n1. Update API signatures in catalog entries where upstream changed.\n1. Update call-site code snippets where Step 6d modified AReaL code.\n1. Update version-guarded code entries (remove cleaned-up guards, add new ones).\n1. Update frontmatter `upstream_paths` if source files moved.\n1. Update the entry count in the Checklist File Status table (bottom of this file).\n\nThis ensures the checklist remains an accurate reference for future upgrades.\n\n#### 6f. Clean up cloned repositories\n\nRemove the cloned upstream source directories to avoid cluttering the workspace:\n\n```bash\nrm -rf \"${REPO_ROOT}/<package>-src\"\n```\n\n### Step 7: Run Pre-Commit\n\n```bash\npre-commit run --all-files\n```\n\n### Step 8: Generate Upgrade Summary\n\nDump a formatted markdown summary to `upgrade-summary.md` in the repository root (this\nfile is gitignored and ephemeral). The summary MUST include:\n\n```markdown\n## Dependency Upgrade Summary\n\n**Date:** YYYY-MM-DD\n**Requested:** <original command>\n\n### Version Changes\n\n| Package | Old Version | New Version | Variant(s) |\n| ------- | ----------- | ----------- | ---------- |\n| ...     | ...         | ...         | ...        |\n\n### Dependency Resolution\n\n- <auto-resolved change description>\n- ...\n\n### Dockerfile Changes\n\n- <change description, or \"No changes required\">\n\n### API Compatibility Audit\n\n#### <package-name> (old → new)\n\n**Breaking changes found:**\n- [file:line] description of change\n\n**Module moves / renames:**\n- [old_path] → [new_path]\n\n**Version-guarded code:**\n- [file:line] status (still needed / can be removed)\n\n**No breaking changes found** _(if clean)_\n\n#### ...\n\n### Unresolved Issues (if any)\n\n- <description of issue and why it could not be auto-resolved>\n```\n\nIf the upgrade failed at any step, the summary should still be generated with the\nfailure reason clearly documented in the \"Unresolved Issues\" section.\n\n### Step 9: Create PR and Trigger CI (Optional)\n\nAsk the user if they want to create a PR.\n\nIf the user agrees:\n\n1. Load the `create-pr` skill to create the PR.\n1. Trigger the CI workflow manually via `.github/workflows/build-docker-image.yml` (only\n   if the Dockerfile was modified or inference backend versions changed).\n1. The Docker build CI builds both sglang and vllm images, then automatically triggers\n   testing on each. Debug until the overall workflow succeeds.\n1. If you encounter issues that cannot be resolved, ask the user for help.\n\n______________________________________________________________________\n\n## Checklist File Status\n\n| Package           | Checklist file                  | Status                                                                                                                                                                              |\n| ----------------- | ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `megatron-core`   | `checklists/megatron-core.md`   | ✅ 23 API entries (parallel_state, DDP, optimizer, pipeline, checkpointing/async internals, transformer config, FP8, GPT/MTP, tensor_parallel, custom attention, layer specs, RoPE) |\n| `megatron-bridge` | `checklists/megatron-bridge.md` | ✅ 9 API entries (AutoBridge, LoRA, save/load HF, Qwen3-VL patch target, AutoMapping, monkey-patch guard)                                                                           |\n| `mbridge`         | `checklists/mbridge.md`         | ✅ 14 API entries (AutoBridge, Bridge properties, weight mappings, LLMBridge subclassing, register_model, monkey-patch target)                                                      |\n| `vllm`            | `checklists/vllm.md`            | ✅ 14 API entries (entrypoints, LoRA manager, worker V0/V1, tool parsers, CLI)                                                                                                      |\n| `sglang`          | `checklists/sglang.md`          | ✅ 14 API entries (HTTP endpoints, tool/reasoning parsers, CLI flags, version guards)                                                                                               |\n| `transformers`    | `checklists/transformers.md`    | ✅ 12 API entries (Auto\\* classes, tokenizer, flash attention monkey-patches, Qwen VL internals, LR schedulers)                                                                     |\n| `peft`            | `checklists/peft.md`            | ✅ 4 API entries (LoraConfig, TaskType, get_peft_model, weight key format)                                                                                                          |\n| `torchao`         | `checklists/torchao.md`         | ✅ 5 API entries (fp8_blockwise_mm, enable_fp8_linear/experts, shard validation, Triton kernels)                                                                                    |\n",".agents/skills/upgrade-deps/checklists/_TEMPLATE.md":"<!-- Template for per-package API checklists. Copy this file and fill in for each package. -->\n\n______________________________________________________________________\n\npackage: <pip-package-name> github: \\<org/repo> branch_template: \"v${VERSION}\"\nupstream_paths:\n\n- path/to/relevant/file.py\n- path/to/relevant/module/\n\n______________________________________________________________________\n\n## Affected Files\n\n### Primary (engine layer — most likely to break)\n\n| File                    | Imports / Usage                   |\n| ----------------------- | --------------------------------- |\n| `areal/path/to/file.py` | `module.function`, `module.Class` |\n\n### Secondary (model / infra layer)\n\n| File                    | Imports / Usage   |\n| ----------------------- | ----------------- |\n| `areal/path/to/file.py` | `module.function` |\n\n### Tertiary (tests, config)\n\n| File                 | Imports / Usage   |\n| -------------------- | ----------------- |\n| `tests/test_file.py` | `module.function` |\n\n______________________________________________________________________\n\n## API Usage Catalog\n\nFor each function/class below, verify the call signature against the upstream source at\nthe target version. Focus on: **missing new required parameters**, **removed old\nparameters**, **renamed parameters**, **changed return types**, **changed method\nsignatures on returned objects**, and **moved/renamed modules**.\n\n### 1. `module.submodule.FunctionOrClass`\n\n**Source:** `upstream-repo/path/to/file.py`\n\nCalled in `areal/path/to/file.py`:\n\n```python\n# Paste the actual call site code here\nFunctionOrClass(param1=..., param2=...)\n```\n\n**Check:** \\[Describe what to verify — new params? renamed? return type change?\\]\n\n### 2. ...\n\n______________________________________________________________________\n\n## Version-Guarded Code\n\n<!-- List any AReaL code that has version-specific behavior for this package -->\n\n- `areal/path/to/file.py:LINE` — description of version guard and threshold\n",".agents/skills/upgrade-deps/checklists/mbridge.md":"---\npackage: mbridge\ngithub: ISEEKYAN/mbridge\nbranch_template: ${VERSION}\nupstream_paths:\n  - mbridge/__init__.py\n  - mbridge/core/__init__.py\n  - mbridge/core/auto_bridge.py\n  - mbridge/core/bridge.py\n  - mbridge/core/llm_bridge.py\n  - mbridge/core/util.py\n---\n\n## Affected Files\n\n### Primary (engine layer — most likely to break)\n\n| File                                        | Imports / Usage                                                                                                                                                                                 |\n| ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `areal/engine/megatron_engine.py`           | `import mbridge` → `mbridge.AutoBridge.from_pretrained`, `.dtype`, `.set_extra_args`                                                                                                            |\n| `areal/models/mcore/hf_load.py`             | `from mbridge.core.bridge import Bridge` → `bridge._get_actual_hf_path`, `._weight_name_mapping_*`, `.hf_config`, `.dtype`, `.config`                                                           |\n| `areal/models/mcore/hf_save.py`             | `from mbridge.core import Bridge`, `from mbridge.core.util import unwrap_model` → `bridge._weight_name_mapping_*`, `._weight_merge_across_tp`, `._weight_to_hf_format`, `.hf_config`, `.config` |\n| `areal/models/mcore/bailing_moe_bridge.py`  | `from mbridge.core import LLMBridge, register_model` → subclasses `LLMBridge`, uses `@register_model` decorator                                                                                 |\n| `areal/models/tree_attn/module_megatron.py` | `from mbridge.core import LLMBridge` → monkey-patches `LLMBridge._get_transformer_layer_spec`                                                                                                   |\n\n### Secondary (model / infra layer)\n\n| File                             | Imports / Usage                                                                          |\n| -------------------------------- | ---------------------------------------------------------------------------------------- |\n| `areal/models/mcore/registry.py` | `from mbridge.core.bridge import Bridge` → `bridge.hf_config`, `.config`, `.get_model()` |\n\n### Tertiary (tests, config)\n\n| File                                          | Imports / Usage                                         |\n| --------------------------------------------- | ------------------------------------------------------- |\n| `tests/test_estimate_num_params.py`           | `import mbridge` → `mbridge.AutoBridge.from_pretrained` |\n| `areal/tools/validation_base.py`              | `\"mbridge\"` in `CRITICAL_PACKAGES` (metadata only)      |\n| `areal/tools/validate_docker_installation.py` | `\"mbridge\"` in package validation list (metadata only)  |\n\n______________________________________________________________________\n\n## API Usage Catalog\n\nFor each function/class below, verify the call signature against the upstream source at\nthe target version. Focus on: **missing new required parameters**, **removed old\nparameters**, **renamed parameters**, **changed return types**, **changed method\nsignatures on returned objects**, and **moved/renamed modules**.\n\n### 1. `mbridge.AutoBridge.from_pretrained`\n\n**Source:** `mbridge/core/auto_bridge.py`\n\nCalled in `areal/engine/megatron_engine.py` (line 507):\n\n```python\nself.bridge = mbridge.AutoBridge.from_pretrained(\n    self.config.path, trust_remote_code=True\n)\n```\n\nAlso called in `tests/test_estimate_num_params.py` (line 43):\n\n```python\nbridge = mbridge.AutoBridge.from_pretrained(model_name_or_path)\n```\n\n**Check:** Confirm `trust_remote_code` is still accepted as a keyword argument. Verify\nthe first positional arg is still the model path (str). Verify the return type is still\na `Bridge` (or subclass like `LLMBridge`) instance with `.hf_config`, `.config`, and\n`.dtype` attributes.\n\n______________________________________________________________________\n\n### 2. `Bridge.dtype` (property/attribute)\n\n**Source:** `mbridge/core/bridge.py`\n\nSet in `areal/engine/megatron_engine.py` (line 510):\n\n```python\nself.bridge.dtype = self.dtype\n```\n\n**Check:** Confirm `dtype` is still a settable attribute (not read-only property).\nVerify it accepts `torch.dtype` values. This controls the precision used for weight\nloading and conversion throughout the bridge.\n\n______________________________________________________________________\n\n### 3. `Bridge.set_extra_args`\n\n**Source:** `mbridge/core/bridge.py` or `mbridge/core/llm_bridge.py`\n\nCalled in `areal/engine/megatron_engine.py` (lines 512–518):\n\n```python\nself.bridge.set_extra_args(\n    recompute_granularity=self.mcore_config.recompute_granularity,\n    recompute_method=self.mcore_config.recompute_method,\n    recompute_num_layers=self.mcore_config.recompute_num_layers,\n    distribute_saved_activations=self.mcore_config.distribute_saved_activations,\n    recompute_modules=self.mcore_config.recompute_modules,\n)\n```\n\n**Check:** Confirm all five keyword arguments are still accepted:\n`recompute_granularity`, `recompute_method`, `recompute_num_layers`,\n`distribute_saved_activations`, `recompute_modules`. Check if new required arguments\nwere added.\n\n______________________________________________________________________\n\n### 4. `Bridge.get_model`\n\n**Source:** `mbridge/core/bridge.py` or `mbridge/core/llm_bridge.py`\n\nCalled in `areal/models/mcore/registry.py` (lines 178–188):\n\n```python\nmodels = bridge.get_model(\n    wrap_with_ddp=mcore_config.wrap_with_ddp,\n    ddp_config=dataclasses.asdict(mcore_config.ddp),\n    use_torch_fsdp2=mcore_config.use_torch_fsdp2,\n    use_custom_fsdp=mcore_config.use_custom_fsdp,\n    fp16=tf_config.fp16,\n    bf16=tf_config.bf16,\n    use_precision_aware_optimizer=mcore_config.use_precision_aware_optimizer,\n    overlap_param_gather_with_optimizer_step=mcore_config.overlap_param_gather_with_optimizer_step,\n)\n```\n\n**Check:** Confirm all keyword arguments are still accepted: `wrap_with_ddp`,\n`ddp_config`, `use_torch_fsdp2`, `use_custom_fsdp`, `fp16`, `bf16`,\n`use_precision_aware_optimizer`, `overlap_param_gather_with_optimizer_step`. Verify the\nreturn type is still an iterable of model instances (GPTModel or DDP-wrapped). Check for\nnewly added required parameters.\n\n______________________________________________________________________\n\n### 5. `Bridge._get_actual_hf_path`\n\n**Source:** `mbridge/core/bridge.py`\n\nCalled in `areal/models/mcore/hf_load.py` (line 539):\n\n```python\nweights_path = bridge._get_actual_hf_path(weights_path)\n```\n\n**Check:** Confirm this private method still exists and accepts a single string argument\n(path). Verify it returns a string (resolved path to the actual HF weights directory).\nSince this is a private API, check if it was renamed or replaced.\n\n______________________________________________________________________\n\n### 6. `Bridge._weight_name_mapping_mcore_local_to_global`\n\n**Source:** `mbridge/core/bridge.py` or `mbridge/core/llm_bridge.py`\n\nCalled in `areal/models/mcore/hf_load.py` (line 570):\n\n```python\nlocal_to_global_map = bridge._weight_name_mapping_mcore_local_to_global(model)\n```\n\nAlso called in `areal/models/mcore/hf_save.py` (line 243):\n\n```python\nbridge._weight_name_mapping_mcore_local_to_global(model, consider_ep=False)\n```\n\n**Check:** Confirm the method still accepts a model as the first positional argument.\nVerify `consider_ep` keyword argument is still accepted (used in hf_save.py). Verify the\nreturn type is still `dict[str, str]` mapping local parameter names to global names.\nCheck if the method was renamed or its signature changed.\n\n______________________________________________________________________\n\n### 7. `Bridge._weight_name_mapping_mcore_to_hf`\n\n**Source:** `mbridge/core/bridge.py` or `mbridge/core/llm_bridge.py`\n\nCalled in `areal/models/mcore/hf_load.py` (line 573):\n\n```python\nlocal_to_hf_map = {\n    k: bridge._weight_name_mapping_mcore_to_hf(v)\n    for k, v in local_to_global_map.items()\n    if \"_extra_state\" not in k\n}\n```\n\n**Check:** Confirm the method accepts a single string argument (mcore global weight\nname). Verify the return type is still `list[str]` (list of HF weight names). This is\nalso overridden in `BailingMoeBridge` — check that the base class signature is stable.\n\n______________________________________________________________________\n\n### 8. `Bridge._weight_merge_across_tp`\n\n**Source:** `mbridge/core/bridge.py` or `mbridge/core/llm_bridge.py`\n\nCalled in `areal/models/mcore/hf_save.py` (lines 414–415):\n\n```python\ninfer_params = bridge._weight_merge_across_tp(\n    s.global_name, infer_params, param\n)\n```\n\n**Check:** Confirm the method accepts three positional arguments: `mcore_weights_name`\n(str), `tp_shards` (list of tensors), `param` (tensor). Verify the return type is still\na `torch.Tensor`. This is overridden in `BailingMoeBridge` — check base class stability.\n\n______________________________________________________________________\n\n### 9. `Bridge._weight_to_hf_format`\n\n**Source:** `mbridge/core/bridge.py` or `mbridge/core/llm_bridge.py`\n\nCalled in `areal/models/mcore/hf_save.py` (lines 422–423):\n\n```python\nconverted_names, converted_params = bridge._weight_to_hf_format(\n    s.global_name, infer_params\n)\n```\n\n**Check:** Confirm the method accepts two positional arguments: `mcore_weights_name`\n(str) and `mcore_weights` (tensor). Verify the return type is still\n`tuple[list[str], list[torch.Tensor]]`. This is overridden in `BailingMoeBridge` — check\nbase class stability.\n\n______________________________________________________________________\n\n### 10. `mbridge.core.util.unwrap_model`\n\n**Source:** `mbridge/core/util.py`\n\nCalled in `areal/models/mcore/hf_save.py` (line 237):\n\n```python\nmodels = [unwrap_model(model) for model in models]\n```\n\n**Check:** Confirm the function still accepts a single model argument. Verify it returns\nthe unwrapped model (strips DDP/FSDP wrappers). Check if the function was moved to a\ndifferent module.\n\n______________________________________________________________________\n\n### 11. `LLMBridge` (subclassing contract)\n\n**Source:** `mbridge/core/llm_bridge.py`\n\nSubclassed in `areal/models/mcore/bailing_moe_bridge.py` (line 87):\n\n```python\nclass BailingMoeBridge(LLMBridge):\n    TransformerConfigClass = MLATransformerConfig\n\n    def _build_config(self): ...\n    def _get_gptmodel_args(self) -> dict: ...\n    def _get_transformer_layer_spec(self, vp_stage: int | None = None): ...\n    def _weight_name_mapping_mcore_to_hf(self, mcore_weights_name: str) -> list[str]: ...\n    def _weight_merge_across_tp(self, mcore_weights_name, tp_shards, param): ...\n    def _weight_to_mcore_format(self, mcore_weights_name, hf_weights): ...\n    def _weight_to_hf_format(self, mcore_weights_name, mcore_weights): ...\n```\n\n**Check:** Confirm `LLMBridge` still exposes these overridable methods with compatible\nsignatures. Verify `TransformerConfigClass` class attribute is still supported. Check if\nany abstract methods were added that subclasses must now implement. Verify\n`_build_base_config(...)` still accepts the keyword arguments used in\n`BailingMoeBridge._build_config()` (especially MoE and MLA parameters). Check if\n`has_vp_stage` attribute is still read by the base class.\n\n______________________________________________________________________\n\n### 12. `register_model` decorator\n\n**Source:** `mbridge/core/__init__.py` (re-exported from `mbridge/core/llm_bridge.py` or\nsimilar)\n\nUsed in `areal/models/mcore/bailing_moe_bridge.py` (lines 84–86):\n\n```python\n@register_model(\"bailing_moe_v2\")\n@register_model(\"bailing_moe_linear\")\n@register_model(\"bailing_hybrid\")\nclass BailingMoeBridge(LLMBridge):\n    ...\n```\n\n**Check:** Confirm `register_model` is still importable from `mbridge.core`. Verify it\nstill accepts a single string argument (model type name). Verify it still works as a\nclass decorator that registers the bridge class for `AutoBridge.from_pretrained`\ndispatch. Check if the registration mechanism changed (e.g., from decorator to explicit\nregistry call).\n\n______________________________________________________________________\n\n### 13. `LLMBridge._get_transformer_layer_spec` (monkey-patch target)\n\n**Source:** `mbridge/core/llm_bridge.py`\n\nMonkey-patched in `areal/models/tree_attn/module_megatron.py` (lines 195–213):\n\n```python\noriginal_layer_spec_getter = LLMBridge._get_transformer_layer_spec\n\ndef _patched_getter(self, vp_stage: int | None = None):\n    spec: TransformerBlockSubmodules = original_layer_spec_getter(self, vp_stage)\n    for layer_spec in spec.layer_specs:\n        ...\n    return spec\n\nLLMBridge._get_transformer_layer_spec = _patched_getter\n```\n\n**Check:** Confirm `_get_transformer_layer_spec` still exists on `LLMBridge` with\nsignature `(self, vp_stage: int | None = None)`. Verify the return type is still\n`TransformerBlockSubmodules` with a `.layer_specs` attribute (list of layer specs). Each\nlayer spec must still have `.module`, `.submodules`, and\n`.submodules.self_attention.submodules.core_attention` that can be reassigned. If the\nmethod was renamed or its return structure changed, the monkey-patch will silently fail.\n\n______________________________________________________________________\n\n### 14. `Bridge.hf_config` and `Bridge.config` (properties)\n\n**Source:** `mbridge/core/bridge.py`\n\nAccessed throughout:\n\n- `bridge.hf_config` → HuggingFace `PretrainedConfig` instance (used in hf_load.py,\n  hf_save.py, registry.py, bailing_moe_bridge.py)\n- `bridge.config` → Megatron `TransformerConfig` instance (used in hf_save.py,\n  registry.py)\n\n```python\n# hf_load.py\nbridge.hf_config  # .num_key_value_heads, .hidden_size, .num_attention_heads, .quantization_config\nbridge.config     # .fp8, .fp8_param\nbridge.dtype      # torch.dtype\n\n# registry.py\nbridge.hf_config  # ._name_or_path\nbridge.config     # returned as TransformerConfig\n```\n\n**Check:** Confirm `hf_config` still returns a HuggingFace `PretrainedConfig` (or\nsubclass). Confirm `config` still returns a Megatron `TransformerConfig` with at least\n`.fp8`, `.fp8_param`, `.num_moe_experts` attributes. These are the most widely accessed\nproperties — any type change will cascade across all Primary files.\n\n______________________________________________________________________\n\n## Version-Guarded Code\n\n_None currently._ The `mbridge` package is pinned to a git commit (`310e8fb`) rather\nthan a versioned release, so no version-conditional code exists.\n",".agents/skills/upgrade-deps/checklists/megatron-bridge.md":"---\npackage: megatron-bridge\ngithub: NVIDIA-NeMo/Megatron-Bridge\nbranch_template: v${VERSION}\nupstream_paths:\n  - megatron/bridge/__init__.py\n  - megatron/bridge/models/conversion/auto_bridge.py\n  - megatron/bridge/models/conversion/param_mapping.py\n  - megatron/bridge/models/qwen_vl/modelling_qwen3_vl/text_model.py\n  - megatron/bridge/peft/lora.py\n---\n\n## Affected Files\n\n### Primary (engine layer — most likely to break)\n\n| File                                           | Imports / Usage                                                |\n| ---------------------------------------------- | -------------------------------------------------------------- |\n| `areal/engine/megatron_engine.py`              | `megatron.bridge.AutoBridge`, `megatron.bridge.peft.lora.LoRA` |\n| `areal/engine/megatron_utils/megatron_lora.py` | `megatron.bridge.AutoBridge` (inside function, monkey-patched) |\n\n### Secondary (model / infra layer)\n\n| File                                        | Imports / Usage                                                   |\n| ------------------------------------------- | ----------------------------------------------------------------- |\n| `areal/models/mcore/vocab_parallel_head.py` | `AutoMapping.register_module_type` for dynamic LM-head subclasses |\n\n### Tertiary (tests, config)\n\n| File                                          | Imports / Usage                                                                   |\n| --------------------------------------------- | --------------------------------------------------------------------------------- |\n| `areal/tools/validation_base.py`              | `\"megatron-bridge\"` → `\"megatron.bridge\"` in `PACKAGE_IMPORT_MAP` (metadata only) |\n| `areal/tools/validate_docker_installation.py` | Megatron Bridge package validation metadata                                       |\n| `tests/test_bailing_v3_nccl.py`               | Conditional Bridge availability and real conversion integration                   |\n| `tests/test_megatron_bridge_deterministic.py` | Bridge provider determinism integration                                           |\n| `tests/test_megatron_engine.py`               | Megatron engine integration through Bridge-backed paths                           |\n| `tests/test_megatron_transport.py`            | Conditional import of Bridge-backed Megatron engine                               |\n| `tests/test_megatron_lm_head.py`              | `AutoMapping` registry assertion for promoted LM-head subclasses                  |\n\n______________________________________________________________________\n\n## API Usage Catalog\n\nFor each function/class below, verify the call signature against the upstream source at\nthe target version. Focus on: **missing new required parameters**, **removed old\nparameters**, **renamed parameters**, **changed return types**, **changed method\nsignatures on returned objects**, and **moved/renamed modules**.\n\n### 1. `megatron.bridge.AutoBridge.from_hf_pretrained`\n\n**Source:** `megatron/bridge/models/conversion/auto_bridge.py`\n\nCalled in `areal/engine/megatron_engine.py` (line 430):\n\n```python\nself.bridge = MegatronBridgeAutoBridge.from_hf_pretrained(\n    self.config.path,\n    trust_remote_code=True,\n    dtype=self.config.dtype,\n)\n```\n\n**Check:** Confirm `trust_remote_code` and `dtype` are still accepted keyword arguments.\nVerify the first positional arg is still the model path. Verify the method still returns\na bridge object that exposes `save_hf_pretrained`, `load_hf_weights`, and (depending on\nversion) `save_hf_adapter`. Check for any new required parameters.\n\n______________________________________________________________________\n\n### 2. `megatron.bridge.AutoBridge.save_hf_pretrained`\n\n**Source:** `megatron/bridge/models/conversion/auto_bridge.py`\n\nCalled in `areal/engine/megatron_engine.py` (line 1561):\n\n```python\nbridge.save_hf_pretrained(model, path, source_path=base_model_path)\n```\n\n**Check:** Confirm `source_path` is still a valid keyword argument. Verify positional\norder of `model` and `path` hasn't changed. Check return type (currently void/`None`).\n\n______________________________________________________________________\n\n### 3. `megatron.bridge.AutoBridge.load_hf_weights`\n\n**Source:** `megatron/bridge/models/conversion/auto_bridge.py`\n\nCalled in `areal/engine/megatron_engine.py` (line 1595):\n\n```python\nbridge.load_hf_weights(model, hf_path=path)\n```\n\n**Check:** Confirm `hf_path` is still the correct keyword name. Verify `model` is still\nthe first positional argument. Check for newly added required arguments.\n\n______________________________________________________________________\n\n### 4. `megatron.bridge.AutoBridge.save_hf_adapter`\n\n**Source:** `megatron/bridge/models/conversion/auto_bridge.py`\n\nCalled in `areal/engine/megatron_engine.py` (lines 1554-1559) via the monkey-patched\nmethod on the bridge instance:\n\n```python\nself.bridge.save_hf_adapter(\n    self.model,\n    path=path,\n    peft_config=self.bridge_lora,\n    base_model_name_or_path=base_model_path or self.config.path,\n)\n```\n\n**Check:** If the new version adds this method natively, confirm its signature matches\nthe monkey-patch in `areal/engine/megatron_utils/megatron_lora.py` (line 189). The\nmonkey-patched signature is:\n\n```python\ndef save_hf_adapter(\n    self, model, path, peft_config, base_model_name_or_path=None, show_progress=True\n)\n```\n\nAny mismatch in parameter names or order will silently break adapter saving. Note that\n`peft_config` receives a `megatron.bridge.peft.lora.LoRA` instance (not a dict). See\nalso the [Version-Guarded Code](#version-guarded-code) section.\n\n______________________________________________________________________\n\n### 5. `megatron.bridge.AutoBridge.export_adapter_weights`\n\n**Source:** `megatron/bridge/models/conversion/auto_bridge.py`\n\nCalled inside the monkey-patched `save_hf_adapter` in\n`areal/engine/megatron_utils/megatron_lora.py` (lines 237-240):\n\n```python\nfor name, tensor in self.export_adapter_weights(\n    model, cpu=True, show_progress=show_progress\n):\n    adapter_state[f\"base_model.model.{name}\"] = tensor.clone().float()\n```\n\n**Check:** Confirm `cpu` and `show_progress` are still accepted. Verify the method\nyields `(name, tensor)` tuples (iterable of pairs). The names are module FQNs without\nthe `base_model.model.` prefix — that prefix is added by the caller. This is a native\nbridge method used inside the monkey-patch — if its return type or signature changes,\nthe patch breaks.\n\n______________________________________________________________________\n\n### 6. `megatron.bridge.peft.lora.LoRA`\n\n**Source:** `megatron/bridge/peft/lora.py`\n\nCalled in `areal/engine/megatron_engine.py` (line 233):\n\n```python\nbridge_lora = MegatronBridgeLoRA(\n    target_modules=target_modules,\n    dim=lora_rank,\n    alpha=lora_alpha,\n    dropout=0.0,\n)\n```\n\n**Check:** Confirm `dim` is still the rank parameter (not renamed to `r` or `rank`).\nVerify `alpha` and `dropout` are still accepted. Check the `target_modules` accepted\ntype (list of strings vs. regex).\n\n______________________________________________________________________\n\n### 7. `LoRA.__call__` (apply to model)\n\n**Source:** `megatron/bridge/peft/lora.py`\n\nCalled in `areal/engine/megatron_engine.py` (lines 239-240):\n\n```python\nself.model = _MegatronModelList(self.bridge_lora(self.model, training=True))\nself.bridge_lora.set_params_to_save(self.model)\n```\n\n**Check:** Confirm `LoRA` instances are still callable with `(model, training=...)`.\nVerify the return type — it returns a modified model (or list of model chunks), which is\nthen wrapped in `_MegatronModelList`. Confirm `set_params_to_save(model)` still exists\nand marks LoRA parameters for checkpoint saving. Check if `training=True` is still the\ncorrect keyword to enable grad on LoRA parameters.\n\n______________________________________________________________________\n\n### 8. Qwen3-VL model forward contract\n\n**Source:** `megatron/bridge/models/qwen_vl/modelling_qwen3_vl/text_model.py`\n\nCalled through `packed_context_parallel_forward` in\n`areal/engine/megatron_utils/packed_context_parallel.py`:\n\n```python\noutput = model(\n    input_ids=input_ids,\n    attention_mask=attention_mask,\n    position_ids=position_ids,\n    packed_seq_params=packed_seq_params,\n    loss_mask=mtp_loss_mask,\n)\n```\n\n**Check:** Verify `Qwen3VLModel.forward` and its `Qwen3VLGPTModel` decoder accept\n`loss_mask`, preserve `labels=None` logits output, and pass layout-aligned `input_ids`\nto MCore's native MTP path. Confirm the upstream shadow embedding exposes\n`word_embeddings`; AReaL no longer patches either class.\n\n______________________________________________________________________\n\n### 9. `AutoMapping.register_module_type`\n\n**Source:** `megatron/bridge/models/conversion/param_mapping.py`\n\nCalled after dynamically promoting an MCore output layer in\n`areal/models/mcore/vocab_parallel_head.py`:\n\n```python\nAutoMapping.register_module_type(output_layer.__class__.__name__, \"column\")\n```\n\n**Check:** Confirm the import path, classmethod signature, accepted mapping type\n(`\"column\"`), and registry lifetime. This mapping must allow Bridge to export dynamic\nAReAL LM-head subclasses without replacing their parameters or hooks.\n\n______________________________________________________________________\n\n## Version-Guarded Code\n\n- `areal/engine/megatron_utils/megatron_lora.py:185` —\n  `hasattr(AutoBridge, \"save_hf_adapter\")` guard. The monkey-patch at line 291 is only\n  applied when `save_hf_adapter` does not exist on `AutoBridge`. The module-level call\n  at line 298 (`_monkey_patch_save_hf_adapter()`) runs at import time, so the guard is\n  evaluated once on first import. If upgrading to a version that ships `save_hf_adapter`\n  natively, the guard will skip patching, but the native signature must then match what\n  AReaL's call site expects (see entry 4 above). Once confirmed compatible, the entire\n  function `_monkey_patch_save_hf_adapter()` and its line-298 invocation can be removed.\n",".agents/skills/upgrade-deps/checklists/megatron-core.md":"---\npackage: megatron-core\ngithub: NVIDIA/Megatron-LM\nbranch_template: core_v${VERSION}\nupstream_paths:\n  - megatron/core/parallel_state.py\n  - megatron/core/distributed/\n  - megatron/core/optimizer/\n  - megatron/core/optimizer_param_scheduler.py\n  - megatron/core/pipeline_parallel/\n  - megatron/core/tensor_parallel/\n  - megatron/core/transformer/transformer_config.py\n  - megatron/core/transformer/pipeline_parallel_layer_layout.py\n  - megatron/core/transformer/multi_token_prediction.py\n  - megatron/core/transformer/multi_latent_attention.py\n  - megatron/core/transformer/spec_utils.py\n  - megatron/core/transformer/transformer_block.py\n  - megatron/core/transformer/transformer_layer.py\n  - megatron/core/dist_checkpointing/\n  - megatron/core/dist_checkpointing/mapping.py\n  - megatron/core/dist_checkpointing/serialization.py\n  - megatron/core/dist_checkpointing/strategies/async_utils.py\n  - megatron/core/dist_checkpointing/strategies/fully_parallel.py\n  - megatron/core/fp8_utils.py\n  - megatron/core/models/gpt/\n  - megatron/core/packed_seq_params.py\n  - megatron/core/models/common/embeddings/rotary_pos_embedding.py\n  - megatron/core/utils.py\n---\n\n## Affected Files\n\n### Primary (engine layer — most likely to break)\n\n| File                                                     | Imports / Usage                                                                                                                                                                                                  |\n| -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `areal/engine/megatron_engine.py`                        | `parallel_state`, `tensor_parallel`, `DDP`, `finalize_model_grads`, `OptimizerConfig`, `get_megatron_optimizer`, `OptimizerParamScheduler`, `get_forward_backward_func`, `TransformerConfig`, `get_model_config` |\n| `areal/engine/megatron_utils/checkpointer.py`            | `dist_checkpointing.save`, `load`, sharded strategies, `ShardedObject`                                                                                                                                           |\n| `areal/engine/megatron_utils/megatron.py`                | `parallel_state`, FP8 detection, `TransformerConfig`                                                                                                                                                             |\n| `areal/engine/megatron_utils/pipeline_parallel.py`       | `TransformerConfig`, `PipelineParallelLayerLayout`                                                                                                                                                               |\n| `areal/engine/megatron_utils/packed_context_parallel.py` | `PackedSeqParams`, `parallel_state`                                                                                                                                                                              |\n| `areal/engine/megatron_utils/fp8/tensor_helper.py`       | `fp8_utils.is_float8tensor`                                                                                                                                                                                      |\n| `areal/engine/megatron_utils/deterministic.py`           | `AttnBackend` enum for deterministic attention selection                                                                                                                                                         |\n| `areal/engine/megatron_utils/weight_residency.py`        | `DistributedDataParallel` flat-buffer residency (`buffers`, `expert_parallel_buffers`)                                                                                                                           |\n\n### Secondary (model / infra layer)\n\n| File                                              | Imports / Usage                                                                                              |\n| ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |\n| `areal/models/mcore/registry.py`                  | `GPTModel`, `DDP`, `TransformerConfig`, `parallel_state`, tensor-parallel utilities                          |\n| `areal/models/mcore/bailing_moe.py`               | `MLATransformerConfig`, layer specs/builders, transformer blocks, TE/torch/fused norms                       |\n| `areal/models/mcore/bailing_v3.py`                | `MLATransformerConfig`, layer specs/builders, transformer blocks, TE/TP linears, TE/torch/fused norms        |\n| `areal/models/mcore/bailing_v3_bridge.py`         | `MLATransformerConfig`, `AttnBackend`, `parallel_state`                                                      |\n| `areal/models/mcore/bailing_v3_mla.py`            | `MLASelfAttention`, `build_module`, TE/TP column-parallel linear                                             |\n| `areal/models/mcore/lightning_attention.py`       | `parallel_state`, `apply_rotary_pos_emb`, `MegatronModule`, `ModuleSpec`, `build_module`, checkpoint helpers |\n| `areal/models/mcore/kda_attention.py`             | Sharded checkpoint types, `PackedSeqParams`, TP mappings/RNG, transformer module/spec/checkpoint helpers     |\n| `areal/models/mcore/hf_load.py`                   | `parallel_state`, `fp8_utils.is_float8tensor`                                                                |\n| `areal/models/mcore/hf_save.py`                   | `parallel_state`, `fp8_utils.is_float8tensor`                                                                |\n| `areal/models/mcore/vocab_parallel_head.py`       | Tensor-parallel layers and collectives                                                                       |\n| `areal/models/tree_attn/module_megatron.py`       | `PackedSeqParams`, `TransformerConfig`, `SelfAttention`, `AttnMaskType`, transformer layer/block specs       |\n| `areal/models/mcore/bailing_moe_bridge.py`        | `MLATransformerConfig`, `AttnBackend`                                                                        |\n| `areal/models/mcore/common.py`                    | `TransformerConfig`                                                                                          |\n| `areal/models/mcore/qwen3.py`                     | `gpt_layer_specs`, `TransformerConfig`                                                                       |\n| `areal/v2/weight_update/awex/delta_detect.py`     | Lazy `parallel_state` access for Megatron delta ownership                                                    |\n| `areal/v2/weight_update/awex/megatron_adapter.py` | Lazy `parallel_state` access for AWEX Megatron rank/group handling                                           |\n\n### Tertiary (tests, infra)\n\n| File                                                | Imports / Usage                                                   |\n| --------------------------------------------------- | ----------------------------------------------------------------- |\n| `areal/infra/workflow_executor.py`                  | conditional `parallel_state` for DP world size                    |\n| `tests/test_estimate_num_params.py`                 | `parallel_state`, `tensor_parallel`                               |\n| `tests/fp8/engine_utils.py`                         | `parallel_state`                                                  |\n| `tests/fp8/model_hooks.py`                          | `parallel_state`                                                  |\n| `tests/fp8/test_fp8_rmsnorm.py`                     | `fp8_utils`, `get_model_config`                                   |\n| `tests/torchrun/run_megatron_engine_distributed.py` | `parallel_state`                                                  |\n| `tests/test_megatron_engine_vlm.py`                 | `GPTModel` unwrapping and VLM engine contracts                    |\n| `tests/test_megatron_lm_head.py`                    | TP layers, `TransformerConfig`, `Float16Module`, PP helpers       |\n| `tests/test_reassemble_cp_logprobs.py`              | Conditional `megatron.core` availability                          |\n| `tests/torchrun/run_vocab_parallel.py`              | `parallel_state`, `ColumnParallelLinear`, `TransformerConfig`     |\n| `areal/tools/validation_base.py`                    | Megatron package/import metadata validation                       |\n| `areal/tools/validate_docker_installation.py`       | Megatron package and submodule smoke imports                      |\n| `tests/test_bailing_v3_hf_load.py`                  | Stubbed `parallel_state` and FP8 import contracts                 |\n| `tests/test_bailing_v3_kda_cp_helpers.py`           | Stubbed KDA checkpoint, packed-sequence, TP, and transformer APIs |\n| `tests/test_bailing_v3_nccl.py`                     | Conditional real MCore integration                                |\n| `tests/test_deterministic_prebuild.py`              | Conditional `AttnBackend` integration                             |\n| `tests/test_megatron_async_save.py`                 | Stubbed async distributed-checkpoint contracts                    |\n| `tests/test_megatron_bridge_deterministic.py`       | Conditional MCore bridge-provider integration                     |\n| `tests/test_megatron_engine.py`                     | Megatron engine integration and package version reporting         |\n| `tests/test_megatron_transport.py`                  | Conditional Megatron engine transport integration                 |\n| `tests/torchrun/run_mopd_teacher_residency.py`      | Patched MCore DDP flat-buffer residency                           |\n\n______________________________________________________________________\n\n## API Usage Catalog\n\nFor each function/class below, verify the call signature against the upstream source at\nthe target version. Focus on: **missing new required parameters**, **removed old\nparameters**, **renamed parameters**, **changed return types**, **changed method\nsignatures on returned objects**, and **moved/renamed modules**.\n\n### 1. `megatron.core.parallel_state.initialize_model_parallel`\n\n**Source:** `megatron/core/parallel_state.py`\n\nCalled in `areal/engine/megatron_engine.py` (line 195):\n\n```python\nmpu.initialize_model_parallel(\n    tensor_model_parallel_size=self.parallel_strategy.tensor_parallel_size,\n    pipeline_model_parallel_size=self.parallel_strategy.pipeline_parallel_size,\n    virtual_pipeline_model_parallel_size=vpp_size if vpp_size > 1 else None,\n    use_sharp=False,\n    order=\"tp-cp-ep-dp-pp\",\n    context_parallel_size=self.parallel_strategy.context_parallel_size,\n    expert_model_parallel_size=self.parallel_strategy.expert_parallel_size,\n    expert_tensor_parallel_size=self.parallel_strategy.expert_tensor_parallel_size,\n    distributed_timeout_minutes=int(DIST_GROUP_DEFAULT_TIMEOUT.seconds / 60),\n)\n```\n\n**Check:** Verify all keyword arguments still accepted — especially\n`expert_tensor_parallel_size` (recently added) and `distributed_timeout_minutes`. This\nis the most critical call — any signature change blocks ALL Megatron training. Confirm\n`order` string format `\"tp-cp-ep-dp-pp\"`.\n\n______________________________________________________________________\n\n### 2. `megatron.core.parallel_state` getter functions\n\n**Source:** `megatron/core/parallel_state.py`\n\nCalled extensively in `areal/engine/megatron_engine.py`, `areal/models/mcore/*.py`:\n\n```python\nmpu.get_data_parallel_rank()\nmpu.get_data_parallel_world_size()\nmpu.get_data_parallel_group()\nmpu.get_tensor_model_parallel_rank()\nmpu.get_tensor_model_parallel_world_size()\nmpu.get_tensor_model_parallel_group()\nmpu.get_pipeline_model_parallel_rank()\nmpu.get_pipeline_model_parallel_world_size()\nmpu.get_pipeline_model_parallel_group()\nmpu.get_context_parallel_rank()\nmpu.get_context_parallel_world_size()\nmpu.get_context_parallel_group()\nmpu.get_expert_model_parallel_rank()\nmpu.get_expert_model_parallel_world_size()\nmpu.is_pipeline_last_stage()\nmpu.is_pipeline_first_stage()\nmpu.model_parallel_is_initialized()\nmpu.destroy_model_parallel()\n```\n\n**Check:** Confirm all getter functions still exist with same no-arg signatures. These\nare called dozens of times across the codebase — any removal or rename is immediately\nfatal.\n\n______________________________________________________________________\n\n### 3. `megatron.core.parallel_state` utility functions\n\n**Source:** `megatron/core/parallel_state.py`\n\nCalled in `areal/engine/megatron_engine.py` (line 1048):\n\n```python\nrank_generator = mpu.RankGenerator(\n    tp=self.parallel_strategy.tensor_parallel_size,\n    ep=1,\n    dp=self.parallel_strategy.data_parallel_size,\n    pp=self.parallel_strategy.pipeline_parallel_size,\n    cp=self.parallel_strategy.context_parallel_size,\n    order=\"tp-cp-ep-dp-pp\",\n    rank_offset=0,\n)\ncontext_and_model_parallel_ranks = rank_generator.get_ranks(\"tp-cp-pp\")\n\ngroup = mpu.create_group(\n    ranks,\n    timeout=DIST_GROUP_DEFAULT_TIMEOUT,\n    pg_options=mpu.get_nccl_options(\"tp-cp-pp\", {}),\n    group_desc=\"CONTEXT_AND_MODEL_PARALLEL_GROUP\",\n)\n```\n\n**Check:** Verify `RankGenerator.__init__` keyword args — especially `rank_offset` and\n`order`. Confirm `get_ranks(group_str)` method. Verify `create_group` accepts `timeout`,\n`pg_options`, `group_desc` kwargs. Confirm `get_nccl_options(group_name, config_dict)`\nsignature.\n\n______________________________________________________________________\n\n### 4. `megatron.core.distributed.DistributedDataParallel`\n\n**Source:** `megatron/core/distributed/`\n\nCalled in `areal/models/mcore/registry.py` (line 310):\n\n```python\nfrom megatron.core.distributed import DistributedDataParallel as DDP\nfrom megatron.core.distributed import DistributedDataParallelConfig as MCoreDDPConfig\n\nddp_config = MCoreDDPConfig(**dataclasses.asdict(mcore_config.ddp))\nwrapped = DDP(\n    config=tf_config,\n    ddp_config=ddp_config,\n    module=model,\n    disable_bucketing=False,\n)\n```\n\n**Check:** Verify keyword arguments `config`, `ddp_config`, `module`,\n`disable_bucketing` are still accepted. Note AReaL does NOT pass `data_parallel_group`\nor `expert_data_parallel_group` — confirm these remain optional with sane defaults.\nCheck for new required parameters. Also verify `MCoreDDPConfig` dataclass fields haven't\nchanged (constructed from `mcore_config.ddp`).\n\n______________________________________________________________________\n\n### 5. `megatron.core.distributed.finalize_model_grads`\n\n**Source:** `megatron/core/distributed/`\n\nAssigned in `areal/engine/megatron_engine.py` (line 403):\n\n```python\nmodel_config.finalize_model_grads_func = finalize_model_grads\n```\n\nNot called directly by AReaL — instead assigned to\n`TransformerConfig.finalize_model_grads_func` and invoked internally by Megatron's\npipeline parallel forward-backward functions.\n\n**Check:** Confirm `finalize_model_grads` still has a compatible signature for\n`TransformerConfig.finalize_model_grads_func`. Verify it accepts `(model_chunks)` or\n`(model_chunks, num_tokens=...)` as expected by the pipeline executor.\n\n______________________________________________________________________\n\n### 6. `megatron.core.optimizer.OptimizerConfig`\n\n**Source:** `megatron/core/optimizer/`\n\nCalled in `areal/engine/megatron_engine.py` (line 1090):\n\n```python\nmcore_opt_config = MCoreOptimizerConfig(\n    optimizer=self.optimizer_config.type,\n    lr=self.optimizer_config.lr,\n    min_lr=self.optimizer_config.min_lr_ratio * self.optimizer_config.lr,\n    weight_decay=self.optimizer_config.weight_decay,\n    bf16=self.dtype is torch.bfloat16,\n    fp16=self.dtype is torch.float16,\n    adam_beta1=self.optimizer_config.beta1,\n    adam_beta2=self.optimizer_config.beta2,\n    adam_eps=self.optimizer_config.eps,\n    use_distributed_optimizer=use_distributed_optimizer,\n    params_dtype=self.dtype,\n    clip_grad=self.optimizer_config.gradient_clipping,\n    fp8_recipe=(self.fp8_config.recipe if self.enable_fp8 else None),\n)\n# Post-init field assignments:\nmcore_opt_config.overlap_param_gather_with_optimizer_step = ...\nmcore_opt_config.use_precision_aware_optimizer = ...\nmcore_opt_config.main_grads_dtype = getattr(torch, ...)\nmcore_opt_config.main_params_dtype = getattr(torch, ...)\nmcore_opt_config.exp_avg_dtype = getattr(torch, ...)\nmcore_opt_config.exp_avg_sq_dtype = getattr(torch, ...)\n```\n\n**Check:** Verify all ~13 constructor kwargs still accepted — especially `fp8_recipe`\nand `fp16`. Confirm the 6 post-init fields (`overlap_param_gather_with_optimizer_step`,\n`use_precision_aware_optimizer`, `main_grads_dtype`, `main_params_dtype`,\n`exp_avg_dtype`, `exp_avg_sq_dtype`) still exist on the dataclass. Check for renamed or\nremoved fields.\n\n______________________________________________________________________\n\n### 7. `megatron.core.optimizer.get_megatron_optimizer`\n\n**Source:** `megatron/core/optimizer/`\n\nCalled in `areal/engine/megatron_engine.py` (line 1122):\n\n```python\nself.optimizer = get_megatron_optimizer(mcore_opt_config, self.model)\n```\n\n**Check:** Verify the two-arg call `(config, model_chunks)` is still valid. Confirm\n`model_chunks` accepts a list of DDP-wrapped modules. Check if new required arguments\nwere added (e.g., `no_weight_decay_cond`, `scale_lr_cond` — AReaL does not pass these,\nso confirm they remain optional).\n\n______________________________________________________________________\n\n### 8. `megatron.core.optimizer_param_scheduler.OptimizerParamScheduler`\n\n**Source:** `megatron/core/optimizer_param_scheduler.py`\n\nCalled in `areal/engine/megatron_engine.py` (line 1126):\n\n```python\nlr_scheduler = OptimizerParamScheduler(\n    self.optimizer,\n    init_lr=0.0 if warmup_steps_proportion > 0 else self.optimizer_config.lr,\n    max_lr=self.optimizer_config.lr,\n    min_lr=self.optimizer_config.min_lr_ratio * self.optimizer_config.lr,\n    lr_warmup_steps=warmup_steps,\n    lr_decay_steps=ft_spec.total_train_steps - warmup_steps,\n    lr_decay_style=self.optimizer_config.lr_scheduler_type,\n    start_wd=self.optimizer_config.weight_decay,\n    end_wd=self.optimizer_config.weight_decay,\n    wd_incr_steps=ft_spec.total_train_steps,\n    wd_incr_style=\"constant\",\n)\n```\n\n**Check:** Confirm all keyword arguments still accepted — especially `start_wd`,\n`end_wd`, `wd_incr_steps`, `wd_incr_style` (weight decay scheduling). Verify\n`lr_decay_style` valid choices (e.g., `\"cosine\"`, `\"linear\"`, `\"WSD\"`). Check for\nrenamed params.\n\n______________________________________________________________________\n\n### 9. `megatron.core.pipeline_parallel.get_forward_backward_func`\n\n**Source:** `megatron/core/pipeline_parallel/`\n\nCalled in `areal/engine/megatron_engine.py` (line 732, hot training loop):\n\n```python\nforward_backward_func = get_forward_backward_func()\nforward_backward_func(\n    forward_step_func=forward_step,\n    data_iterator=data_iterator,\n    model=self.model if len(self.model) > 1 else self.model[0],\n    num_microbatches=len(mb_list),\n    seq_length=mb_list.max_seqlen,\n    micro_batch_size=1,\n    forward_only=forward_only,\n)\n```\n\n**Check:** Verify `get_forward_backward_func()` still returns a callable with this\nsignature. Confirm `seq_length` and `micro_batch_size` kwargs are still accepted (marked\n\"no use when input_shapes was set\" in code). This is in the HOT training loop — any\nbreakage stops all PP training.\n\n______________________________________________________________________\n\n### 10. `megatron.core.transformer.TransformerConfig`\n\n**Source:** `megatron/core/transformer/transformer_config.py`\n\nUsed in `areal/engine/megatron_engine.py`,\n`areal/engine/megatron_utils/pipeline_parallel.py`, `areal/models/mcore/registry.py`,\n`areal/models/mcore/common.py`, `areal/models/mcore/qwen3.py`:\n\n```python\ntransformer_config = TransformerConfig(\n    num_layers=num_layers,\n    hidden_size=hidden_size,\n    num_attention_heads=num_heads,\n    num_key_value_heads=num_kv_heads,\n    # ... many more fields\n)\n```\n\n**Check:** This is the central config dataclass — verify no fields were removed or\nrenamed. New optional fields with defaults are fine. Check `MLATransformerConfig`\nsubclass compatibility (used in `bailing_moe.py` and `bailing_moe_bridge.py`).\n\n______________________________________________________________________\n\n### 11. `megatron.core.dist_checkpointing` save/load\n\n**Source:** `megatron/core/dist_checkpointing/`\n\nCalled in `areal/engine/megatron_utils/checkpointer.py` (lines 23-32, 53-85):\n\n```python\nfrom megatron.core import dist_checkpointing\nfrom megatron.core.dist_checkpointing.mapping import ShardedObject\nfrom megatron.core.dist_checkpointing.serialization import (\n    get_default_save_sharded_strategy,\n    get_default_load_sharded_strategy,\n)\nfrom megatron.core.dist_checkpointing.strategies.fully_parallel import (\n    FullyParallelLoadStrategyWrapper,\n    FullyParallelSaveStrategyWrapper,\n)\n\n# Save (line 62):\nsave_strategy = get_default_save_sharded_strategy(\"torch_dist\")\nsave_strategy = FullyParallelSaveStrategyWrapper(save_strategy, dp_group)\ndist_checkpointing.save(\n    sharded_state_dict, ckpt_path,\n    sharded_strategy=save_strategy,\n    async_sharded_save=async_save,\n    validate_access_integrity=validate_sharding_integrity,\n)\n\n# Load (line 75):\nload_strategy = get_default_load_sharded_strategy(ckpt_dir)\nload_strategy = FullyParallelLoadStrategyWrapper(load_strategy, dp_group)\nstate_dict = dist_checkpointing.load(\n    sharded_state_dict, ckpt_dir, sharded_strategy=load_strategy\n)\n```\n\n**Check:** Verify `save` kwargs — especially `async_sharded_save` and\n`validate_access_integrity`. Confirm `get_default_save_sharded_strategy` accepts\n`\"torch_dist\"` string. Confirm `get_default_load_sharded_strategy` accepts `ckpt_dir`\npath. Verify import paths: `serialization` for strategy factories,\n`strategies.fully_parallel` for wrappers, `mapping` for `ShardedObject`. Checkpoint\nformat compatibility is critical — verify no format version bumps.\n\n______________________________________________________________________\n\n### 12. `megatron.core.transformer.PipelineParallelLayerLayout`\n\n**Source:** `megatron/core/transformer/pipeline_parallel_layer_layout.py`\n\nCalled in `areal/engine/megatron_utils/pipeline_parallel.py` (line 6):\n\n```python\nfrom megatron.core.transformer.pipeline_parallel_layer_layout import (\n    PipelineParallelLayerLayout,\n)\n```\n\n**Check:** Confirm the import path is still\n`megatron.core.transformer.pipeline_parallel_layer_layout` (not\n`transformer_config.py`). Verify constructor kwargs. Check if\n`get_num_layers_to_build()` and `get_transformer_layer_offset()` are still accessible.\n\n______________________________________________________________________\n\n### 13. `megatron.core.fp8_utils.is_float8tensor`\n\n**Source:** `megatron/core/fp8_utils.py`\n\nCalled in `areal/engine/megatron_utils/fp8/tensor_helper.py`:\n\n```python\nfrom megatron.core.fp8_utils import is_float8tensor\nif is_float8tensor(param):\n    ...\n```\n\n**Check:** Confirm function still exists at this path. Verify it accepts a single tensor\nargument. Check if FP8 support was restructured (e.g., moved to\n`megatron.core.extensions.transformer_engine`).\n\n______________________________________________________________________\n\n### 14. `megatron.core.models.gpt.GPTModel`\n\n**Source:** `megatron/core/models/gpt/`\n\nCalled in `areal/models/mcore/registry.py`:\n\n```python\nmodel = GPTModel(\n    config=transformer_config,\n    transformer_layer_spec=layer_spec,\n    vocab_size=vocab_size,\n    max_sequence_length=max_seq_len,\n    pre_process=pre_process,\n    post_process=post_process,\n)\n```\n\n**Check:** Verify constructor kwargs. Confirm `get_gpt_decoder_block_spec()` still\nreturns a valid layer spec. Check for new required parameters.\n\n______________________________________________________________________\n\n### 15. `megatron.core.tensor_parallel` utilities\n\n**Source:** `megatron/core/tensor_parallel/`\n\nCalled in `areal/engine/megatron_engine.py` and\n`areal/models/mcore/lightning_attention.py`; extended by\n`areal/models/mcore/vocab_parallel_head.py`:\n\n```python\ntensor_parallel.model_parallel_cuda_manual_seed(seed)\ntensor_parallel.gather_from_sequence_parallel_region(output)\ntensor_parallel.get_cuda_rng_tracker()\nlinear_with_areal_output(..., gtp_remat_size=gtp_remat_size)\n```\n\n**Check:** Verify these functions still exist with same signatures.\n`model_parallel_cuda_manual_seed` is called once at init;\n`gather_from_sequence_parallel_region` is in hot path. Keep the custom LM-head autograd\nfunction's forward/backward arity aligned with\n`LinearWithGradAccumulationAndAsyncCommunication`, including `gtp_remat_size`.\n\n______________________________________________________________________\n\n### 16. Transformer layer specs and module builders\n\n**Source:** `megatron/core/transformer/`\n\nCalled in `areal/models/mcore/bailing_moe.py`,\n`areal/models/mcore/lightning_attention.py`,\n`areal/models/mcore/tree_attn/module_megatron.py`:\n\n```python\nfrom megatron.core.transformer import ModuleSpec, build_module\nfrom megatron.core.transformer.enums import AttnMaskType, LayerType\nfrom megatron.core.transformer.transformer_layer import (\n    TransformerLayer, TransformerLayerSubmodules,\n)\nfrom megatron.core.transformer.transformer_block import TransformerBlockSubmodules\nfrom megatron.core.transformer.attention import SelfAttention\nfrom megatron.core.transformer.custom_layers.transformer_engine import (\n    # TE norm modules\n)\n```\n\n**Check:** Verify enum values in `AttnMaskType` and `LayerType`. Confirm `ModuleSpec`\nand `build_module` signatures. Check `SelfAttention.__init__` for new required params.\nVerify TE integration module paths haven't been restructured.\n\n______________________________________________________________________\n\n### 17. `PackedSeqParams`\n\n**Source:** `megatron/core/packed_seq_params.py` (or `megatron/core/transformer/`)\n\nCalled in `areal/engine/megatron_utils/packed_context_parallel.py`:\n\n```python\nfrom megatron.core.packed_seq_params import PackedSeqParams\npacked_seq_params = PackedSeqParams(\n    cu_seqlens_q=cu_seqlens_q,\n    cu_seqlens_kv=cu_seqlens_kv,\n    max_seqlen_q=max_seqlen_q,\n    max_seqlen_kv=max_seqlen_kv,\n    qkv_format=\"thd\",\n)\n```\n\n**Check:** Confirm constructor kwargs and import path. Verify `qkv_format` valid\nchoices.\n\n______________________________________________________________________\n\n### 18. RoPE utilities\n\n**Source:** `megatron/core/models/common/embeddings/`\n\nCalled in `areal/models/mcore/bailing_moe.py`,\n`areal/models/mcore/lightning_attention.py`:\n\n```python\nfrom megatron.core.models.common.embeddings.rotary_pos_embedding import apply_rotary_pos_emb\n# Also various rope_utils for extended RoPE (YaRN, etc.)\n```\n\n**Check:** Verify `apply_rotary_pos_emb` signature (`t`, `freqs`) and the optional MLA\nrotary arguments. MCore 0.19 owns THD+CP frequency selection; AReaL must not reinstall\nthe removed global MLA RoPE workaround.\n\n______________________________________________________________________\n\n### 19. Async distributed-checkpoint request and queue internals\n\n**Source:** `megatron/core/dist_checkpointing/strategies/async_utils.py`\n\nUsed in `areal/engine/megatron_utils/checkpointer.py`:\n\n```python\nfrom megatron.core.dist_checkpointing.strategies.async_utils import (\n    AsyncCallsQueue,\n    AsyncRequest,\n)\n\nsave_kwargs[\"async_strategy\"] = \"mcore\"\nasync_request = dist_checkpointing.save(**save_kwargs)\nqueue = AsyncCallsQueue()\nqueue.schedule_async_request(async_request)\n```\n\nThe checkpoint manager also validates and clears the retained tensor payload through the\nprivate `AsyncRequest.async_fn_args` write-bucket layout after D2H staging.\n\n**Check:** Confirm `AsyncRequest` and `AsyncCallsQueue` import paths and methods. Verify\n`dist_checkpointing.save(..., async_strategy=\"mcore\")` still returns an `AsyncRequest`,\nand audit the exact `async_fn_args` / write-bucket payload structure before retaining\nthe private-reference cleanup optimization.\n\n______________________________________________________________________\n\n### 20. MTP model, loss, and metrics APIs\n\n**Source:** `megatron/core/models/gpt/gpt_model.py` and\n`megatron/core/transformer/multi_token_prediction.py`\n\nConfigured in `areal/models/mcore/registry.py`, forwarded by\n`areal/engine/megatron_utils/packed_context_parallel.py`, and collected in\n`areal/engine/megatron_engine.py`:\n\n```python\nprovider.mtp_detach_heads = True\nmodel(..., labels=None, loss_mask=mtp_loss_mask)\nMTPLossLoggingHelper.reduce_metrics_in_tracker()\nloss = MTPLossLoggingHelper.tracker[\"loss_values\"]\nMTPLossLoggingHelper.clean_metrics_in_tracker()\n```\n\n**Check:** Verify `GPTModel.forward` forwards `input_ids` and `loss_mask` to native\n`process_mtp_loss` when `labels=None`; `mtp_detach_heads` must isolate both hidden state\nand tied/untied output weights. Verify the `MTPLossLoggingHelper` tracker keys and\nreduce/clean method names. These are semi-private contracts and changed in 0.19.\n\n______________________________________________________________________\n\n### 21. MLA configuration, attention backend, and layer-layout helpers\n\n**Source:** `megatron/core/transformer/multi_latent_attention.py`,\n`megatron/core/transformer/enums.py`, `transformer_block.py`, and `transformer_layer.py`\n\nUsed by the Bailing model family and pipeline layout helpers:\n\n```python\nfrom megatron.core.transformer.multi_latent_attention import (\n    MLASelfAttention,\n    MLATransformerConfig,\n)\nfrom megatron.core.transformer.enums import AttnBackend, LayerType\nfrom megatron.core.transformer.transformer_block import get_num_layers_to_build\nfrom megatron.core.transformer.transformer_layer import get_transformer_layer_offset\n```\n\n**Check:** Confirm imports and constructor signatures, enum members used by AReaL, and\nthe helper return types. Verify `MLASelfAttention` still exposes the overridable methods\nconsumed by `bailing_v3_mla.py`.\n\n______________________________________________________________________\n\n### 22. Custom attention module and sharded-state helpers\n\n**Source:** `megatron/core/dist_checkpointing/mapping.py`,\n`megatron/core/tensor_parallel/mappings.py`, and `megatron/core/transformer/`\n\nUsed by `areal/models/mcore/kda_attention.py` and the custom attention modules:\n\n```python\nfrom megatron.core.dist_checkpointing import ShardedTensor\nfrom megatron.core.dist_checkpointing.mapping import ReplicaId, ShardedTensorFactory\nfrom megatron.core.tensor_parallel.mappings import (\n    gather_from_tensor_model_parallel_region,\n    scatter_to_sequence_parallel_region,\n)\nfrom megatron.core.transformer.spec_utils import ModuleSpec, build_module\nfrom megatron.core.transformer.utils import (\n    make_sharded_tensors_for_checkpoint,\n    sharded_state_dict_default,\n)\n```\n\n**Check:** Verify import paths, factory/dataclass fields, TP collective signatures, and\nthe sharded-state helper inputs/returns. Confirm the optional\n`PackedSeqParamsWithSeqidx` import remains safely absent or compatible upstream.\n\n______________________________________________________________________\n\n### 23. `Float16Module` and pipeline-stage helpers\n\n**Source:** `megatron/core/transformer/module.py` and\n`megatron/core/pipeline_parallel/utils.py`\n\nExercised in `tests/test_megatron_lm_head.py` to preserve output casting:\n\n```python\nmodel = Float16Module(config, module)\noutput = model(fp32_output=False)\n```\n\nThe test also patches `is_pp_first_stage`, `is_pp_last_stage`, `is_vp_first_stage`, and\n`is_vp_last_stage` with their process-group/stage arguments.\n\n**Check:** Verify the `Float16Module` constructor/forward pass-through behavior and the\npipeline helper signatures so the AReAL LM-head output override is still propagated.\n\n______________________________________________________________________\n\n## Version-Guarded Code\n\nNo known version-guarded code exists in AReaL for `megatron-core`. However, the\n`MLATransformerConfig` class used in `bailing_moe.py` and `bailing_moe_bridge.py` is a\nrelatively new addition — confirm it exists in the target version.\n",".agents/skills/upgrade-deps/checklists/peft.md":"---\npackage: peft\ngithub: huggingface/peft\nbranch_template: v${VERSION}\nupstream_paths:\n  - src/peft/peft_model.py\n  - src/peft/tuners/lora/config.py\n  - src/peft/utils/peft_types.py\n---\n\n## Affected Files\n\n### Primary (engine layer — most likely to break)\n\n| File                          | Imports / Usage                            |\n| ----------------------------- | ------------------------------------------ |\n| `areal/engine/fsdp_engine.py` | `LoraConfig`, `TaskType`, `get_peft_model` |\n\n### Secondary (model / infra layer)\n\n| File                                             | Imports / Usage                                                                                                                                                     |\n| ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `areal/engine/megatron_engine.py`                | indirect — consumes PEFT config dict schema (`r`, `lora_alpha`, `target_modules`, `bias`) via `megatron.bridge.peft.lora`; stores in `WeightUpdateMeta.peft_config` |\n| `areal/engine/megatron_utils/megatron_lora.py`   | no direct import — generates `adapter_config.json` in PEFT format; parses PEFT weight key format (`base_model.model.layers.N.self_attn.q_proj.lora_A.weight`)       |\n| `areal/engine/vllm_ext/vllm_worker_extension.py` | indirect — uses `vllm.lora.peft_helper.PEFTHelper`, not direct peft import                                                                                          |\n| `areal/engine/vllm_remote.py`                    | builds HTTP payloads containing `peft_config` dict for distributed LoRA updates                                                                                     |\n| `areal/api/cli_args.py`                          | config fields only: `use_lora`, `lora_rank`, `lora_alpha`, `target_modules`, `peft_type`                                                                            |\n| `areal/api/io_struct.py`                         | `WeightUpdateMeta.peft_config: dict` — schema `{\"r\": rank, \"lora_alpha\": alpha, \"target_modules\": [...], \"bias\": \"none\"}`                                           |\n\n### Tertiary (tests, config)\n\n| File                                               | Imports / Usage                                                                     |\n| -------------------------------------------------- | ----------------------------------------------------------------------------------- |\n| `tests/torchrun/run_fsdp_memory_efficient_lora.py` | integration test with `use_lora=True, lora_rank=8, lora_alpha=16, peft_type=\"lora\"` |\n\n______________________________________________________________________\n\n## API Usage Catalog\n\nFor each function/class below, verify the call signature against the upstream source at\nthe target version. Focus on: **missing new required parameters**, **removed old\nparameters**, **renamed parameters**, **changed return types**, **changed method\nsignatures on returned objects**, and **moved/renamed modules**.\n\n### 1. `peft.LoraConfig` and `peft.TaskType.CAUSAL_LM`\n\n**Source:** `src/peft/tuners/lora/config.py`, `src/peft/utils/peft_types.py`\n\nCalled in `areal/engine/fsdp_engine.py` (`_apply_peft_wrapper`, lines 930–946):\n\n```python\nfrom peft import LoraConfig, TaskType, get_peft_model\n\npeft_config = {\n    \"task_type\": TaskType.CAUSAL_LM,\n    \"r\": config.lora_rank,\n    \"lora_alpha\": config.lora_alpha,\n    \"target_modules\": target_modules,  # str \"all-linear\" or list[str]\n    \"bias\": \"none\",\n}\nif self.config.peft_type == \"lora\":\n    peft_config = LoraConfig(**peft_config)\nelse:\n    raise NotImplementedError()\n```\n\n**Check:** Verify `LoraConfig.__init__` still accepts `task_type`, `r`, `lora_alpha`,\n`target_modules`, and `bias` as keyword arguments with the same types. Check whether any\nnew required parameters were added. Confirm `bias=\"none\"` is still a valid string choice\n(not renamed or replaced with an enum). Confirm `target_modules` still accepts both a\nstring (`\"all-linear\"`) and a `list[str]`. Verify `TaskType` is still an enum importable\nfrom `peft` and `TaskType.CAUSAL_LM` still exists. Verify the class is still importable\ndirectly from `peft` (not moved to `peft.tuners.lora` only).\n\n______________________________________________________________________\n\n### 2. `peft.get_peft_model`\n\n**Source:** `src/peft/peft_model.py`\n\nCalled in `areal/engine/fsdp_engine.py` (`_apply_peft_wrapper`, lines 948–953), after\n`model.enable_input_require_grads()` and before FSDP sharding:\n\n```python\nself.model.enable_input_require_grads()\nself.model = get_peft_model(\n    self.model,\n    peft_config,\n    autocast_adapter_dtype=False,\n)\n```\n\n**Check:** Confirm `get_peft_model` is still exported at the top-level `peft` namespace.\nVerify its signature is `get_peft_model(model, peft_config, ...)` with\n`autocast_adapter_dtype` still an accepted keyword argument (it was added in a mid-cycle\nrelease and could be renamed or removed). Confirm the return type is still a `PeftModel`\nwrapping the original model. Check whether the returned model's `.parameters()`,\n`.named_modules()`, and FSDP-compatibility hooks remain intact — the call site\nimmediately passes the result into FSDP2 sharding.\n\n______________________________________________________________________\n\n### 3. PEFT weight key format and `adapter_config.json` schema (indirectly consumed)\n\n**Source:** `src/peft/tuners/lora/` (weight saving conventions)\n\nParsed in `areal/engine/megatron_utils/megatron_lora.py`\n(`_infer_target_modules_from_adapter_weights`, lines 124–150):\n\n```python\n# Expected key pattern for LoRA weights:\n# \"base_model.model.layers.0.self_attn.q_proj.lora_A.weight\" -> q_proj\n# \"base_model.model.layers.1.mlp.gate_proj.lora_B.weight\"   -> gate_proj\nfor key in weight_keys:\n    key = key.replace(\"base_model.model.\", \"\")\n    if \".lora_A.weight\" in key:\n        module_name = key.replace(\".lora_A.weight\", \"\").split(\".\")[-1]\n    elif \".lora_B.weight\" in key:\n        module_name = key.replace(\".lora_B.weight\", \"\").split(\".\")[-1]\n```\n\nAlso used in `_build_adapter_config_dict` (lines 153–178) to emit an\n`adapter_config.json`:\n\n```python\n{\n    \"base_model_name_or_path\": base_model_name_or_path,\n    \"peft_type\": \"LORA\",\n    \"task_type\": \"CAUSAL_LM\",\n    \"inference_mode\": False,\n    \"r\": peft_config.dim,\n    \"lora_alpha\": peft_config.alpha,\n    \"lora_dropout\": peft_config.dropout,\n    \"target_modules\": target_modules,\n    \"bias\": \"none\",\n    \"fan_in_fan_out\": False,\n    \"modules_to_save\": None,\n    \"init_lora_weights\": True,\n    \"layers_to_transform\": None,\n    \"layers_pattern\": None,\n}\n```\n\n**Check:** Confirm the saved weight key format (`base_model.model.<...>.lora_A.weight` /\n`lora_B.weight`) hasn't changed. Check whether new LoRA variants (e.g.,\n`lora_magnitude_vector` for DoRA) added extra keys that the parser doesn't handle.\nVerify all `adapter_config.json` fields (`peft_type`, `task_type`, `r`, `lora_alpha`,\n`lora_dropout`, `target_modules`, `bias`, `fan_in_fan_out`, etc.) still match what\nPEFT's `LoraConfig.from_pretrained()` expects when loading. This schema is also used for\nvLLM LoRA hot-swap via `WeightUpdateMeta.peft_config`.\n\n______________________________________________________________________\n\n### 4. `WeightUpdateMeta.peft_config` dict schema (cross-engine contract)\n\n**Source:** PEFT conventions (not a direct import, but a schema contract)\n\nPopulated in `areal/engine/megatron_engine.py` (lines 1196–1203) and\n`areal/engine/fsdp_engine.py` (lines 1148–1153):\n\n```python\nmeta.peft_config = {\n    \"r\": self.config.lora_rank,\n    \"lora_alpha\": self.config.lora_alpha,\n    \"target_modules\": target_modules,  # list[str] or str\n    \"bias\": \"none\",\n}\n```\n\nConsumed in `areal/engine/vllm_remote.py` (lines 169–172):\n\n```python\n\"lora_target_modules\": meta.peft_config[\"target_modules\"],\n\"lora_rank\": meta.peft_config[\"r\"],\n\"lora_alpha\": meta.peft_config[\"lora_alpha\"],\n\"lora_bias\": meta.peft_config[\"bias\"],\n```\n\nAnd in `areal/engine/vllm_ext/vllm_worker_extension.py` (lines 243–246) to reconstruct a\n`PEFTHelper` for vLLM's LoRA manager.\n\n**Check:** This dict is the **contract** between AReaL's training engines and inference\nbackends. If PEFT changes its config field names (`r` → `rank`, `lora_alpha` → `alpha`,\netc.), all three sites must be updated in sync. Verify that vLLM's\n`PEFTHelper.from_dict` still accepts the same key names as what PEFT's `LoraConfig`\nuses.\n\n______________________________________________________________________\n\n## Version-Guarded Code\n\nNo known version-guarded code exists in AReaL for `peft`.\n",".agents/skills/upgrade-deps/checklists/sglang.md":"---\npackage: sglang\ngithub: sgl-project/sglang\nbranch_template: v${VERSION}\nupstream_paths:\n  - python/sglang/srt/entrypoints/engine.py\n  - python/sglang/srt/entrypoints/http_server.py\n  - python/sglang/srt/entrypoints/openai/protocol.py\n  - python/sglang/srt/function_call/function_call_parser.py\n  - python/sglang/srt/function_call/core_types.py\n  - python/sglang/srt/managers/scheduler.py\n  - python/sglang/srt/managers/io_struct.py\n  - python/sglang/srt/parser/reasoning_parser.py\n  - python/sglang/srt/server_args.py\n  - python/sglang/srt/utils/__init__.py\n  - python/sglang/srt/utils/common.py\n  - python/sglang/srt/utils/network.py\n  - python/sglang/srt/utils/numa_utils.py\n  - python/sglang/launch_server.py\n---\n\n## Affected Files\n\n### Primary (engine layer — most likely to break)\n\n| File                                                 | Imports / Usage                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |\n| ---------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `areal/engine/sglang_remote.py`                      | HTTP-only (no Python imports). Sends requests to `/generate`, `/load_lora_adapter`, `/update_weights_from_disk`, `/update_weights_from_distributed`, `/init_weights_update_group`, `/pause_generation`, `/continue_generation`, `/health`, `/release_memory_occupation`, `/resume_memory_occupation`. Parses `meta_info`, `finish_reason`, `output_token_logprobs`, `routed_experts` from JSON responses.                                                                                                                   |\n| `areal/experimental/openai/tool_call_parser.py`      | `sglang.srt.entrypoints.openai.protocol.Function`, `sglang.srt.entrypoints.openai.protocol.Tool`, `sglang.srt.function_call.function_call_parser.FunctionCallParser`, `sglang.srt.parser.reasoning_parser.ReasoningParser`                                                                                                                                                                                                                                                                                                  |\n| `areal/v2/inference_service/sglang/launch_server.py` | `sglang.srt.entrypoints.engine.Engine`, `sglang.srt.entrypoints.engine.init_tokenizer_manager`, `sglang.srt.entrypoints.http_server._execute_server_warmup`, `sglang.srt.entrypoints.http_server._setup_and_run_http_server`, `sglang.srt.entrypoints.http_server.app`, `sglang.srt.managers.detokenizer_manager.run_detokenizer_process`                                                                                                                                                                                   |\n| `areal/v2/inference_service/sglang/scheduler.py`     | `sglang.srt.managers.scheduler.Scheduler`, `sglang.srt.managers.scheduler.configure_scheduler`, `sglang.srt.observability.trace.process_tracing_init`, `sglang.srt.observability.trace.trace_set_thread_info`, `sglang.srt.utils.get_bool_env_var`, `sglang.srt.utils.kill_itself_when_parent_died`, `sglang.srt.utils.set_gpu_proc_affinity`, `sglang.srt.utils.numa_utils.get_numa_node_if_available`, `sglang.srt.utils.numa_utils.numa_bind_to_node`, `sglang.srt.environ.envs`, `sglang.utils.get_exception_traceback` |\n| `areal/v2/inference_service/sglang/rpc_proxy.py`     | `sglang.srt.managers.io_struct.RpcReqInput`, `sglang.srt.managers.io_struct.RpcReqOutput`, `sglang.srt.server_args.PortArgs`, `sglang.srt.utils.network.get_zmq_socket`                                                                                                                                                                                                                                                                                                                                                     |\n| `areal/v2/inference_service/sglang/bridge.py`        | HTTP-only (same protocol as `sglang_remote.py`). `/generate` request/response, `/pause_generation`, `/continue_generation`, `/release_memory_occupation`, `/resume_memory_occupation`.                                                                                                                                                                                                                                                                                                                                      |\n\n### Secondary (model / infra layer)\n\n| File                                                  | Imports / Usage                                                                                                                      |\n| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |\n| `areal/infra/launcher/sglang_server.py`               | HTTP-only. Polls `/v1/models` to confirm server readiness. Calls `SGLangConfig.build_cmd()` to construct the server launch command.  |\n| `areal/api/cli_args.py`                               | `SGLangConfig.build_cmd()`, `SGLangConfig.build_cmd_from_args()`, `SGLangConfig.build_args()`. Version check against `0.5.10.post1`. |\n| `areal/trainer/rl_trainer.py`                         | `SGLangConfig.build_args()` to construct `RemoteSGLangEngine`.                                                                       |\n| `areal/infra/launcher/ray.py`                         | `SGLangConfig` import + `to_structured_cfg`.                                                                                         |\n| `areal/infra/launcher/local.py`                       | `SGLangConfig` import + `to_structured_cfg`.                                                                                         |\n| `areal/infra/launcher/slurm.py`                       | `SGLangConfig` import + `to_structured_cfg`.                                                                                         |\n| `areal/v2/inference_service/controller/controller.py` | `SGLangConfig` in gateway controller.                                                                                                |\n| `areal/v2/inference_service/data_proxy/backend.py`    | HTTP protocol implementation for inference service proxy (SGLangBridgeBackend).                                                      |\n\n### Tertiary (tests, config)\n\n| File                                                 | Imports / Usage                                             |\n| ---------------------------------------------------- | ----------------------------------------------------------- |\n| `tests/grpo/test_grpo.py`                            | Test parameterization with `sglang` backend.                |\n| `tests/experimental/openai/test_tool_call_parser.py` | `FunctionCallParser` / `ReasoningParser` integration tests. |\n\n______________________________________________________________________\n\n## API Usage Catalog\n\nFor each function/class below, verify the call signature against the upstream source at\nthe target version. Focus on: **missing new required parameters**, **removed old\nparameters**, **renamed parameters**, **changed return types**, **changed method\nsignatures on returned objects**, and **moved/renamed modules**.\n\n> **Note:** Most sglang integration in AReaL is HTTP-based — the server is launched as a\n> subprocess and communicated with over REST. Only `areal/experimental/openai/` imports\n> the Python SDK directly. For the HTTP-based call sites, the risk is in **JSON field\n> renames** in request payloads and response bodies, not in Python API signature\n> changes.\n\n______________________________________________________________________\n\n### 1. `POST /generate` — request payload\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py` (request handler)\n\nCalled in `areal/engine/sglang_remote.py` (`SGLangBackend.build_generation_request`,\nlines 42–88):\n\n```python\nsample_params = {\n    \"top_p\": gconfig.top_p,\n    \"top_k\": gconfig.top_k,\n    \"max_new_tokens\": gconfig.max_new_tokens,\n    \"temperature\": 0.0 if gconfig.greedy else gconfig.temperature,\n    \"stop_token_ids\": stop_token_ids,\n    \"ignore_eos\": gconfig.ignore_eos,\n    \"skip_special_tokens\": gconfig.skip_special_tokens,\n    \"frequency_penalty\": gconfig.frequency_penalty,\n}\nif stop:\n    sample_params[\"stop\"] = stop\n\npayload = {\n    \"input_ids\": req.input_ids.copy(),\n    \"image_data\": req.image_data,\n    \"sampling_params\": sample_params,\n    \"return_logprob\": True,\n    \"stream\": False,\n}\n# conditionally added: \"return_routed_experts\": True, \"lora_path\": <str>\n```\n\n**Check:** Confirm `input_ids`, `image_data`, `sampling_params`, `return_logprob`,\n`lora_path`, and `return_routed_experts` are still accepted top-level fields. Verify all\n`sampling_params` sub-keys are unchanged (`top_p`, `top_k`, `max_new_tokens`,\n`temperature`, `stop_token_ids`, `ignore_eos`, `skip_special_tokens`,\n`frequency_penalty`, `stop`). Check whether `stream=False` is still the correct way to\nrequest a non-streaming response.\n\n______________________________________________________________________\n\n### 2. `POST /generate` — response parsing\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py` (response schema)\n\nParsed in `areal/engine/sglang_remote.py` (`SGLangBackend.parse_generation_response`,\nlines 90–126):\n\n```python\nmeta_info = response[\"meta_info\"]\nfinish_reason = meta_info[\"finish_reason\"]\nstop_reason = finish_reason[\"type\"]\nstop_message = finish_reason.get(\"message\", \"\")\nrouted_experts = meta_info.get(\"routed_experts\", None)\nif routed_experts is not None:\n    num_sgl_token = meta_info[\"prompt_tokens\"] + meta_info[\"completion_tokens\"] - 1\n    routed_experts = np.frombuffer(\n        pybase64.b64decode(routed_experts.encode(\"utf-8\")), dtype=np.int32\n    ).reshape(num_sgl_token, -1)\noutput_tokens = [x[1] for x in meta_info[\"output_token_logprobs\"]]\noutput_logprobs = [x[0] for x in meta_info[\"output_token_logprobs\"]]\n```\n\n**Check:** Confirm `meta_info` is still a top-level key. Verify `finish_reason` is still\na dict with a `\"type\"` key (not a plain string). Verify `output_token_logprobs` is still\na list of `[logprob, token_id]` pairs. Check that `prompt_tokens` and\n`completion_tokens` are still present when `return_logprob=True`. Confirm\n`routed_experts` is still base64-encoded int32 that can be decoded via\n`pybase64.b64decode` and reshaped to `(num_tokens, num_layers*expert_top_k)`.\n\n______________________________________________________________________\n\n### 3. `POST /load_lora_adapter` — request payload\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py`\n\nCalled in `areal/engine/sglang_remote.py`\n(`SGLangBackend.build_disk_weight_update_requests`, lines 139–144):\n\n```python\nHttpRequest(\n    endpoint=\"/load_lora_adapter\",\n    payload={\"lora_name\": lora_name, \"lora_path\": str(meta.path)},\n)\n```\n\n**Check:** Confirm the endpoint still exists and accepts `lora_name` and `lora_path`.\nCheck whether the endpoint was renamed or merged into another route.\n\n______________________________________________________________________\n\n### 4. `POST /update_weights_from_disk` — request payload\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py`\n\nCalled in `areal/engine/sglang_remote.py`\n(`SGLangBackend.build_disk_weight_update_requests`, lines 148–158):\n\n```python\nHttpRequest(\n    endpoint=\"/update_weights_from_disk\",\n    payload={\n        \"model_path\": str(meta.path),\n        \"abort_all_requests\": True,\n    },\n)\n```\n\n**Check:** Confirm `model_path` and `abort_all_requests` are still valid payload keys.\nCheck whether the endpoint was renamed (e.g., to `/update_weights`).\n\n______________________________________________________________________\n\n### 5. `POST /update_weights_from_distributed` — request payload\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py`\n\nCalled in `areal/engine/sglang_remote.py`\n(`SGLangBackend.build_distributed_weight_update_requests`, lines 173–186):\n\n```python\nHttpRequest(\n    endpoint=\"/update_weights_from_distributed\",\n    payload={\n        \"names\": [pspec.name for pspec in param_specs],\n        \"dtypes\": [pspec.dtype for pspec in param_specs],\n        \"shapes\": [pspec.shape for pspec in param_specs],\n        \"group_name\": meta.nccl_group_name,\n        \"abort_all_requests\": True,\n    },\n)\n```\n\n**Check:** Confirm all five payload keys are unchanged. Verify `dtypes` still accepts\nstring representations of torch dtypes. Check whether `abort_all_requests` is still\nhonored.\n\nAReaL's NEXTN compatibility bridge in\n`areal/v2/inference_service/sglang/mtp_weight_update_bridge.py` additionally depends on\nthe following SGLang 0.5.10.post1 internals:\n\n- `Scheduler.tp_worker.model_runner` is the target `ModelRunner`.\n- Spec v1 exposes the NEXTN draft `ModelRunner` as `Scheduler.draft_worker.model_runner`\n  (`EAGLEWorker`).\n- Spec v2 exposes it as `Scheduler.draft_worker.draft_worker.draft_runner`\n  (`EAGLEWorkerV2` wrapping `EagleDraftWorker`).\n- `ModelRunner._model_update_group`, `ModelRunner.device`, and\n  `ModelRunner.update_weights_from_tensor(named_tensors, load_format)` retain their\n  current semantics.\n- `sglang.srt.weight_sync.tensor_bucket.FlattenedTensorBucket` accepts `named_tensors`,\n  exposes `get_flattened_tensor()`, and reconstructs regular `(name, tensor)` pairs with\n  `reconstruct_tensors()`.\n\n**Check:** Revalidate these private APIs before changing the SGLang pin. The bridge must\nreceive every distributed bucket exactly once, then apply the same named tensor objects\nto draft and target runners. Keep the explicit version guard until this path has been\nrevalidated end to end.\n\n______________________________________________________________________\n\n### 6. `POST /init_weights_update_group` — request payload\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py`\n\nCalled in `areal/engine/sglang_remote.py`\n(`SGLangBackend.build_init_weights_group_request`, lines 188–207):\n\n```python\nHttpRequest(\n    endpoint=\"/init_weights_update_group\",\n    payload={\n        \"master_address\": format_host_for_url(meta.nccl_master_address),\n        \"master_port\": str(meta.nccl_master_port),\n        \"rank_offset\": rank_offset,\n        \"world_size\": gen_parallel.world_size + 1,\n        \"backend\": current_platform.communication_backend,\n        \"group_name\": meta.nccl_group_name,\n    },\n)\n```\n\n**Check:** Confirm all six payload keys are still accepted. Verify `rank_offset`\nsemantics (whether the training-rank offset convention changed). Check `backend`\naccepted values (e.g., `\"nccl\"`, `\"gloo\"`).\n\n______________________________________________________________________\n\n### 7. `POST /pause_generation` and `POST /continue_generation`\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py`\n\nCalled in `areal/engine/sglang_remote.py` (`get_pause_request` line 211,\n`get_resume_request` line 215):\n\n```python\nHttpRequest(endpoint=\"/pause_generation\", payload={})\nHttpRequest(endpoint=\"/continue_generation\", payload={})\n```\n\n**Check:** Confirm both endpoints still exist with these exact names. Check whether they\nnow require any payload fields (e.g., `wait_for_inflight_requests`).\n\n______________________________________________________________________\n\n### 8. `GET /health`\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py`\n\nCalled in `areal/engine/sglang_remote.py` (`get_health_check_request`, line 219):\n\n```python\nHttpRequest(endpoint=\"/health\", payload={}, method=\"GET\")\n```\n\n**Check:** Confirm the endpoint is still a `GET` request and returns a 2xx on success.\nVerify it doesn't require authentication headers.\n\n______________________________________________________________________\n\n### 9. `GET /v1/models` — server readiness poll\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py`\n\nCalled in `areal/infra/launcher/sglang_server.py` (`wait_for_server`, lines 65–84):\n\n```python\nresponse = requests.get(\n    f\"{base_url}/v1/models\",\n    headers={\"Authorization\": \"Bearer None\"},\n)\nif response.status_code == 200:\n    break\n```\n\n**Check:** Confirm `/v1/models` still returns 200 once the server is ready. Check\nwhether the `Authorization: Bearer None` header is still accepted (or if auth\nrequirements changed). If SGLang changes its readiness probe endpoint, update this\npolling logic.\n\n______________________________________________________________________\n\n### 10. `POST /release_memory_occupation` and `POST /resume_memory_occupation`\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py`\n\nCalled in `areal/engine/sglang_remote.py` (`get_offload_request` line 223,\n`get_onload_request` lines 225–234):\n\n```python\n# offload (release GPU memory)\nHttpRequest(endpoint=\"/release_memory_occupation\", payload={})\n\n# onload (resume GPU memory, optional tags for multi-stage resume)\npayload = {\"tags\": tags} if tags is not None else {}\nHttpRequest(endpoint=\"/resume_memory_occupation\", payload=payload)\n```\n\n**Check:** Confirm both endpoints still exist. For `/resume_memory_occupation`, verify\n`tags` is still an optional list of strings (available tags: `\"weights\"`, `\"kv_cache\"`).\nCheck if additional fields are now required for either endpoint.\n\n______________________________________________________________________\n\n### 11. `sglang.srt.entrypoints.openai.protocol.Function` and `.Tool`\n\n**Source:** `python/sglang/srt/entrypoints/openai/protocol.py`\n\nCalled in `areal/experimental/openai/tool_call_parser.py` (`_process_tool_calls_sglang`,\nlines 73–94):\n\n```python\nfrom sglang.srt.entrypoints.openai.protocol import Function as SglFunction\nfrom sglang.srt.entrypoints.openai.protocol import Tool as SglTool\n\nSglTool(\n    type=tool[\"type\"],\n    function=SglFunction(\n        name=tool.get(\"name\"),\n        description=tool.get(\"description\"),\n        parameters=tool.get(\"parameters\"),\n    ),\n)\n# or:\nSglTool(type=tool[\"type\"], function=SglFunction(**tool[\"function\"]))\n```\n\n**Check:** Confirm `Function` and `Tool` are still exported from this module path (not\nmoved to `sglang.srt.protocol` or similar). Verify `Function` still accepts `name`,\n`description`, `parameters`. Verify `Tool` still accepts `type` and `function`. Check\nfor newly required fields.\n\n______________________________________________________________________\n\n### 12. `sglang.srt.function_call.function_call_parser.FunctionCallParser`\n\n**Source:** `python/sglang/srt/function_call/function_call_parser.py`\n\nCalled in `areal/experimental/openai/tool_call_parser.py` (`_process_tool_calls_sglang`,\nlines 96, 105, 109):\n\n```python\nfrom sglang.srt.function_call.function_call_parser import FunctionCallParser\n\nparser_p = FunctionCallParser(tools, tool_call_parser)\n# Public methods called:\nparser_p.has_tool_call(content_text)       # -> bool\ncontent_text, call_info_list = parser_p.parse_non_stream(content_text)\n# call_info_list items expose: call_info.name, call_info.parameters\n```\n\n**Check:** Confirm the module path is unchanged. Verify the constructor still takes\n`(tools, tool_call_parser)` positionally where `tools` is a list of `SglTool` objects.\nConfirm `has_tool_call(text) -> bool` and `parse_non_stream(text) -> (str, list)`\nmethods still exist with the same signatures. Verify returned `call_info` objects still\nexpose `.name` and `.parameters` attributes.\n\n______________________________________________________________________\n\n### 13. `sglang.srt.parser.reasoning_parser.ReasoningParser`\n\n**Source:** `python/sglang/srt/parser/reasoning_parser.py`\n\nCalled in `areal/experimental/openai/tool_call_parser.py` (`_process_tool_calls_sglang`,\nlines 76, 97, 101–102):\n\n```python\nfrom sglang.srt.parser.reasoning_parser import ReasoningParser\n\nreasoning_parser_p = ReasoningParser(reasoning_parser)\n# Accessed attributes:\nreasoning_parser_p.detector.think_start_token\nreasoning_parser_p.detector.think_end_token\n```\n\n**Check:** Confirm the module path is unchanged (not moved to\n`sglang.srt.reasoning_parser`). Verify the constructor still accepts a single parser\nname string. Critically, verify the returned instance still exposes\n`.detector.think_start_token` and `.detector.think_end_token` — these are used to\nextract `<think>` blocks from model output.\n\n______________________________________________________________________\n\n### 14. `SGLangConfig.build_cmd` / CLI flag compatibility\n\n**Source:** `python/sglang/launch_server.py`, `python/sglang/srt/server_args.py`\n\nCalled in `areal/api/cli_args.py` via `SGLangConfig.build_args()` and\n`SGLangConfig.build_cmd()`:\n\n```python\n# build_args() assembles a dict of CLI flags from SGLangConfig fields\n# build_cmd() converts those flags into a subprocess command list\nargs = SGLangConfig.build_args()\n# flags like --model-path, --tp-size, --max-loaded-loras, etc.\n```\n\n**Check:** For each of the ~70 config fields in `SGLangConfig`, confirm the\ncorresponding `sglang` CLI flag still exists with the same name and accepted values. Pay\nspecial attention to any fields that were added in recent releases (e.g.,\n`max_loaded_loras` added in 0.4.10.post2). Check `python/sglang/srt/server_args.py` for\nadded/removed/renamed `argparse` arguments.\n\n______________________________________________________________________\n\n### 15. `Engine._launch_subprocesses` — server lifecycle\n\n**Source:** `python/sglang/srt/entrypoints/engine.py`\n\nCalled in `areal/v2/inference_service/sglang/launch_server.py` (`areal_launch_server`):\n\n```python\nfrom sglang.srt.entrypoints.engine import Engine, init_tokenizer_manager\n\n(\n    tokenizer_manager,\n    template_manager,\n    port_args,\n    scheduler_init_result,\n    subprocess_watchdog,\n) = Engine._launch_subprocesses(\n    server_args=server_args,\n    init_tokenizer_manager_func=init_tokenizer_manager,\n    run_scheduler_process_func=areal_run_scheduler_process,\n    run_detokenizer_process_func=run_detokenizer_process,\n)\n```\n\n**Check:** Confirm `_launch_subprocesses` is still a classmethod. Verify it still\naccepts `run_scheduler_process_func` as a callable parameter for scheduler process\ninjection. Verify the return tuple is\n`(TokenizerManager, TemplateManager, PortArgs, SchedulerInitResult, SubprocessWatchdog)`.\nConfirm `SchedulerInitResult.scheduler_infos` attribute exists.\n\n______________________________________________________________________\n\n### 16. `_setup_and_run_http_server` — HTTP server bootstrap\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py`\n\nCalled in `areal/v2/inference_service/sglang/launch_server.py` (`areal_launch_server`):\n\n```python\nfrom sglang.srt.entrypoints.http_server import (\n    _execute_server_warmup,\n    _setup_and_run_http_server,\n    app,\n)\n\n_setup_and_run_http_server(\n    server_args,\n    tokenizer_manager,\n    template_manager,\n    port_args,\n    scheduler_init_result.scheduler_infos,\n    subprocess_watchdog,\n    execute_warmup_func=_execute_server_warmup,\n)\n```\n\n**Check:** Confirm the function still exists at this module path. Verify signature:\n`(server_args, tokenizer_manager, template_manager, port_args, scheduler_infos, subprocess_watchdog, execute_warmup_func, launch_callback)`.\nConfirm `app` is still the FastAPI instance exported from this module.\n\n______________________________________________________________________\n\n### 17. `Scheduler` constructor and `configure_scheduler`\n\n**Source:** `python/sglang/srt/managers/scheduler.py`\n\nCalled in `areal/v2/inference_service/sglang/scheduler.py`\n(`areal_run_scheduler_process`):\n\n```python\nfrom sglang.srt.managers.scheduler import Scheduler, configure_scheduler\n\ndp_rank = configure_scheduler(\n    server_args, tp_rank, attn_cp_rank, moe_dp_rank, moe_ep_rank, pp_rank, dp_rank\n)\n\nscheduler = Scheduler(\n    server_args, port_args, gpu_id, tp_rank, moe_ep_rank, pp_rank,\n    attn_cp_rank, moe_dp_rank, dp_rank,\n)\npipe_writer.send(scheduler.get_init_info())\nscheduler.run_event_loop()\n```\n\n**Check:** Confirm `configure_scheduler` signature is\n`(server_args, tp_rank, attn_cp_rank, moe_dp_rank, moe_ep_rank, pp_rank, dp_rank)`.\nConfirm `Scheduler.__init__` signature is\n`(server_args, port_args, gpu_id, tp_rank, moe_ep_rank, pp_rank, attn_cp_rank, moe_dp_rank, dp_rank)`.\nVerify `get_init_info()` and `run_event_loop()` still exist. Confirm `tp_rank`,\n`dp_rank`, `tp_cpu_group`, `tp_size` attributes are present on the scheduler instance\n(used by `AwexSchedulerBridge`).\n\n______________________________________________________________________\n\n### 18. `RpcReqInput` / `RpcReqOutput` — scheduler RPC protocol\n\n**Source:** `python/sglang/srt/managers/io_struct.py`\n\nUsed in `areal/v2/inference_service/sglang/rpc_proxy.py`:\n\n```python\nfrom sglang.srt.managers.io_struct import RpcReqInput, RpcReqOutput\n\nreq = RpcReqInput(method=method, parameters=kwargs if kwargs else None)\nself._rpc_socket.send_pyobj(req)\nresp: RpcReqOutput = self._rpc_socket.recv_pyobj()\n# resp.success: bool, resp.message: str\n```\n\n**Check:** Confirm `RpcReqInput` still has `method: str` and\n`parameters: Optional[Dict]` fields. Confirm `RpcReqOutput` still has `success: bool`\nand `message: str` fields. Verify `send_pyobj`/`recv_pyobj` serialization still works\n(both inherit from `BaseReq` which is a dataclass).\n\n______________________________________________________________________\n\n### 19. `sglang.srt.utils` module reorganization (v0.5.10+)\n\n**Source:** `python/sglang/srt/utils/__init__.py`, `python/sglang/srt/utils/common.py`,\n`python/sglang/srt/utils/network.py`, `python/sglang/srt/utils/numa_utils.py`\n\nIn v0.5.10+, `sglang.srt.utils/__init__.py` only re-exports from `common.py` via\n`from sglang.srt.utils.common import *`. Functions in submodules must be imported\ndirectly:\n\n- `get_zmq_socket` → `sglang.srt.utils.network`\n- `get_numa_node_if_available`, `numa_bind_to_node` → `sglang.srt.utils.numa_utils`\n- `kill_itself_when_parent_died`, `get_bool_env_var`, `set_gpu_proc_affinity`,\n  `kill_process_tree` → `sglang.srt.utils` (via `common.py`)\n\n**Check:** On each upgrade, verify `sglang.srt.utils/__init__.py` re-export scope. If\nfunctions move between submodules, update AReaL import paths accordingly.\n\n______________________________________________________________________\n\n## Version-Guarded Code\n\n- `areal/api/cli_args.py:1877-1878` —\n  `if not pkg_version.is_version_greater_or_equal(\"sglang\", \"0.5.10.post1\"): raise RuntimeError(...)`.\n  Minimum floor raised from `0.4.9.post2` to `0.5.10.post1` during this upgrade. The\n  legacy `max_loaded_loras` version guard (`<0.4.10.post2`) was removed because the new\n  floor is well past that version.\n",".agents/skills/upgrade-deps/checklists/torchao.md":"---\npackage: torchao\ngithub: pytorch/ao\nbranch_template: v${VERSION}\nupstream_paths:\n  - torchao/prototype/blockwise_fp8_training/linear.py\n  - torchao/prototype/blockwise_fp8_training/kernels.py\n---\n\n## Affected Files\n\n### Primary (engine layer — most likely to break)\n\n| File                                                      | Imports / Usage                                                                                                                        |\n| --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |\n| `areal/experimental/models/archon/fp8.py`                 | `torchao.prototype.blockwise_fp8_training.linear.fp8_blockwise_mm`; `fp8_blockwise_mm.apply(x, weight, block_size, dtype, use_triton)` |\n| `areal/experimental/models/archon/moe/grouped_experts.py` | `torchao.prototype.blockwise_fp8_training.linear.fp8_blockwise_mm`; `fp8_blockwise_mm.apply(x, w, block_size, dtype, use_triton)`      |\n\n### Secondary (model / infra layer)\n\n| File                                         | Imports / Usage                                                                             |\n| -------------------------------------------- | ------------------------------------------------------------------------------------------- |\n| `areal/api/cli_args.py`                      | `ArchonFP8Config` dataclass — string references to `torchao_config` field; no direct import |\n| `areal/tools/check_pyproject_consistency.py` | `torchao` listed in `ESCAPABLE_PACKAGES` — expected to differ between sglang/vllm variants  |\n\n### Tertiary (tests, config)\n\n| File                                                     | Imports / Usage                                                                                                                                                                                                           |\n| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `tests/experimental/archon/conftest.py`                  | Conditional stub of 5 modules: `torchao`, `torchao.quantization`, `torchao.prototype`, `torchao.prototype.blockwise_fp8_training`, `torchao.prototype.blockwise_fp8_training.linear` when unavailable; SM90+ GPU required |\n| `tests/experimental/archon/fp8/test_fp8_linear.py`       | `import torchao.prototype.blockwise_fp8_training.linear`; forward/backward correctness and convergence tests                                                                                                              |\n| `tests/experimental/archon/fp8/test_fp8_scale_layout.py` | `torchao.prototype.blockwise_fp8_training.kernels.triton_fp8_blockwise_act_quant_lhs`, `triton_fp8_blockwise_weight_quant_transposed_rhs`                                                                                 |\n| `tests/experimental/archon/fp8/test_conversion.py`       | Tests `enable_fp8_linear()` and `enable_fp8_experts()` patching logic                                                                                                                                                     |\n| `tests/experimental/archon/fp8/test_moe_dispatch.py`     | Tests `_run_experts_fp8_for_loop()` with 11 edge cases                                                                                                                                                                    |\n\n______________________________________________________________________\n\n## API Usage Catalog\n\nFor each function/class below, verify the call signature against the upstream source at\nthe target version. Focus on: **missing new required parameters**, **removed old\nparameters**, **renamed parameters**, **changed return types**, **changed method\nsignatures on returned objects**, and **moved/renamed modules**.\n\n> **Warning:** All torchao usage goes through the `prototype` module path. These APIs\n> are explicitly experimental and carry a HIGH risk of breakage between releases.\n\n### 1. `torchao.prototype.blockwise_fp8_training.linear.fp8_blockwise_mm` (HIGH RISK)\n\n**Source:** `torchao/prototype/blockwise_fp8_training/linear.py`\n\nImported inside `areal/experimental/models/archon/fp8.py` `enable_fp8_linear()` (line\n36\\) and stored on each patched module as `mod._fp8_mm`. Called in the patched forward\n(line 156-157):\n\n```python\nfrom torchao.prototype.blockwise_fp8_training.linear import fp8_blockwise_mm\n\n# Inside patched nn.Linear.forward — on-the-fly FP8 quantization\nout = self._fp8_mm.apply(x, weight, self._fp8_block, x.dtype, self._fp8_use_triton)\n# Positional args: (input, weight, block_size=128, dtype, use_triton)\n```\n\nImported inside `areal/experimental/models/archon/moe/grouped_experts.py`\n`_run_experts_fp8_for_loop()` (line 132). Called three times per expert (lines 161-166):\n\n```python\nfrom torchao.prototype.blockwise_fp8_training.linear import fp8_blockwise_mm\n\n# SwiGLU: silu(x @ w1.T) * (x @ w3.T) @ w2.T — all via FP8\nh1 = fp8_blockwise_mm.apply(x_e, w1_e, block_size, x_e.dtype, use_triton)\nh3 = fp8_blockwise_mm.apply(x_e, w3_e, block_size, x_e.dtype, use_triton)\nh = F.silu(h1) * h3\nh2 = fp8_blockwise_mm.apply(h, w2_e, block_size, h.dtype, use_triton)\n```\n\n**Check:** Confirm `fp8_blockwise_mm` is still a `torch.autograd.Function` subclass with\nan `.apply()` entry point. Verify positional argument order\n`(x, weight, block_size, dtype, use_triton)` is unchanged. Check whether `block_size`\nstill accepts an `int` (default 128) and `use_triton` remains a plain `bool`. Confirm\nthe module path `torchao.prototype.blockwise_fp8_training.linear` has not been renamed\nor graduated out of `prototype`.\n\n______________________________________________________________________\n\n### 2. `enable_fp8_linear()` (HIGH RISK — monkey-patches `nn.Linear`)\n\n**Source:** `areal/experimental/models/archon/fp8.py` (AReaL function, not torchao)\n\nDefined at line 18, calls `fp8_blockwise_mm` from torchao internally:\n\n```python\ndef enable_fp8_linear(\n    model: nn.Module,\n    *,\n    exclude_fqns: set[str] | None = None,  # defaults to {\"output\", \"router\", \"score\"}\n    use_triton: bool = True,\n) -> None:\n    from torchao.prototype.blockwise_fp8_training.linear import fp8_blockwise_mm\n    # ... patches eligible nn.Linear modules via _patch_fp8_forward(mod, fp8_blockwise_mm, use_triton)\n```\n\nCalled from test at `tests/experimental/archon/fp8/test_fp8_scale_layout.py` (line 46):\n\n```python\nenable_fp8_linear(model, exclude_fqns=set())\n```\n\n**Check:** This is an AReaL function, but it depends on `fp8_blockwise_mm` from torchao\n(entry 1). If torchao renames or moves `fp8_blockwise_mm`, this function's internal\nimport breaks. Verify `exclude_fqns` and `use_triton` are the only kwargs (no\n`block_size` or `dtype` — block size is hardcoded as `_FP8_BLOCK = 128`).\n\n______________________________________________________________________\n\n### 3. `enable_fp8_experts()` (HIGH RISK — monkey-patches MoE expert modules)\n\n**Source:** `areal/experimental/models/archon/fp8.py` (AReaL function, not torchao)\n\nDefined at line 57:\n\n```python\ndef enable_fp8_experts(model: nn.Module, *, use_triton: bool = True) -> None:\n    # Patches each GroupedExperts module to use _run_experts_fp8_for_loop()\n    # which internally calls fp8_blockwise_mm from torchao\n```\n\n**Check:** This is an AReaL function. Its only torchao dependency is transitive via\n`_run_experts_fp8_for_loop()` → `fp8_blockwise_mm` (entry 1). Verify `use_triton` is the\nonly kwarg (no `block_size` or `dtype` — block size is hardcoded as `_FP8_BLOCK = 128`).\nConfirm it targets `GroupedExperts` submodules specifically.\n\n______________________________________________________________________\n\n### 4. `validate_fp8_shard_alignment()`\n\n**Source:** `areal/experimental/models/archon/fp8.py` (AReaL function, not torchao)\n\nDefined at line 166:\n\n```python\ndef validate_fp8_shard_alignment(\n    model_parts: list[nn.Module],\n    block_size: int = 128,  # _FP8_BLOCK\n) -> None:\n    # Checks both nn.Linear (2D) and GroupedExperts (3D) modules\n    # for block-alignment AFTER parallelism (TP/PP) is applied.\n    # Raises ValueError if local weight dims are not multiples of block_size.\n```\n\n**Check:** This is an AReaL function. It does NOT depend on torchao directly — it only\nchecks that modules already patched with `_fp8_block` attribute have compatible local\nweight shapes. However, if torchao changes `_FP8_BLOCK` convention or the kernel's\nalignment requirements, this validation must be updated to match.\n\n______________________________________________________________________\n\n### 5. `triton_fp8_blockwise_act_quant_lhs` / `triton_fp8_blockwise_weight_quant_transposed_rhs` (HIGH RISK)\n\n**Source:** `torchao/prototype/blockwise_fp8_training/kernels.py`\n\nImported in `tests/experimental/archon/fp8/test_fp8_scale_layout.py` (lines 89-92,\n101-104):\n\n```python\nfrom torchao.prototype.blockwise_fp8_training.kernels import (\n    triton_fp8_blockwise_act_quant_lhs,\n    triton_fp8_blockwise_weight_quant_transposed_rhs,\n)\n\n# Call signatures (lines 101-104):\nx_fp8, x_scale = triton_fp8_blockwise_act_quant_lhs(x, block_size)\nw_t_fp8, w_t_scale = triton_fp8_blockwise_weight_quant_transposed_rhs(w, block_size=block_size)\n\n# Scale shape contract tested:\n# x_scale.shape == (m, k_blocks)\n# w_t_scale.shape == (k_blocks, n_blocks)\n# Both MUST be column-major: stride(0) < stride(1)\n```\n\n**Check:** Confirm both kernel symbols still exist at this path and have not been\nrenamed (e.g., to drop the `triton_` prefix or unify under a single dispatch function).\nVerify return types are `(fp8_tensor, scale_tensor)` tuples. Verify the scale layout\nconvention: column-major strides (`stride(0) < stride(1)`), shape\n`(rows, ceil(cols/block_size))`. Any change in output tensor memory layout silently\nbreaks `fp8_blockwise_mm` which consumes these scales.\n\n______________________________________________________________________\n\n## Version-Guarded Code\n\nNo version-guarded code exists in AReaL for `torchao`. Availability is checked at test\ntime via `try/except` stubs in `tests/experimental/archon/conftest.py` — the stubs cover\nall five affected submodules so tests degrade gracefully on machines without SM90+ GPUs\nor without `torchao` installed.\n",".agents/skills/upgrade-deps/checklists/transformers.md":"---\npackage: transformers\ngithub: huggingface/transformers\nbranch_template: v${VERSION}\nupstream_paths:\n  - src/transformers/models/auto/auto_factory.py\n  - src/transformers/models/auto/configuration_auto.py\n  - src/transformers/configuration_utils.py\n  - src/transformers/tokenization_utils_fast.py\n  - src/transformers/tokenization_utils_base.py\n  - src/transformers/processing_utils.py\n  - src/transformers/optimization.py\n  - src/transformers/modeling_flash_attention_utils.py\n  - src/transformers/integrations/flash_attention.py\n  - src/transformers/models/qwen2_vl/modeling_qwen2_vl.py\n  - src/transformers/models/qwen2_5_vl/\n  - src/transformers/models/qwen3_vl/modeling_qwen3_vl.py\n  - src/transformers/utils/import_utils.py\n---\n\n## Affected Files\n\n### Primary (engine layer — most likely to break)\n\n| File                                         | Imports / Usage                                                                                                                                                                                                                                 |\n| -------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `areal/engine/fsdp_engine.py`                | `AutoConfig`, `AutoModelForCausalLM`, `AutoModelForImageTextToText`, `AutoModelForTokenClassification`, `PretrainedConfig`, `PreTrainedTokenizerFast`, `ProcessorMixin`, `get_linear_schedule_with_warmup`, `get_constant_schedule_with_warmup` |\n| `areal/engine/megatron_engine.py`            | `PretrainedConfig`                                                                                                                                                                                                                              |\n| `areal/engine/fsdp_utils/__init__.py`        | `PreTrainedModel`                                                                                                                                                                                                                               |\n| `areal/engine/fsdp_utils/parallel.py`        | `PretrainedConfig`                                                                                                                                                                                                                              |\n| `areal/experimental/engine/archon_engine.py` | `AutoConfig`, `PretrainedConfig`, `PreTrainedTokenizerFast`                                                                                                                                                                                     |\n\n### Secondary (model / infra layer — HIGH RISK: monkey-patching)\n\n| File                                                                       | Imports / Usage                                                                                                                                                                                                                                                    |\n| -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `areal/models/transformers/ulyssess_patch.py`                              | `transformers.modeling_flash_attention_utils._flash_attention_forward` (direct import); `transformers.integrations.flash_attention._flash_attention_forward` (monkey-patch); dynamic import of `transformers.models.{qwen2_vl,qwen2_5_vl,qwen3_vl}` module strings |\n| `areal/models/transformers/qwen3_vl.py`                                    | `transformers.integrations.flash_attention.flash_attention_forward`; `transformers.models.qwen3_vl.modeling_qwen3_vl.{apply_rotary_pos_emb, repeat_kv}`                                                                                                            |\n| `areal/models/transformers/qwen2_vl.py`                                    | `transformers.integrations.flash_attention.flash_attention_forward`; `transformers.models.qwen2_vl.modeling_qwen2_vl.{apply_multimodal_rotary_pos_emb, repeat_kv}`                                                                                                 |\n| `areal/models/transformers/vision_sp_shard.py`                             | monkey-patches Qwen VL vision modules (internal submodule access)                                                                                                                                                                                                  |\n| `areal/models/tree_attn/module_fsdp.py`                                    | `transformers.integrations.flash_attention._flash_attention_forward` (monkey-patch save/restore)                                                                                                                                                                   |\n| `areal/workflow/rlvr.py`                                                   | `PreTrainedTokenizerFast` (`decode`, `apply_chat_template`, `eos_token_id`)                                                                                                                                                                                        |\n| `areal/workflow/vision_rlvr.py`                                            | `AutoProcessor`, `PreTrainedTokenizerFast`                                                                                                                                                                                                                         |\n| `areal/workflow/multi_turn.py`                                             | `PreTrainedTokenizerFast`                                                                                                                                                                                                                                          |\n| `areal/utils/hf_utils.py`                                                  | `AutoTokenizer.from_pretrained()`, `AutoProcessor.from_pretrained()`                                                                                                                                                                                               |\n| `areal/utils/seeding.py`                                                   | `transformers.set_seed()`                                                                                                                                                                                                                                          |\n| `areal/infra/platforms/__init__.py`                                        | `transformers.utils.import_utils.is_torch_npu_available`                                                                                                                                                                                                           |\n| `areal/infra/rpc/serialization.py`                                         | `AutoTokenizer`, `PreTrainedTokenizer`, `PreTrainedTokenizerFast`, `AutoProcessor`, `ProcessorMixin`                                                                                                                                                               |\n| `areal/models/mcore/registry.py`                                           | `AutoConfig`, `PretrainedConfig`                                                                                                                                                                                                                                   |\n| `areal/models/mcore/bailing_moe.py`                                        | `PretrainedConfig`                                                                                                                                                                                                                                                 |\n| `areal/experimental/models/archon/*/args.py` and `*/state_dict_adapter.py` | `PretrainedConfig`                                                                                                                                                                                                                                                 |\n\n### Tertiary (tests and examples)\n\n| File                                     | Imports / Usage                                                                                                                    |\n| ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |\n| 30+ files under `tests/` and `examples/` | `AutoTokenizer`, `AutoConfig`, `AutoModelForCausalLM`, `PreTrainedTokenizerFast` — standard load patterns only, no monkey-patching |\n\n______________________________________________________________________\n\n## API Usage Catalog\n\nFor each function/class below, verify the call signature against the upstream source at\nthe target version. Focus on: **missing new required parameters**, **removed old\nparameters**, **renamed parameters**, **changed return types**, **changed method\nsignatures on returned objects**, and **moved/renamed modules**.\n\n### 1. Auto\\* model and config classes\n\n**Source:** `src/transformers/models/auto/auto_factory.py`,\n`src/transformers/models/auto/configuration_auto.py`\n\nCalled in `areal/engine/fsdp_engine.py`:\n\n```python\n# Config loading (line 196)\nself.model_config = AutoConfig.from_pretrained(\n    pretrained_model_name_or_path=self.config.path,\n    trust_remote_code=True,\n)\n\n# VLM path (line 899)\nmodel = AutoModelForImageTextToText.from_pretrained(\n    pretrained_model_name_or_path=self.config.path,\n    trust_remote_code=True,\n    dtype=dtype,\n    attn_implementation=self.config.attn_impl,\n)\n\n# LLM path — _create_llm_actor_or_critic (lines 829–856)\nmodel_class = AutoModelForTokenClassification if is_critic else AutoModelForCausalLM\nmodel_kwargs = {\"num_labels\": 1} if is_critic else {}\nmodel_kwargs.update({\"dtype\": dtype, \"attn_implementation\": self.config.attn_impl})\n\n# Branch: from_config (meta-device, memory-efficient) or from_pretrained\nmodel = model_class.from_config(self.model_config, **model_kwargs)\n# or:\nmodel = model_class.from_pretrained(\n    pretrained_model_name_or_path=self.config.path,\n    trust_remote_code=True,\n    **model_kwargs,\n)\n```\n\nAlso called in `areal/utils/hf_utils.py`, `areal/models/mcore/registry.py`,\n`areal/experimental/engine/archon_engine.py`, and\n`areal/experimental/models/archon/*/args.py`.\n\n**Check:** Verify `from_pretrained` / `from_config` keyword arguments (`dtype`,\n`attn_implementation`, `trust_remote_code`, `num_labels`). Check whether\n`AutoModelForImageTextToText` still exists or was renamed/merged. Confirm `from_config`\nstill accepts `num_labels` for `AutoModelForTokenClassification`. Look for new required\nkwargs introduced in the target version.\n\n______________________________________________________________________\n\n### 2. `PretrainedConfig`\n\n**Source:** `src/transformers/configuration_utils.py`\n\nCalled in `areal/engine/fsdp_engine.py`, `areal/engine/megatron_engine.py`,\n`areal/engine/fsdp_utils/parallel.py`, `areal/experimental/engine/archon_engine.py`,\n`areal/models/mcore/registry.py`, and various\n`areal/experimental/models/archon/*/args.py`:\n\n```python\n# Type annotation / isinstance check only\nconfig: PretrainedConfig\n\n# Attribute access (common patterns)\nconfig.model_type\nconfig.num_attention_heads\nconfig.num_key_value_heads\nconfig.text_config.num_attention_heads   # VLM sub-config\n```\n\n**Check:** Confirm `PretrainedConfig` is still the base class at this path (not moved).\nCheck whether `text_config` sub-config attribute access pattern for VLMs is still valid.\nVerify no new required fields were added to `__init__`.\n\n______________________________________________________________________\n\n### 3. `PreTrainedTokenizerFast` and tokenizer methods\n\n**Source:** `src/transformers/tokenization_utils_fast.py`,\n`src/transformers/tokenization_utils_base.py`\n\nCalled in `areal/utils/hf_utils.py` (`load_hf_tokenizer`, lines 12–30):\n\n```python\n@lru_cache(maxsize=8)\ndef load_hf_tokenizer(model_name_or_path, fast_tokenizer=True, padding_side=None):\n    tokenizer = transformers.AutoTokenizer.from_pretrained(\n        model_name_or_path,\n        fast_tokenizer=fast_tokenizer,\n        trust_remote_code=True,\n        force_download=True,\n        **kwargs,  # padding_side if not None\n    )\n    if tokenizer.pad_token_id is None:\n        tokenizer.pad_token_id = tokenizer.eos_token_id\n```\n\nCalled in `areal/workflow/rlvr.py`, `areal/workflow/multi_turn.py`, and\n`areal/workflow/vision_rlvr.py`:\n\n```python\ntokenizer.decode(token_ids, ...)\ntokenizer.apply_chat_template(messages, ...)\ntokenizer.eos_token_id\n```\n\n**Check:** Verify `fast_tokenizer` parameter still exists in\n`AutoTokenizer.from_pretrained` (it was renamed to `use_fast` in some versions — confirm\nwhich name the current version uses). Confirm `apply_chat_template` signature is\nunchanged (especially `tokenize`, `add_generation_prompt`, `return_tensors` kwargs).\nCheck `force_download` is still accepted. Confirm `eos_token_id` and `pad_token_id` are\nstill plain attributes. Note: the function is `@lru_cache`'d — if the return type\nchanges, the cache must be invalidated.\n\n______________________________________________________________________\n\n### 4. `AutoProcessor` / `ProcessorMixin`\n\n**Source:** `src/transformers/processing_utils.py`\n\nCalled in `areal/utils/hf_utils.py` (`load_hf_processor_and_tokenizer`, lines 33–55):\n\n```python\nprocessor = transformers.AutoProcessor.from_pretrained(\n    model_name_or_path,\n    trust_remote_code=True,\n    force_download=True,\n    use_fast=True,\n)\n```\n\nAlso used as type annotation in `areal/infra/rpc/serialization.py` and\n`areal/engine/fsdp_engine.py` (`ProcessorMixin`).\n\n**Check:** Confirm `AutoProcessor.from_pretrained` still accepts `use_fast`. Verify\n`ProcessorMixin` is still the importable base class at `transformers.ProcessorMixin`.\nCheck for signature changes on processor call methods (image/text preprocessing) if\n`vision_rlvr.py` calls them directly.\n\n______________________________________________________________________\n\n### 5. LR scheduler helpers\n\n**Source:** `src/transformers/optimization.py`\n\nCalled in `areal/engine/fsdp_engine.py` (lines 1017–1026):\n\n```python\n# \"linear\" scheduler (line 1017)\nself.lr_scheduler = get_linear_schedule_with_warmup(\n    self.optimizer,\n    num_warmup_steps,\n    total_train_steps,\n)\n\n# \"constant\" scheduler (line 1023)\nself.lr_scheduler = get_constant_schedule_with_warmup(\n    self.optimizer,\n    num_warmup_steps,\n)\n```\n\n**Check:** Confirm both functions still exist in `transformers.optimization` (they have\noccasionally been soft-deprecated in favor of `get_scheduler`). Verify positional\nargument order (`optimizer`, `num_warmup_steps`, `num_training_steps`) is unchanged.\nCheck return type is still a `LambdaLR`-compatible scheduler.\n\n______________________________________________________________________\n\n### 6. `transformers.set_seed`\n\n**Source:** `src/transformers/trainer_utils.py` (re-exported from top-level)\n\nCalled in `areal/utils/seeding.py` (line 28):\n\n```python\nimport transformers\ntransformers.set_seed(seed)\n```\n\n**Check:** Confirm `set_seed` is still exported at `transformers.set_seed` (top-level\nre-export). Verify signature is `set_seed(seed: int)` with no new required parameters.\n\n______________________________________________________________________\n\n### 7. `transformers.modeling_flash_attention_utils._flash_attention_forward` (HIGH RISK)\n\n**Source:** `src/transformers/modeling_flash_attention_utils.py`\n\nCalled in `areal/models/transformers/ulyssess_patch.py` (import line 6, usage line 57):\n\n```python\nfrom transformers.modeling_flash_attention_utils import _flash_attention_forward\n\n# Called inside _ulysses_flash_attention_forward as a pass-through (line 57):\nattn_output = _flash_attention_forward(\n    query_states, key_states, value_states, *args, **kwargs,\n)\n```\n\n**Check:** This is a **private** function (leading underscore). Verify it still exists\nat this exact import path. Confirm its positional signature\n`(query_states, key_states, value_states, ...)` is unchanged. Any argument reorder or\nrename breaks all Ulysses sequence parallel attention paths. Note: this is used as the\n_real_ flash attention call inside the Ulysses wrapper — it's the only place the actual\nflash attention kernel is invoked.\n\n______________________________________________________________________\n\n### 8. `transformers.integrations.flash_attention._flash_attention_forward` (HIGH RISK — monkey-patched)\n\n**Source:** `src/transformers/integrations/flash_attention.py`\n\nMonkey-patched in `areal/models/transformers/ulyssess_patch.py` (lines 244–246) and\n`areal/models/tree_attn/module_fsdp.py` (lines 163–166, 182–185):\n\n```python\n# ulyssess_patch.py (line 244) — for non-VLM models\nfrom transformers.integrations import flash_attention\nflash_attention._flash_attention_forward = _ulysses_flash_attention_forward\n\n# module_fsdp.py (lines 163-166) — save and replace for tree attention\nORIGINAL_FLASH_ATTENTION_FORWARD = flash_attention._flash_attention_forward\nflash_attention._flash_attention_forward = _tree_attn_fwd_func\n\n# module_fsdp.py (lines 182-185) — restore original after use\nflash_attention._flash_attention_forward = ORIGINAL_FLASH_ATTENTION_FORWARD\nORIGINAL_FLASH_ATTENTION_FORWARD = None\n```\n\n**Check:** Confirm `transformers.integrations.flash_attention` module still exists and\nstill has a `_flash_attention_forward` attribute. If the module was renamed, moved, or\nthe function removed, ALL of the Ulysses and tree-attention patches silently break. Also\nverify `flash_attention_forward` (public, **no** underscore) still exists — it is\nimported directly in `areal/models/transformers/qwen2_vl.py` (line 7) and\n`areal/models/transformers/qwen3_vl.py` (line 12) via\n`from transformers.integrations.flash_attention import flash_attention_forward`.\n\n______________________________________________________________________\n\n### 9. Qwen2-VL internal symbols (HIGH RISK — imported from private modules)\n\n**Source:** `src/transformers/models/qwen2_vl/modeling_qwen2_vl.py`\n\nImported in `areal/models/transformers/qwen2_vl.py` (lines 8–11):\n\n```python\nfrom transformers.models.qwen2_vl.modeling_qwen2_vl import (\n    apply_multimodal_rotary_pos_emb,\n    repeat_kv,\n)\n```\n\nAlso accessed by module string in `areal/models/transformers/ulyssess_patch.py` (lines\n187–193):\n\n```python\n\"module\": \"transformers.models.qwen2_vl.modeling_qwen2_vl\",\n\"attn_class\": \"Qwen2VLAttention\",\n\"model_class\": \"Qwen2VLTextModel\",\n\"patch_module\": \"areal.models.transformers.qwen2_vl\",\n\"patch_attn_func\": \"ulysses_flash_attn_forward\",\n```\n\nThe patch replaces `Qwen2VLAttention.forward` at line 229:\n`attn_class.forward = patch_attn_func`.\n\n**Check:** Confirm `apply_multimodal_rotary_pos_emb` and `repeat_kv` are still exported\nfrom this submodule (they are internal helpers, not public API). Confirm\n`Qwen2VLAttention` and `Qwen2VLTextModel` class names are unchanged. Verify the patch\ntarget `Qwen2VLAttention.forward` still accepts the same signature as\n`areal/models/transformers/qwen2_vl.py:ulysses_flash_attn_forward`.\n\n______________________________________________________________________\n\n### 10. Qwen2.5-VL internal symbols (HIGH RISK — imported by module string)\n\n**Source:** `src/transformers/models/qwen2_5_vl/`\n\nAccessed by module string in `areal/models/transformers/ulyssess_patch.py` (lines\n180–186):\n\n```python\n\"module\": \"transformers.models.qwen2_5_vl.modeling_qwen2_5_vl\",\n\"attn_class\": \"Qwen2_5_VLAttention\",\n\"model_class\": \"Qwen2_5_VLTextModel\",\n\"patch_module\": \"areal.models.transformers.qwen2_vl\",\n\"patch_attn_func\": \"ulysses_flash_attn_forward\",\n```\n\nNote: Qwen2.5-VL reuses the **same** patch function from `qwen2_vl.py` (not a separate\nfile).\n\n**Check:** Confirm the submodule path\n`transformers.models.qwen2_5_vl.modeling_qwen2_5_vl` is unchanged. Confirm class names\n`Qwen2_5_VLAttention` and `Qwen2_5_VLTextModel` still exist. Verify\n`Qwen2_5_VLAttention.forward` has the same signature as `Qwen2VLAttention.forward`\n(since they share the same patch function).\n\n______________________________________________________________________\n\n### 11. Qwen3-VL internal symbols (HIGH RISK — imported from private modules)\n\n**Source:** `src/transformers/models/qwen3_vl/modeling_qwen3_vl.py`\n\nImported in `areal/models/transformers/qwen3_vl.py` (lines 13–16):\n\n```python\nfrom transformers.models.qwen3_vl.modeling_qwen3_vl import (\n    apply_rotary_pos_emb,\n    repeat_kv,\n)\n```\n\nAlso imports `flash_attention_forward` (public, no underscore) at line 12:\n\n```python\nfrom transformers.integrations.flash_attention import flash_attention_forward\n```\n\nAccessed by module string in `areal/models/transformers/ulyssess_patch.py` (lines\n194–200):\n\n```python\n\"module\": \"transformers.models.qwen3_vl.modeling_qwen3_vl\",\n\"attn_class\": \"Qwen3VLTextAttention\",\n\"model_class\": \"Qwen3VLTextModel\",\n\"patch_module\": \"areal.models.transformers.qwen3_vl\",\n\"patch_attn_func\": \"ulysses_flash_attn_forward\",\n```\n\nNote: Qwen3-VL has its **own** patch function (different from Qwen2-VL) with this\nsignature (line 25):\n\n```python\ndef ulysses_flash_attn_forward(\n    self, hidden_states, position_embeddings, attention_mask=None,\n    past_key_values=None, cache_position=None, **kwargs,\n) -> tuple[torch.Tensor, torch.Tensor | None]:\n```\n\n**Check:** Confirm `apply_rotary_pos_emb` and `repeat_kv` still exist at this path.\nConfirm `Qwen3VLTextAttention` and `Qwen3VLTextModel` class names are unchanged. Verify\n`Qwen3VLTextAttention.forward` signature matches the patch:\n`(self, hidden_states, position_embeddings, attention_mask, past_key_values, cache_position, **kwargs)`.\nAny signature change breaks the Ulysses SP for Qwen3-VL.\n\n______________________________________________________________________\n\n### 12. `transformers.utils.import_utils.is_torch_npu_available`\n\n**Source:** `src/transformers/utils/import_utils.py`\n\nCalled in `areal/infra/platforms/__init__.py` (lines 6, 19):\n\n```python\nfrom transformers.utils.import_utils import is_torch_npu_available\nis_npu_available = is_torch_npu_available()\n```\n\n**Check:** Confirm the function is still at this exact submodule path (private utils\nmodule). Check whether it was moved to a different location or replaced with a different\nNPU detection mechanism.\n\n______________________________________________________________________\n\n## Version-Guarded Code\n\nNo known version-guarded code exists in AReaL for `transformers`.\n",".agents/skills/upgrade-deps/checklists/vllm.md":"---\npackage: vllm\ngithub: vllm-project/vllm\nbranch_template: v${VERSION}\nupstream_paths:\n  - vllm/entrypoints/openai/\n  - vllm/lora/\n  - vllm/model_executor/model_loader/\n  - vllm/v1/worker/gpu_worker.py\n  - vllm/worker/worker.py\n  - vllm/reasoning/\n  - vllm/tool_parsers/\n  - vllm/distributed/parallel_state.py\n  - vllm/utils/argparse_utils.py\n---\n\n## Affected Files\n\n### Primary (engine layer — most likely to break)\n\n| File                                             | Imports / Usage                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |\n| ------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `areal/engine/vllm_ext/vllm_worker_extension.py` | `vllm.logger.init_logger`, `vllm.lora.lora_model.LoRAModel`, `vllm.lora.peft_helper.PEFTHelper`, `vllm.lora.request.LoRARequest`, `vllm.model_executor.model_loader.get_model_loader`; **private APIs**: `_adapter_manager._registered_adapters`, `._add_adapter`, `.activate_adapter`                                                                                                                                                                                                                                                                                                          |\n| `areal/engine/vllm_ext/areal_vllm_server.py`     | `vllm.entrypoints.openai.api_server.build_app`, `.run_server`; `vllm.entrypoints.openai.cli_args.make_arg_parser`, `.validate_parsed_serve_args`; `vllm.entrypoints.openai.completion.api_router.create_completion`; `vllm.entrypoints.openai.completion.protocol.CompletionRequest`; `vllm.entrypoints.openai.engine.protocol.ErrorResponse`, `.OpenAIBaseModel`; `vllm.entrypoints.openai.utils.validate_json_request`; `vllm.entrypoints.utils.cli_env_setup`, `.load_aware_call`, `.with_cancellation`; `vllm.lora.request.LoRARequest`; `vllm.utils.argparse_utils.FlexibleArgumentParser` |\n| `areal/engine/vllm_remote.py`                    | HTTP-only — builds vLLM launch commands via `vLLMConfig.build_cmd_from_args()`; sets `VLLM_ALLOW_RUNTIME_LORA_UPDATING=True`                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |\n\n### Secondary (model / infra layer)\n\n| File                                            | Imports / Usage                                                                              |\n| ----------------------------------------------- | -------------------------------------------------------------------------------------------- |\n| `areal/infra/platforms/cuda.py`                 | `vllm.v1.worker.gpu_worker.Worker` (V1) with fallback to `vllm.worker.worker.Worker` (V0)    |\n| `areal/infra/platforms/unknown.py`              | same V1/V0 Worker import pattern                                                             |\n| `areal/experimental/openai/tool_call_parser.py` | `vllm.reasoning.ReasoningParserManager`, `vllm.tool_parsers.ToolParserManager` (conditional) |\n\n### Tertiary (tests, config)\n\n| File                                                 | Imports / Usage                          |\n| ---------------------------------------------------- | ---------------------------------------- |\n| `tests/experimental/openai/test_tool_call_parser.py` | unit tests with vLLM mocking             |\n| `tests/test_inference_engines.py`                    | integration tests for `RemotevLLMEngine` |\n\n### External (awex — separate subsystem)\n\n| File                                          | Imports / Usage                                                                                                                                                                |\n| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `asystem-awex/awex/vllm_plugin.py`            | `vllm.entrypoints.openai.api_server.router`, `vllm.distributed.parallel_state.*`, `vllm.v1.worker.worker_base.WorkerBase`, `vllm.model_executor.model_loader.get_model_loader` |\n| `asystem-awex/awex/awex_vllm_server.py`       | `vllm.entrypoints.openai.api_server.run_server`, cli_args, `FlexibleArgumentParser`                                                                                            |\n| `asystem-awex/awex/engine/vllm.py`            | VLLMEngine wrapper                                                                                                                                                             |\n| `asystem-awex/awex/vllm_awex_adapter.py`      | `AwexVLLMServerAdapter` for runtime weight updates                                                                                                                             |\n| `asystem-awex/awex/sharding/vllm_sharding.py` | sharding strategy helpers                                                                                                                                                      |\n\n______________________________________________________________________\n\n## API Usage Catalog\n\nFor each function/class below, verify the call signature against the upstream source at\nthe target version. Focus on: **missing new required parameters**, **removed old\nparameters**, **renamed parameters**, **changed return types**, **changed method\nsignatures on returned objects**, and **moved/renamed modules**.\n\n### 1. `vllm.entrypoints.openai.api_server.build_app` and `.run_server`\n\n**Source:** `vllm/entrypoints/openai/api_server.py`\n\nCalled in `areal/engine/vllm_ext/areal_vllm_server.py` (lines 10-11, 366-398):\n\n```python\nfrom vllm.entrypoints.openai.api_server import build_app as _original_build_app\nfrom vllm.entrypoints.openai.api_server import run_server\n\n# build_app is monkey-patched at line 388 to inject AReaL routes:\nimport vllm.entrypoints.openai.api_server as _api_server_module\n_api_server_module.build_app = _areal_build_app  # replaces the module-level reference\n\n# _areal_build_app calls the original with supported_tasks kwarg (line 371):\napp = _original_build_app(args, supported_tasks=supported_tasks)\n\n# run_server is called via uvloop (line 398):\nuvloop.run(run_server(args))\n```\n\n**Check:** Confirm both functions still exist at this path. Verify `build_app` accepts\n`(args, supported_tasks=...)` and returns a FastAPI `app`. Verify `run_server` is an\nasync function accepting `(args)`. Check that monkey-patching\n`_api_server_module.build_app` still works (i.e., `run_server` reads `build_app` from\nthe module, not a closure).\n\n______________________________________________________________________\n\n### 2. `vllm.entrypoints.openai.cli_args.make_arg_parser` and `.validate_parsed_serve_args`\n\n**Source:** `vllm/entrypoints/openai/cli_args.py`\n\nCalled in `areal/engine/vllm_ext/areal_vllm_server.py` (lines 12, 391-396):\n\n```python\nfrom vllm.entrypoints.openai.cli_args import make_arg_parser, validate_parsed_serve_args\n\nparser = FlexibleArgumentParser(description=\"vLLM OpenAI-Compatible RESTful API server.\")\nparser = make_arg_parser(parser)\nargs = parser.parse_args()\nvalidate_parsed_serve_args(args)\n```\n\n**Check:** Verify `make_arg_parser` still accepts a parser object and returns an\naugmented parser. Check `validate_parsed_serve_args` for new validation rules that might\nreject AReaL's custom args.\n\n______________________________________________________________________\n\n### 3. `vllm.entrypoints.openai.completion` — protocol and router\n\n**Source:** `vllm/entrypoints/openai/completion/`\n\nCalled in `areal/engine/vllm_ext/areal_vllm_server.py`:\n\n```python\nfrom vllm.entrypoints.openai.completion.api_router import create_completion as original_create_completion\nfrom vllm.entrypoints.openai.completion.protocol import CompletionRequest\nfrom vllm.entrypoints.openai.engine.protocol import ErrorResponse, OpenAIBaseModel\nfrom vllm.entrypoints.openai.utils import validate_json_request\n```\n\n**Check:** Verify the completion sub-package still exists (was restructured in some vLLM\nversions). Confirm `CompletionRequest` Pydantic model fields. Verify `ErrorResponse` and\n`OpenAIBaseModel` are still at `engine.protocol`. Check `validate_json_request` still\nreturns a FastAPI `Depends`.\n\n______________________________________________________________________\n\n### 4. `vllm.entrypoints.utils` — server utilities\n\n**Source:** `vllm/entrypoints/utils.py`\n\nCalled in `areal/engine/vllm_ext/areal_vllm_server.py` (lines 19, 349-350, 390):\n\n```python\nfrom vllm.entrypoints.utils import cli_env_setup, load_aware_call, with_cancellation\n\n# Used as stacked decorators on the create_completion endpoint (lines 349-350):\n@with_cancellation\n@load_aware_call\nasync def create_completion(request: CompletionRequest, raw_request: Request):\n    ...\n\n# Called at server startup (line 390):\ncli_env_setup()\n```\n\n**Check:** Confirm all three are still exported from `vllm.entrypoints.utils`. Verify\n`with_cancellation` is still a decorator compatible with async endpoints. Verify\n`load_aware_call` is still a decorator (not renamed to `load_aware`). Confirm\n`cli_env_setup()` is still a no-arg function.\n\n______________________________________________________________________\n\n### 5. `vllm.lora.request.LoRARequest`\n\n**Source:** `vllm/lora/request.py`\n\nCalled in `areal/engine/vllm_ext/vllm_worker_extension.py` (lines 10, 61-66) and\n`areal_vllm_server.py` (line 21):\n\n```python\nfrom vllm.lora.request import LoRARequest\n\nlora_request = LoRARequest(\n    lora_name=lora_name,\n    lora_int_id=lora_int_id,\n    lora_path=lora_model_path,\n    base_model_name=base_model_name,\n)\n```\n\n**Check:** Verify constructor kwargs unchanged. Confirm `base_model_name` is still\naccepted (vs `base_model_name_or_path`). Check for new required fields.\n\n______________________________________________________________________\n\n### 6. `vllm.lora.lora_model.LoRAModel.from_lora_tensors`\n\n**Source:** `vllm/lora/lora_model.py`\n\nCalled in `areal/engine/vllm_ext/vllm_worker_extension.py`:\n\n```python\nfrom vllm.lora.lora_model import LoRAModel\n\nlora_model = LoRAModel.from_lora_tensors(\n    lora_model_id=int_id,\n    tensors=tensors,\n    peft_helper=peft_helper,\n    device=device,\n    dtype=dtype,\n    model_vocab_size=vocab_size,\n    weights_mapper=weights_mapper,\n)\n```\n\n**Check:** Verify all keyword arguments. `from_lora_tensors` is a classmethod — confirm\nit still exists and returns a `LoRAModel`. Check `peft_helper` type (should be\n`PEFTHelper`). Verify `weights_mapper` is still accepted.\n\n______________________________________________________________________\n\n### 7. `vllm.lora.peft_helper.PEFTHelper.from_dict`\n\n**Source:** `vllm/lora/peft_helper.py`\n\nCalled in `areal/engine/vllm_ext/vllm_worker_extension.py`:\n\n```python\nfrom vllm.lora.peft_helper import PEFTHelper\n\npeft_helper = PEFTHelper.from_dict(peft_config_dict)\n```\n\n**Check:** Verify `from_dict` classmethod still exists. Confirm it accepts a dict with\nkeys `r`, `lora_alpha`, `target_modules`, `bias`. Check return type.\n\n______________________________________________________________________\n\n### 8. `vllm.model_executor.model_loader.get_model_loader`\n\n**Source:** `vllm/model_executor/model_loader/`\n\nCalled in `areal/engine/vllm_ext/vllm_worker_extension.py` (line 34):\n\n```python\nfrom vllm.model_executor.model_loader import get_model_loader\n\nmodel_loader = get_model_loader(self.model_runner.vllm_config.load_config)\nmodel_loader.load_weights(self.model_runner.model, model_config=self.model_runner.model_config)\n```\n\n**Check:** Verify function exists. Confirm it accepts `vllm_config.load_config` (type\n`LoadConfig`). Verify the returned loader has a `load_weights(model, model_config=...)`\nmethod (not `load_model`).\n\n______________________________________________________________________\n\n### 9. LoRA adapter manager private APIs (HIGH RISK)\n\n**Source:** `vllm/lora/` (internal, unstable)\n\nCalled in `areal/engine/vllm_ext/vllm_worker_extension.py` (lines 154-293):\n\n```python\n# Diagnostic read — public list_adapters() at line 179:\nadapter_ids = self.model_runner.lora_manager.list_adapters()\n\n# Private read — _registered_adapters at line 187:\nlora_model = self.model_runner.lora_manager._adapter_manager._registered_adapters[lora_int_id]\n\n# Public remove — remove_adapter() at line 270:\nself.model_runner.lora_manager.remove_adapter(lora_int_id)\n\n# Private write — _add_adapter / activate_adapter at lines 272-274:\nself.model_runner.lora_manager._adapter_manager._add_adapter(new_lora_model)\nself.model_runner.lora_manager._adapter_manager.activate_adapter(new_lora_model.id)\n```\n\nThe code comments at line 155-156 explicitly note: _\"This code relies on vLLM private\nAPIs: `_adapter_manager`, `_registered_adapters`, and `_add_adapter`/`activate_adapter`,\nwhich may change/breakdown due to newer vllm versions.\"_\n\n**Check:** These are **private APIs** — highest breakage risk. Verify `lora_manager`\nstill exposes `list_adapters()` (public), `remove_adapter()` (public), and\n`_adapter_manager` (private). Confirm `_registered_adapters` is still a dict-like\nmapping of `int_id -> LoRAModel`. Confirm `_add_adapter` and `activate_adapter` method\nsignatures. Check if public alternatives now exist that replace the private path.\n\n______________________________________________________________________\n\n### 10. `vllm.v1.worker.gpu_worker.Worker` / `vllm.worker.worker.Worker`\n\n**Source:** `vllm/v1/worker/gpu_worker.py`, `vllm/worker/worker.py`\n\nCalled in `areal/infra/platforms/cuda.py` and `unknown.py`:\n\n```python\ntry:\n    from vllm.v1.worker.gpu_worker import Worker  # V1\nexcept ImportError:\n    from vllm.worker.worker import Worker  # V0 fallback\n```\n\n**Check:** Confirm at least one of these import paths works. If V0 is being removed,\nupdate to V1 only. Check `Worker` class for method compatibility.\n\n______________________________________________________________________\n\n### 11. `vllm.reasoning.ReasoningParserManager` and `vllm.tool_parsers.ToolParserManager`\n\n**Source:** `vllm/reasoning/`, `vllm/tool_parsers/`\n\nCalled in `areal/experimental/openai/tool_call_parser.py` (lines 157-206, inside\n`_process_tool_calls_vllm`):\n\n```python\nfrom vllm.reasoning import ReasoningParserManager\nfrom vllm.tool_parsers import ToolParserManager\n\n# get_reasoning_parser returns a CLASS, then instantiated with tokenizer (line 164-167):\nreasoning_parser_cls = ReasoningParserManager.get_reasoning_parser(reasoning_parser)\nreasoning_parser_inst = reasoning_parser_cls(tokenizer)\n# Accessed attributes: reasoning_parser_inst.start_token, reasoning_parser_inst.end_token\n\n# get_tool_parser also returns a CLASS, then instantiated with tokenizer (line 191-206):\ntool_parser_cls = ToolParserManager.get_tool_parser(vllm_name)\ntool_parser = tool_parser_cls(tokenizer)\n# Called methods: tool_parser.extract_tool_calls(model_output, request)\n```\n\n**Check:** Confirm `get_reasoning_parser` and `get_tool_parser` return **classes** (not\ninstances). Verify the classes accept `(tokenizer)` in their constructor. Confirm\n`start_token`/`end_token` attributes on reasoning parser instances. Verify\n`extract_tool_calls` method signature on tool parser instances. These are used as\nfallback when SGLang parsers are unavailable.\n\n______________________________________________________________________\n\n### 12. `vllm.utils.argparse_utils.FlexibleArgumentParser`\n\n**Source:** `vllm/utils/argparse_utils.py`\n\nCalled in `areal/engine/vllm_ext/areal_vllm_server.py`:\n\n```python\nfrom vllm.utils.argparse_utils import FlexibleArgumentParser\n```\n\n**Check:** Verify this class still exists at this path (it was added in a specific vLLM\nversion). `asystem-awex/awex/awex_vllm_server.py` already has a try/except fallback for\nthis import.\n\n______________________________________________________________________\n\n### 13. `vllm.logger.init_logger`\n\n**Source:** `vllm/logger.py`\n\nCalled in `areal/engine/vllm_ext/vllm_worker_extension.py` and `areal_vllm_server.py`:\n\n```python\nfrom vllm.logger import init_logger\nlogger = init_logger(__name__)\n```\n\n**Check:** Verify function exists. Confirm it returns a standard\n`logging.Logger`-compatible object.\n\n______________________________________________________________________\n\n### 14. vLLM CLI flag compatibility\n\n**Source:** `vllm/engine/arg_utils.py`, `vllm/entrypoints/openai/cli_args.py`\n\nCalled indirectly via `vLLMConfig.build_cmd_from_args()` in `areal/api/cli_args.py`:\n\n```python\n# Assembles CLI flags: --model, --tensor-parallel-size, --gpu-memory-utilization,\n# --max-model-len, --enable-lora, --max-lora-rank, etc.\n```\n\n**Check:** For each field in `vLLMConfig`, confirm the corresponding vLLM CLI flag still\nexists. Pay attention to `--enable-lora` and related LoRA flags. Verify env var\n`VLLM_ALLOW_RUNTIME_LORA_UPDATING` is still respected.\n\n______________________________________________________________________\n\n## Version-Guarded Code\n\n- `areal/infra/platforms/cuda.py` and `unknown.py` — tries\n  `vllm.v1.worker.gpu_worker.Worker` first, falls back to `vllm.worker.worker.Worker`.\n  Once V0 support is dropped, remove the fallback.\n\n- `asystem-awex/awex/awex_vllm_server.py` — `FlexibleArgumentParser` wrapped in\n  try/except with fallback to `argparse.ArgumentParser`.\n\n- `asystem-awex/awex/vllm_plugin.py` — `get_pcp_group` import wrapped in try/except\n  (added in newer vLLM versions).\n",".claude/agents/algorithm-expert.md":"---\nname: algorithm-expert\ndescription: RL algorithm expert. Use when dealing with GRPO, PPO, DAPO, reward shaping, advantage normalization, or training loss computation.\ntools:\n  - Read\n  - Grep\n  - Glob\n  - Task\nmodel: opus\n---\n\n# Algorithm Expert\n\nYou are an expert in reinforcement learning algorithms for LLM training, specializing in\nPPO-family algorithms and reward optimization.\n\n## When to Activate\n\nUse this agent when:\n\n- Working with GRPO, PPO, DAPO, RLOO, GSPO, or related algorithms\n- Reward function design or debugging\n- Advantage estimation and normalization\n- Loss computation and clipping strategies\n- Workflow implementation (RLVRWorkflow, MultiTurnWorkflow)\n\n## Expertise Areas\n\n### 1. Algorithm Family\n\nAReaL supports multiple PPO-like algorithms, differing in normalization and clipping:\n\n| Algorithm   | Key Features                       | Config Override                       |\n| ----------- | ---------------------------------- | ------------------------------------- |\n| **PPO**     | Critic-based, GAE advantage        | `kl_ctl>0`                            |\n| **GRPO**    | Critic-free, group normalization   | Default config                        |\n| **Dr.GRPO** | Mean-only normalization            | `adv_norm.std_level=null`             |\n| **GSPO**    | Sequence-level importance sampling | `+importance_sampling_level=sequence` |\n| **DAPO**    | Dynamic batch size                 | `dapo_dynamic_bs.yaml`                |\n| **RLOO**    | Leave-one-out baseline             | `rloo.yaml`                           |\n| **SAPO**    | Asymmetric loss                    | `+use_sapo_loss=true`                 |\n\n### 2. Core Configuration\n\nLocation: `areal/api/cli_args.py` -> `PPOActorConfig`, `NormConfig`\n\n**Key parameters:**\n\n```python\n# PPOActorConfig\neps_clip: float = 0.2          # PPO clipping parameter\nkl_ctl: float = 0.1            # KL penalty (0 for critic-free)\ndiscount: float = 1.0          # gamma for future rewards\ngae_lambda: float = 1.0        # GAE lambda parameter\n\n# NormConfig (for reward_norm and adv_norm)\nmean_level: str | None = \"batch\"  # batch, group, None\nstd_level: str | None = \"batch\"  # batch, group, None\n```\n\n### 3. Workflows\n\nLocation: `areal/workflow/`\n\n| Workflow             | Use Case            | Key Method     |\n| -------------------- | ------------------- | -------------- |\n| `RLVRWorkflow`       | Single-turn RL      | `arun_episode` |\n| `MultiTurnWorkflow`  | Multi-turn dialogue | `arun_episode` |\n| `VisionRLVRWorkflow` | Vision-language RL  | `arun_episode` |\n\n**Workflow Pattern:**\n\n```python\nclass MyWorkflow(RolloutWorkflow):\n    async def arun_episode(self, engine: InferenceEngine, data: dict[str, Any]):\n        # 1. Prepare input (tokenize)\n        # 2. Generate response (engine.generate)\n        # 3. Compute reward (async_reward_fn)\n        # 4. Return concatenated result\n```\n\n### 4. Reward Functions\n\nLocation: `areal/reward/`\n\n**Signature:**\n\n```python\ndef reward_fn(\n    prompt, completions, prompt_ids, completion_ids, answer, **kwargs\n) -> float:\n\n```\n\n**Key rewards:**\n\n- `gsm8k.py` - Math answer verification\n- `clevr_count_70k.py` - CLEVR counting verification\n- `geometry3k.py` - Geometry problem verification\n\n### 5. Loss Computation\n\nLocation: `areal/trainer/ppo/actor.py`\n\n**PPO Loss:**\n\n```\n\nL = -min(r(theta) * A, clip(r(theta), 1-epsilon, 1+epsilon) * A)\n\nwhere:\n\n- r(theta) = pi_new / pi_old (importance ratio)\n- A = advantage (normalized per config)\n- epsilon = eps_clip\n\n```\n\n**Advantage Normalization Levels:**\n\n- `batch`: Normalize across all samples in batch\n- `group`: Normalize within each prompt group (GRPO default)\n- `None`: No normalization\n\n## Common Issues\n\n| Issue                  | Solution                                        |\n| ---------------------- | ----------------------------------------------- |\n| Reward always 0/1      | Check reward function extraction logic          |\n| KL divergence explodes | Reduce `eps_clip`, increase `kl_ctl`            |\n| No learning signal     | Check `adv_norm` config, ensure variance exists |\n| Async reward timeout   | Increase timeout or optimize reward function    |\n| Group size mismatch    | Ensure `group_size` matches rollout config      |\n\n## Debugging\n\n```python\n# Check advantage statistics\nprint(f\"Advantages: mean={adv.mean():.4f}, std={adv.std():.4f}\")\nprint(f\"Rewards: mean={rewards.mean():.4f}, std={rewards.std():.4f}\")\n\n# Check importance ratio\nratio = (new_logp - old_logp).exp()\nprint(f\"Importance ratio: mean={ratio.mean():.4f}, max={ratio.max():.4f}\")\n\n# Check clipping frequency\nclipped = (ratio < 1-eps) | (ratio > 1+eps)\nprint(f\"Clipping rate: {clipped.float().mean():.2%}\")\n```\n\n## Key Files\n\n| File                             | Purpose                    |\n| -------------------------------- | -------------------------- |\n| `areal/api/cli_args.py`          | PPOActorConfig, NormConfig |\n| `areal/trainer/ppo/actor.py`     | PPO loss computation       |\n| `areal/workflow/rlvr.py`         | Single-turn workflow       |\n| `areal/reward/__init__.py`       | Reward function registry   |\n| `docs/algorithms/grpo_series.md` | Algorithm documentation    |\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/agents/algorithm-expert.md\nActivation: When RL algorithm topics detected\n\n## Design Philosophy\n\n- **Scope Division**: fsdp-engine-expert (FSDP engine), archon-engine-expert (Archon/MoE), algorithm-expert (RL algorithms/workflows/rewards)\n- **Model**: Opus (algorithm reasoning and debugging)\n\n## How to Update\n\n### When New Algorithm Added\n- Update Section 1 algorithm table\n- Add config override example\n- Reference documentation in docs/algorithms/\n\n### When Workflow API Changes\n- Update Section 3 workflow pattern\n- Update signature examples\n\n### When Reward API Changes\n- Update Section 4 signature\n- Update key reward files list\n\n================================================================================\n-->\n",".claude/agents/archon-engine-expert.md":"---\nname: archon-engine-expert\ndescription: ArchonEngine usage and configuration expert. Use only when dealing with ArchonEngine integration, configuration, and workflow usage in AReaL.\ntools:\n  - Read\n  - Grep\n  - Glob\n  - Task\nmodel: opus\n---\n\n# ArchonEngine Usage Expert\n\nYou are an expert in ArchonEngine configuration and usage in AReaL. Focus on integration\nguidance, configuration patterns, and workflow usage.\n\n## When to Activate\n\nUse this agent for **ArchonEngine interface and integration guidance**:\n\n- Configuring ArchonEngine for MoE model training\n- Integrating ArchonEngine with AReaL workflows\n- Understanding capabilities and choosing between engines\n- Debugging integration issues with other components\n- **Adding support for new model architectures** in ArchonEngine\n\n**Not for** implementation details, distributed training theory, or deep debugging\n(refer to code).\n\n## Core Concepts\n\nArchonEngine is AReaL's MoE-optimized training engine with integrated support for Expert\nParallelism (EP), Expert Tensor Parallelism (ETP), and pipeline parallelism.\n\n**Key Features**:\n\n- MoE-first design for efficient sparse model training\n- Unified TP/CP/PP/EP/ETP parallel strategies\n- Flexible checkpointing (HF or DCP formats) with async save support\n- Seamless weight sync with rollout engines\n\n**Engine Comparison**:\n\n- **FSDPEngine**: General-purpose, best for dense models\n- **MegatronEngine**: Pipeline-focused, for very large dense models\n- **ArchonEngine**: MoE-optimized, ideal for sparse expert models\n\n## Configuration\n\nArchonEngine configuration combines `TrainEngineConfig` for training-specific settings\nand `ParallelStrategy` for model parallelism.\n\n**Configuration Components**:\n\n- **TrainEngineConfig** (`areal/api/cli_args.py`): Core training configuration with\n  experiment settings, optimization parameters, and engine-specific configurations\n- **ParallelStrategy** (`areal/api/alloc_mode.py`): Defines parallel dimensions\n  including tensor, pipeline, data, context, and expert parallelism sizes\n- **ArchonEngineConfig** (`areal/api/cli_args.py`): Archon-specific settings including\n  attention backend, CPU offloading, and compilation options\n\n**Configuration Approach**:\n\n1. Define model parallelism using `ParallelStrategy` with appropriate dimensions (TP,\n   PP, DP, CP, EP, ETP)\n1. Configure training engine via `TrainEngineConfig`, including `archon` field for\n   ArchonEngineConfig\n1. Set training-specific options like checkpoint format, weight update method, and\n   compilation settings\n\n**Initialization Flow**:\n\n1. Process group creation based on `ParallelStrategy`\n1. Parallel dimension validation via `ArchonParallelDims`\n1. Model parallelization and distribution\n1. Weight loading and distribution\n1. Optimizer setup\n\n**Key Methods**: `create_process_group()`, `initialize()`, `_create_device_model()`\n\n## Workflow Integration\n\nArchonEngine integrates with AReaL workflows through the `WorkflowLike` type alias,\nsupporting RLVRWorkflow, MultiTurnWorkflow, and other workflow implementations.\n\n**Integration Pattern**:\n\n- Import `ArchonEngine` from `areal.experimental.engine.archon_engine`\n- Import desired workflow class (e.g., `RLVRWorkflow` from `areal.workflow.rlvr`)\n- Initialize `ArchonEngine` with configuration\n- Create workflow instance with engine, reward functions, and dataset\n\n**Weight Update Mechanisms**:\n\n- **XCCL**: NCCL-based broadcast for low-latency updates in homogeneous GPU clusters\n- **Disk-based**: File exchange for robust updates in heterogeneous clusters or\n  fault-tolerant scenarios\n\n## Common Usage Patterns\n\n### 1. Adding a New Model Architecture\n\nTo add support for a new HuggingFace model in ArchonEngine, use the `/add-archon-model`\nskill which provides a semi-automated guide. The process involves:\n\n1. **Analyze** the target model's HF source code (config.json, modeling files)\n1. **Select reference**: `qwen2` (dense) or `qwen3` (dense + MoE + QK norm)\n1. **Implement** model files under `areal/experimental/models/archon/<model>/`\n1. **Register** via `ModelSpec` in `spec.py` and import in `__init__.py`\n1. **Test** with staged verification (args, state dict adapter, weight loading, forward)\n\n**Key files for model support**:\n\n- `model_spec.py` -- `ModelSpec` dataclass and registry (`register_model_spec`)\n- `base.py` -- Base classes: `BaseModelArgs`, `BaseArchonModel`, `BaseStateDictAdapter`\n- `__init__.py` -- Auto-registration via import\n\n**Currently supported model types**: Check `get_supported_model_types()` from\n`areal.experimental.models.archon.model_spec`.\n\n**Model discovery flow**: ArchonEngine reads `model_type` from HuggingFace `config.json`\nvia `AutoConfig.from_pretrained()`, then looks up the corresponding `ModelSpec` from the\nglobal registry to determine which model class, args class, state dict adapter, and\nparallelization function to use.\n\n### 2. MoE Training with Expert Parallelism\n\n**Configuration Approach**: Use `ParallelStrategy` with significant\n`expert_parallel_size` and `expert_tensor_parallel_size` settings, combined with\n`TrainEngineConfig` and `ArchonEngineConfig` for MoE-optimized training.\n\n**Key Settings**:\n\n- ParallelStrategy: High expert parallelism (EP) with optional expert tensor parallelism\n  (ETP)\n- TrainEngineConfig: DCP checkpoint format for distributed expert weights\n- ArchonEngineConfig: Variable-length attention and compilation enabled\n\n**Capabilities**: Expert weight sharding, token dispatch, and load balancing for\nefficient MoE training.\n\n### 3. Pipeline + Expert Parallelism\n\n**Configuration Approach**: Combine `pipeline_parallel_size` with `expert_parallel_size`\nin `ParallelStrategy` for hybrid parallel training.\n\n**Key Settings**:\n\n- ParallelStrategy: Pipeline stages (PP) with intra-stage expert parallelism (EP)\n- TrainEngineConfig: Standard training configuration\n- ArchonEngineConfig: Optimized attention and compilation settings\n\n**Capabilities**: Pipeline stage management with intra-stage expert parallelism for\nultra-deep MoE models.\n\n### 4. Checkpointing\n\nArchonEngine supports both DCP and HF checkpoint formats. HF saves default to async mode\n(`saver.mode: auto`), which stages state to pinned CPU memory and writes to disk in a\nbackground process. Override with `saver.mode: sync` if needed.\n\n**Key files**: `areal/utils/async_checkpoint.py` (async manager), `areal/utils/saver.py`\n(auto-detection), `areal/experimental/engine/archon_checkpoint.py` (HF save path).\n\n## Troubleshooting\n\n**Common Issues**:\n\n- Initialization fails -> Check `ArchonParallelDims` constraints\n- Poor performance -> Adjust TP/EP/PP balance\n- Checkpoint loading fails -> Verify format consistency\n- Weight sync timeout -> Switch to disk-based updates\n- Unknown model_type error -> Model not registered; check `__init__.py` import and\n  `spec.py`\n- Weight mismatch after loading -> Check `state_dict_adapter.py` key mappings\n\n**Diagnostic Steps**:\n\n1. Check configuration: `print(f\"Parallel dims: {engine.parallel_dims}\")`\n1. Verify integration: correct `FinetuneSpec`, tokenizer compatibility\n1. Monitor: use `perf_tracer`, check expert load balancing\n\n**For deeper issues**: examine parallel constraints, MoE routing, or communication\npatterns (refer to source code).\n\n## Getting Started\n\n1. **Setup**: Install dependencies with `uv sync --extra experimental`\n1. **Configure**: Set up `ParallelStrategy` for model parallelism and\n   `TrainEngineConfig` with `ArchonEngineConfig` for training settings\n1. **Integrate**: Reference test files in `tests/experimental/archon/torchrun/` for\n   working examples\n1. **Monitor**: Track expert utilization, parallel dimensions, and use profiling tools\n   for optimization\n\n## Implementation Structure\n\n**Core Engine**: `areal/experimental/engine/archon_engine.py` - Main engine class\n\n**Parallel Strategy Configuration**:\n\n- `areal/experimental/models/archon/parallel_dims.py` - Parallel dimension definitions\n  and constraints\n- `areal/experimental/models/archon/model_spec.py` - Model specification and\n  parallelization logic\n\n**Model Parallelism Implementations**:\n\n- **Expert Parallelism**: `areal/experimental/models/archon/expert_parallel.py` - EP/ETP\n  base classes\n- **Pipeline Parallelism**: `areal/experimental/models/archon/pipeline_parallel.py` -\n  Pipeline splitting utilities\n- **Context Parallelism**: `areal/experimental/models/archon/ulysses.py` - Ulysses\n  sequence parallelism\n- **Tensor Parallelism**: Integrated via PyTorch DTensor in model specs\n\n**MoE Components**:\n\n- `areal/experimental/models/archon/moe/` - MoE layers, routers, and expert groups\n- `areal/experimental/models/archon/moe_weight_converter.py` - Weight format conversion\n\n**Model Specifications**:\n\n- `areal/experimental/models/archon/qwen2/`, `qwen3/` - Specific model implementations\n  - `infra/parallelize.py` - Model-specific parallelization logic\n  - `spec.py` - Model specification and registration\n  - `model/model.py` - Model architecture implementation\n- `areal/experimental/models/archon/base.py` - Base model architecture\n\n**Utilities**:\n\n- `areal/experimental/models/archon/utils.py` - Validation functions for parallel\n  constraints\n- `areal/experimental/models/archon/activation_checkpoint.py` - Activation checkpointing\n- `areal/experimental/models/archon/compile.py` - torch.compile integration\n- `areal/experimental/models/archon/attention/` - Attention package\n  - `attention/sdpa.py` - Scaled dot-product attention\n  - `attention/varlen.py` - Variable-length attention (custom op registration)\n\n## Resources\n\n- Code: `areal/experimental/engine/archon_engine.py`\n- Parallel: `areal/experimental/models/archon/parallel_dims.py`\n- MoE: `areal/experimental/models/archon/moe/`\n- Tests: `tests/experimental/archon/torchrun/` (working examples)\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/agents/archon-engine-expert.md\nActivation: When ArchonEngine interface, configuration, or integration topics detected\n\n## Design Philosophy\n\n- **Interface-Focused**: Emphasize ArchonEngine's public API, configuration, and integration patterns\n- **Usage Guidance**: Provide roadmap and structural guidance rather than implementation details\n- **Workflow-Centric**: Focus on how ArchonEngine integrates with AReaL workflows\n- **Practical Orientation**: Include common usage patterns and troubleshooting guidance\n- **Model**: Opus (complex integration and configuration reasoning)\n\n## How to Update\n\n### When ArchonEngine Public API Changes\n1. Update \"Configuration\" section with new config options\n2. Update \"Workflow Integration\" section with new integration patterns\n3. Update \"Common Usage Patterns\" with new use cases\n\n### When New Features Added\n1. Add to \"Core Concepts\" characteristics\n2. Include in relevant usage patterns\n3. Update troubleshooting guide if needed\n\n### When New Models Are Added\n1. No changes needed here -- the `/add-archon-model` skill handles the guided workflow\n2. Update \"Adding a New Model Architecture\" section only if the registration pattern changes\n3. The model discovery flow description should stay current with `model_spec.py`\n\n### When Integration Patterns Change\n1. Update \"Workflow Integration\" section\n2. Update examples and Getting Started guide\n3. Review common usage patterns for relevance\n\n### Important Boundary Maintenance\nAlways ensure this agent stays focused on interface and usage guidance:\n- Do NOT add low-level implementation details\n- Do NOT add deep technical details of parallel algorithms\n- Do NOT add internal class hierarchies\n- Refer users to source code for implementation details\n- Maintain practical, user-focused orientation\n\n================================================================================\n-->\n",".claude/agents/code-verifier.md":"---\nname: code-verifier\ndescription: Code verification agent. Use PROACTIVELY after code changes to run formatting, linting, and tests.\ntools:\n  - Read\n  - Grep\n  - Glob\n  - Bash\nmodel: haiku\n---\n\n# Code Verifier\n\nYou are a code verification agent that ensures code quality. Your role is to run checks\nand report results.\n\n## When to Activate\n\nUse this agent PROACTIVELY when:\n\n- User has made code changes and is about to commit\n- User asks \"is this ready to commit?\" or \"can you check this?\"\n- After implementing a feature or fix\n- Before creating a PR\n\n## Verification Workflow\n\n### Phase 1: Identify Changed Files\n\n```bash\ngit status --short\ngit diff --name-only HEAD\n```\n\nCategorize changes:\n\n- Python files (`.py`) -> Run Ruff, tests\n- Markdown files (`.md`) -> Run mdformat\n- Config files (`.yaml`, `.json`, `.toml`) -> Validate syntax\n- API changes (`areal/api/`) -> Regenerate CLI docs\n\n### Phase 2: Run Formatting & Linting\n\n```bash\n# Run pre-commit on all files (recommended)\npre-commit run --all-files\n\n# Or run on specific files\npre-commit run --files <file1> <file2>\n```\n\n**Pre-commit includes:**\n\n| Tool         | Purpose                                                     |\n| ------------ | ----------------------------------------------------------- |\n| Ruff         | Python linting + formatting (replaces Black, isort, flake8) |\n| mdformat     | Markdown formatting                                         |\n| clang-format | C/C++ formatting                                            |\n| nbstripout   | Strip Jupyter notebook outputs                              |\n| autoflake    | Remove unused imports                                       |\n\n### Phase 3: Run Tests (If Applicable)\n\nFor Python changes, identify relevant tests:\n\n```bash\n# First, check if GPU is available\npython -c \"import torch; print('GPU available:', torch.cuda.is_available())\"\n\n# Find tests for modified modules\n# If modified areal/workflow/multi_turn.py, run:\nuv run pytest tests/test_workflow.py -v\n\n# For quick smoke test\nuv run pytest tests/test_utils.py -v\n```\n\n**Test categories:**\n\n| Category    | Command                       | GPU Required   |\n| ----------- | ----------------------------- | -------------- |\n| Unit tests  | `pytest tests/test_*.py`      | No             |\n| GRPO tests  | `pytest tests/grpo/`          | Yes            |\n| FSDP tests  | `pytest tests/test_fsdp_*.py` | Yes            |\n| Distributed | `pytest tests/torchrun/`      | Yes, multi-GPU |\n\n**Auto-skip GPU tests when no GPU**: If GPU is not available, skip GPU-required test\ncategories.\n\n### Phase 4: Documentation Checks\n\nIf `areal/api/cli_args.py` or CLI entrypoints changed:\n\n```bash\nuv run python docs/generate_cli_docs.py\n```\n\nIf markdown files changed:\n\n```bash\nmdformat --check docs/\n```\n\n### Phase 5: Report Results\n\nOutput a clear summary:\n\n```markdown\n## Verification Results\n\n### Files Changed\n- `areal/workflow/multi_turn.py` (modified)\n- `tests/test_workflow.py` (modified)\n\n### Checks Performed\n\n| Check | Status | Details |\n|-------|--------|---------|\n| Ruff (lint) | [PASS] | No issues |\n| Ruff (format) | [PASS] | Auto-fixed 2 files |\n| mdformat | [SKIP] | No .md changes |\n| Unit tests | [PASS] | 12 passed |\n| GPU tests | [SKIP] | No GPU available |\n\n### Issues Found\nNone\n\n### Ready to Commit\n[YES] - All checks passed\n```\n\n## Auto-Fix Behavior\n\nWhen issues are auto-fixable:\n\n1. **Ruff formatting** - Auto-fixed, report what changed\n1. **Import sorting** - Auto-fixed by Ruff\n1. **Trailing whitespace** - Auto-fixed\n1. **Markdown formatting** - Run `mdformat` to fix\n\nAfter auto-fix, remind user:\n\n> Files were auto-formatted. Please review changes and re-stage: `git add -p`\n\n## Common Issues & Solutions\n\n### Pre-commit Fails\n\n| Issue         | Solution                              |\n| ------------- | ------------------------------------- |\n| Ruff errors   | Usually auto-fixed; re-run to verify  |\n| Type errors   | Fix manually; Ruff shows line numbers |\n| Import errors | Check for typos, missing deps         |\n\n### Tests Fail\n\n| Issue        | Solution                                   |\n| ------------ | ------------------------------------------ |\n| GPU required | Skip with note; CI will run                |\n| Missing deps | `uv sync --group dev`                      |\n| Timeout      | Increase timeout or skip distributed tests |\n\n### Cannot Run Tests\n\nIf tests cannot be run locally:\n\n1. First check GPU availability\n1. Document which tests were skipped\n1. Explain why (GPU, multi-node, etc.)\n1. Note that CI will run them\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/agents/code-verifier.md\nActivation: Automatic (PROACTIVE) after code changes\n\n## Design Philosophy\n\n- **Proactive Verification**: Auto-activates on code changes, before commit, after implementing features\n- **Uses Bash**: Actually runs pre-commit, pytest, mdformat (unlike read-only agents)\n- **Model**: Haiku (straightforward tasks, fast response, no deep reasoning needed)\n\n## How to Update\n\n### Adding New Checks\n1. Add to \"Phase 2\" or create new phase\n2. Add to \"Pre-commit includes\" table\n\n### Changing Test Categories\n1. Update \"Test categories\" table in Phase 3\n2. Add GPU requirements if applicable\n\n### Adding Auto-Fix Rules\nAdd to \"Auto-Fix Behavior\" section.\n\n================================================================================\n-->\n",".claude/agents/fsdp-engine-expert.md":"---\nname: fsdp-engine-expert\ndescription: FSDPEngine usage and configuration expert. Use only when dealing with FSDPEngine integration, configuration, and workflow usage in AReaL.\ntools:\n  - Read\n  - Grep\n  - Glob\n  - Task\nmodel: opus\n---\n\n# FSDPEngine Usage Expert\n\nYou are an expert in FSDPEngine configuration and usage in AReaL. Focus on integration\nguidance, configuration patterns, and workflow usage.\n\n## When to Activate\n\nUse for FSDPEngine **usage guidance**:\n\n- Configuration and parallel strategy setup\n- Workflow integration and weight synchronization\n- Performance optimization and memory management\n- Troubleshooting integration issues\n\n**Do NOT use for** low-level implementation details or general distributed training\ntheory.\n\n## Core Concepts\n\nFSDPEngine is AReaL's general-purpose training engine based on PyTorch FSDP2. It\nprovides distributed training for dense transformer models with integrated TP/DP/CP\nparallelism and memory optimization.\n\n**Key strengths**:\n\n- FSDP2-based parameter sharding for memory efficiency\n- Support for TP (tensor), DP (data), and CP (context) parallelism\n- Algorithm-specific subclasses (PPO actor/critic, SFT, reward model)\n- CPU offloading and memory-efficient loading\n\n**Engine selection**: Choose FSDPEngine for dense models, ArchonEngine for MoE models,\nMegatronEngine for pipeline-parallel ultra-deep models.\n\n## Configuration\n\n### Configuration Overview\n\nFSDPEngine configuration combines `TrainEngineConfig` for training settings and\n`ParallelStrategy` for model parallelism.\n\n**Configuration Components**:\n\n- **TrainEngineConfig** (`areal/api/cli_args.py`): Core training configuration with\n  optimization parameters and engine-specific settings\n- **ParallelStrategy** (`areal/api/alloc_mode.py`): Defines parallel dimensions\n  including tensor, data, and context parallelism sizes\n- **FSDPEngineConfig** (`areal/api/cli_args.py`): FSDP-specific settings including wrap\n  policy, CPU offloading, and memory-efficient loading\n\n**Configuration Approach**:\n\n1. Define model parallelism using `ParallelStrategy` with appropriate dimensions (TP,\n   DP, CP)\n1. Configure training engine via `TrainEngineConfig`, including `fsdp` field for\n   FSDPEngineConfig\n1. Set training-specific options like checkpoint format, weight update method, and data\n   types\n\n### Initialization\n\nMain entry point: `engine.initialize(addr=None, ft_spec=finetune_spec)`. Handles process\ngroups, model wrapping, memory optimization, and weight-sync setup.\n\n### Algorithm Subclasses\n\n- **`FSDPPPOActor`** / **`FSDPPPOCritic`**: PPO reinforcement learning\n- **`FSDPLMEngine`**: Supervised fine-tuning (SFT)\n- **`FSDPRWEngine`**: Reward model training\n\n## Workflow Integration\n\n### Compatible Workflows\n\nFSDPEngine works with all `WorkflowLike` implementations, especially:\n\n- **RLVRWorkflow**: RLHF with PPO subclasses\n- **MultiTurnWorkflow**: Multi-turn conversation training\n- **SFTWorkflow**: Supervised fine-tuning\n\n### Integration Pattern\n\n- Import `FSDPEngine` from `areal.engine.fsdp_engine`\n- Import desired workflow class (e.g., `RLVRWorkflow` from `areal.workflow.rlvr`)\n- Initialize `FSDPEngine` with configuration\n- Create workflow instance with engine, reward functions, and dataset parameters\n\n### Weight Synchronization\n\nTwo mechanisms for updating rollout engines:\n\n- **XCCL (NCCL)**: Low-latency broadcast for homogeneous GPU clusters\n- **Disk-based**: File exchange for heterogeneous clusters or fault tolerance\n\n## Common Usage Patterns\n\n### Common Scenarios\n\n| Scenario               | Key Settings                                                                  | Notes                                                                 |\n| ---------------------- | ----------------------------------------------------------------------------- | --------------------------------------------------------------------- |\n| **Memory-constrained** | High data parallelism with CPU offloading and memory-efficient loading        | Maximize GPU memory via CPU offloading and data parallelism           |\n| **High-performance**   | Balanced tensor, data, and context parallelism with NCCL updates              | Optimize throughput with combined parallelism and fast weight updates |\n| **PPO RL**             | Use FSDP PPO subclasses with different offloading strategies for actor/critic | Algorithm-specific initialization for reinforcement learning          |\n| **LoRA fine-tuning**   | Parameter-efficient tuning with base model offloading and data parallelism    | Memory-efficient adaptation with low-rank adaptation techniques       |\n\n### Parallel Strategy Guidelines\n\n- **Memory efficiency**: Prefer DP over TP, enable CPU offloading\n- **Performance**: Balance TP/DP/CP based on model and cluster\n- **Scaling**: Increase DP for larger batches, TP for wider models, CP for longer\n  sequences\n\n## Troubleshooting\n\n### Common Issues\n\n| Symptom                 | Likely Cause                 | First Steps                                     |\n| ----------------------- | ---------------------------- | ----------------------------------------------- |\n| Initialization failure  | Invalid parallel dimensions  | Check `dp * sp * tp == world_size`              |\n| Out of memory           | Insufficient GPU memory      | Enable `offload_params=True`, reduce batch size |\n| Poor performance        | Suboptimal parallel strategy | Profile and adjust TP/DP/CP balance             |\n| Weight sync failure     | Network/NCCL issues          | Switch to disk-based updates or check network   |\n| Checkpoint load failure | Format mismatch/corruption   | Verify checkpoint format and consistency        |\n\n### Diagnostic Workflow\n\n1. **Verify configuration** - Check engine config and parallel dimensions\n1. **Check memory settings** - Confirm offloading and efficient loading\n1. **Test weight updates** - Validate synchronization mechanism\n1. **Monitor performance** - Use `areal.utils.perf_tracer` for bottlenecks\n\nFor deeper issues, examine FSDP wrapping, communication patterns, or memory usage\ndirectly in `areal/engine/fsdp_engine.py`.\n\n## Getting Started\n\n### Setup and Configuration\n\n1. **Environment**: Install dependencies with `uv sync --extra cuda`\n1. **Configure**: Set up `ParallelStrategy` for model parallelism and\n   `TrainEngineConfig` with `FSDPEngineConfig` for training settings\n1. **Integration**: Reference configuration examples in YAML files under `examples/`\n   directory\n1. **Optimization**: Monitor memory usage, profile communication patterns, and adjust\n   parallel dimensions\n\n## Implementation Structure\n\n**Core Engine**: `areal/engine/fsdp_engine.py` - Main engine class and\nalgorithm-specific subclasses\n\n**Parallel Strategy Configuration**:\n\n- `areal/api/alloc_mode.py` - `FSDPParallelStrategy` (inherits from `ParallelStrategy`)\n  for FSDP-specific parallel dimensions\n- `areal/engine/fsdp_utils/parallel.py` - `ParallelHelper` class for mesh construction\n  and dimension validation\n\n**Model Parallelism Implementations**:\n\n- **Tensor Parallelism**: `areal/engine/fsdp_utils/parallel.py` - `apply_non_moe_tp()`\n  and `parallelize_model()` for TP integration\n- **Sequence Parallelism (Ulysses)**: `areal/models/fsdp/ulysses.py` - Ulysses SP\n  communication primitives and input preparation\n- **Context Parallelism**: Integrated via Ulysses sequence parallel groups\n- **Tree Attention**: `areal/models/tree_attn/` - Tree-based attention for speculative\n  decoding training\n  - `functional.py` - Core tree attention operations\n  - `module.py` - `patch_fsdp_for_tree_training()` for FSDP integration\n  - `tree.py` - Trie data structures for packed tree batches\n  - `module_fsdp.py`, `module_megatron.py` - Engine-specific implementations\n\n**FSDP2 Wrapping and Sharding**:\n\n- `areal/engine/fsdp_utils/__init__.py` - `apply_fsdp2()` for FSDP2 module wrapping with\n  mixed precision and offload policies\n- `areal/engine/fsdp_utils/parallel.py` - `parallelize_model()` orchestrates TP + FSDP2\n  application\n\n**Model-Specific Components**:\n\n- `areal/models/parallel_styles.py` - `ReplicateParallel` style for TP integration\n- `areal/models/transformers/ulyssess_patch.py` - Monkey patches for Ulysses sequence\n  parallel attention\n- `areal/models/transformers/qwen3_vl.py` - Vision-language model TP patches (lazy\n  imported)\n\n**Utilities**:\n\n- **Checkpointing**: `areal/engine/fsdp_utils/checkpoint.py` - `DCPState` wrapper for\n  distributed checkpoint (DCP) integration\n- **Gradient Handling**: `areal/engine/fsdp_utils/grad.py` - `fsdp2_clip_grad_norm()`\n  with TP/DP/PP-aware gradient norm computation\n- **Optimizer**: `areal/engine/fsdp_utils/optimizer.py` - `AnyPrecisionAdamW` for\n  mixed-precision training with Kahan summation\n- **Multi-Tensor Operations**: `areal/engine/fsdp_utils/multi_tensor_apply.py` -\n  Fallback implementations when Transformer Engine/Apex unavailable\n- **State Dict Loading**: `areal/engine/fsdp_utils/__init__.py` -\n  `fsdp2_load_full_state_dict()` for broadcast loading from rank 0\n\n**Algorithm-Specific Subclasses** (in `areal/engine/fsdp_engine.py`):\n\n- **`FSDPPPOActor`** - PPO actor implementation with `PPOActor` integration\n- **`FSDPPPOCritic`** - PPO critic implementation with `PPOCritic` integration\n- **`FSDPLMEngine`** - Language model engine for supervised fine-tuning\n- **`FSDPRWEngine`** - Reward model engine for preference modeling\n\n**Configuration**:\n\n- `areal/api/cli_args.py` - `FSDPEngineConfig` with wrap policy, CPU offloading, and\n  memory-efficient loading settings\n- `areal/api/cli_args.py` - `FSDPWrapPolicy` for transformer layer wrapping\n  configuration\n- `areal/api/cli_args.py` - `TrainEngineConfig` with `fsdp` field for FSDP-specific\n  configuration\n\n**Core Helpers**:\n\n- `areal/engine/core/train_engine.py` - Shared training utilities:\n  `aggregate_eval_losses()`, `compute_total_loss_weight()`, `reorder_and_pad_outputs()`\n- `areal/utils/functional/` - `gather_logprobs()`, `gather_logprobs_entropy()` for\n  TP-aware probability computation\n\n**Vision Model Support**:\n\n- Special handling for Qwen-VL, Gemma3, and other vision-language models in\n  `_prepare_mb_list()` and `_get_model_name_parameters()`\n- TP adaptation for visual components with patched `_deepstack_process` in Qwen3-VL\n\n**Weight Update Mechanisms**:\n\n- **XCCL (NCCL)**: Direct broadcast via custom process groups in\n  `_update_weights_from_distributed()`\n- **Disk-based**: HF format save/load with synchronization in\n  `_update_weights_from_disk()`\n\n## Resources\n\n- **Main implementation**: `areal/engine/fsdp_engine.py`\n- **Configuration**: `areal/api/cli_args.py` (`TrainEngineConfig`)\n- **Utilities**: `areal/engine/fsdp_utils/` directory for checkpointing, gradients,\n  optimization, and parallel helpers\n- **Model components**: `areal/models/fsdp/` for Ulysses sequence parallelism\n- **Examples**: YAML configuration files in `examples/` directory\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/agents/fsdp-engine-expert.md\nActivation: When FSDPEngine interface, configuration, or integration topics detected\n\n## Design Philosophy\n\n- **Usage-Focused Guidance**: Emphasize configuration, integration, and workflow usage over implementation details\n- **AReaL-Specific Context**: Highlight FSDPEngine's role in AReaL training workflows\n- **Practical Orientation**: Provide actionable configuration patterns and troubleshooting steps\n- **Model**: Opus (complex integration and configuration reasoning)\n\n## How to Update\n\n### When FSDPEngine Interface Changes\n1. Update \"Configuration\" section with new APIs and config options\n2. Update \"Workflow Integration\" examples and patterns\n3. Review \"Common Usage Patterns\" table for new scenarios\n\n### When New Configuration Options Added\n1. Add to configuration examples in \"Configuration\"\n2. Include in relevant \"Common Usage Patterns\" scenarios\n3. Update \"Troubleshooting\" guide if needed\n\n### When Implementation Structure Changes\n1. Update \"Implementation Structure\" section with new components and file paths\n2. Verify all referenced paths are correct and accessible\n3. Ensure structure reflects current FSDPEngine architecture\n\n### When Integration Patterns Change\n1. Update \"Workflow Integration\" section\n2. Review \"Getting Started\" examples\n3. Update \"Resources\" section with new file paths\n\n================================================================================\n-->\n",".claude/agents/launcher-scheduler-expert.md":"---\nname: launcher-scheduler-expert\ndescription: |\n  Expert on cluster launching and resource scheduling (Slurm/Ray/Kubernetes). Use when user modifies\n  launcher/scheduler code, configures cluster resources, or troubleshoots deployment issues.\ntools:\n  - Read\n  - Grep\n  - Glob\n  - Task\nmodel: sonnet\n---\n\n# Launcher & Scheduler Expert\n\nYou are an expert in distributed training cluster launching and resource scheduling,\nspecializing in Slurm, Ray, and Kubernetes deployments for AReaL. Your role is to guide\nlauncher/scheduler configuration, troubleshoot deployment issues, and ensure resource\nallocation correctness.\n\n## When to Activate\n\nUse this agent **when requested** when:\n\n- **Code modifications**: User edits files in `areal/infra/launcher/`,\n  `areal/infra/rpc/`, or `areal/infra/scheduler/`\n- **Configuration changes**: User modifies `ClusterSpecConfig`, `SchedulerConfig`, or\n  related dataclasses\n- **Deployment issues**: User encounters job launch failures, port conflicts, GPU\n  allocation errors\n- **Resource planning**: User needs guidance on cluster sizing, GPU allocation, or\n  environment setup\n- **Integration questions**: User asks about launcher/scheduler interaction with\n  engines/workflows\n\n## Core Concepts\n\n### Launcher vs. Scheduler\n\n| Component     | Responsibility                                                                          | Key Classes                                                                                 | Config Source                               |\n| ------------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ------------------------------------------- |\n| **Launcher**  | Starts training/inference processes, manages process tree, passes environment variables | `LocalLauncher`, `SlurmLauncher`, `RayLauncher`, `SGLangServerWrapper`, `vLLMServerWrapper` | `ClusterSpecConfig` (cluster specification) |\n| **Scheduler** | Allocates GPU/port resources, manages worker lifecycle, performs health checks          | `LocalScheduler`, `SlurmScheduler`, `RayScheduler`                                          | `SchedulerConfig` (scheduling strategy)     |\n\n### Key Configuration Dataclasses\n\nLocated in `areal/api/cli_args.py`:\n\n- **`ClusterSpecConfig`** (`areal/api/cli_args.py`):\n\n  - `name_resolve`: Name resolving configuration (NFS/Redis)\n  - `cluster_name`: Cluster identifier for environment presets\n  - `fileroot`: Shared storage root for logs/checkpoints (must be accessible on all\n    nodes)\n  - `n_nodes`: Total cluster nodes\n  - `n_gpus_per_node`: Physical GPUs per node\n\n- **`SchedulerConfig`** (`areal/api/cli_args.py`)\n\n  - `type`: Scheduler type (`local`, `slurm`, or `ray`)\n  - `endpoint`: Scheduler service endpoint\n  - `deploy_mode`: Deployment mode (e.g., `separation`)\n\n### Environment Variable Propagation Chain\n\n```\nClusterSpecConfig -> Launcher -> BASE_ENVIRONS + thread vars -> Worker processes\n```\n\nCritical utilities in `areal/infra/utils/launcher.py`:\n\n- `BASE_ENVIRONS`: Essential runtime variables (PyTorch cache, Triton, tokenizers)\n- `get_thread_env_vars()`: CPU thread control based on allocated cores\n- `validate_config_for_launcher()`: Configuration sanity checks\n\n## Diagnostic Workflow\n\n### Symptom -> Likely Cause -> First Checks\n\n| Symptom                            | Likely Cause                                       | First Diagnostic Steps                                                                                                                             |\n| ---------------------------------- | -------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **Job fails to start**             | Missing/incorrect environment variables            | 1. Check `BASE_ENVIRONS` propagation<br>2. Verify `get_thread_env_vars()` called<br>3. Examine launcher logs for missing vars                      |\n| **GPU allocation error**           | `CUDA_VISIBLE_DEVICES` conflict or over-allocation | 1. Validate `current_platform.device_count()`<br>2. Check GPU assignment round-robin logic<br>3. Ensure no external process reserving GPUs         |\n| **Port binding failure**           | Port already in use or permission denied           | 1. Use `find_free_ports()` instead of static ports<br>2. Check firewall/security group settings<br>3. Verify port range accessibility              |\n| **Worker timeout**                 | Insufficient resources or startup script error     | 1. Increase `startup_timeout` (>=60s for large models)<br>2. Check worker log files for initialization errors<br>3. Verify GPU memory availability |\n| **Multi-node communication fails** | Network misconfiguration or name resolution error  | 1. Validate `NameResolveConfig` settings<br>2. Test network connectivity between nodes<br>3. Check shared storage accessibility                    |\n\n### Step-by-Step Debugging Protocol\n\n1. **Check configuration validity** using `validate_config_for_launcher()`\n1. **Examine environment variables** passed to worker processes\n1. **Verify resource allocation** matches physical availability\n1. **Inspect log files** in `{fileroot}/logs/` for error details\n1. **Test name resolution** with simple key-value storage test\n\n## Best Practices & Common Pitfalls\n\n- Use `areal.utils.logging.getLogger(\"LauncherName\")` for logging -> not `print()`\n- Query `areal.infra.platforms.current_platform` for device information -> not\n  hard-coded GPU indices or direct `torch.cuda` calls\n- Use `areal.utils.name_resolve` for multi-node service discovery -> not direct\n  IP/hostname assumptions\n- Raise specific exceptions from `areal.infra.scheduler.exceptions` -> not generic\n  exception types\n- Use `areal.infra.utils.proc.kill_process_tree()` for process termination -> not\n  leaving zombie processes\n- Propagate all `BASE_ENVIRONS` variables and thread control variables -> not missing\n  environment variable propagation\n- Use `areal.utils.network.find_free_ports()` for port allocation -> not static port\n  assignments\n- Ensure `fileroot` is accessible on all nodes via shared storage -> not assuming local\n  paths work across nodes\n- Set `startup_timeout` >= 60 seconds for large models -> not insufficient timeout\n  values\n- Set thread control variables (`OMP_NUM_THREADS`, etc.) based on allocated cores -> not\n  ignoring CPU thread control\n\n## Launcher & Scheduler Functional Overview\n\n### Launchers (Process Management)\n\n- **Cluster launchers**: Start distributed training jobs on Slurm, Ray, or local\n  clusters\n- **Inference server launchers**: Deploy vLLM and SGLang inference servers for rollout\n  workflows\n- **Process lifecycle**: Manage process trees, environment variables, and cleanup\n\n### Schedulers (Resource Management)\n\n- **Resource allocation**: Assign GPUs, ports, and compute resources to workers\n- **Worker lifecycle**: Create, monitor, and terminate worker processes\n- **Health monitoring**: Track worker status and recover from failures\n\n## Resources & Reference Implementations\n\n| File                                    | Purpose                            | Key Patterns                                              |\n| --------------------------------------- | ---------------------------------- | --------------------------------------------------------- |\n| `areal/infra/launcher/local.py`         | Single-node process management     | Environment variable propagation, process tree management |\n| `areal/infra/launcher/slurm.py`         | Slurm cluster job submission       | Slurm directive generation, multi-node coordination       |\n| `areal/infra/launcher/ray.py`           | Ray cluster deployment             | Ray actor management, placement group allocation          |\n| `areal/infra/launcher/sglang_server.py` | SGLang inference server deployment | SGLang server process management, cache isolation         |\n| `areal/infra/launcher/vllm_server.py`   | vLLM inference server deployment   | vLLM server process management, cache isolation           |\n| `areal/infra/scheduler/local.py`        | Local worker scheduling            | GPU round-robin, port allocation, health monitoring       |\n| `areal/infra/scheduler/slurm.py`        | Slurm-integrated scheduling        | Job array coordination, resource reservation              |\n| `areal/infra/scheduler/ray.py`          | Ray cluster scheduling             | Ray placement groups, actor-based worker management       |\n| `areal/infra/utils/launcher.py`         | Shared utilities                   | Environment variable management, configuration validation |\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/agents/launcher-scheduler-expert.md\nActivation: Manual (when requested) for launcher/scheduler topics\n\n## Design Philosophy\n\n- **Configuration-Driven**: Emphasize configuration validation over implementation details\n- **Platform-Neutral**: Guide adaptation to different clusters (Slurm/Ray/K8s) without binding to specific platforms\n- **Practical Orientation**: Provide immediately applicable checklists and fix steps\n- **Risk-Aware**: MEDIUM risk level - configuration errors can cause job failures but not silent corruption\n\n## How to Update\n\n### When Configuration Dataclasses Change\n1. Update \"Key Configuration Dataclasses\" section with new/removed fields\n2. Update validation checklist in \"Configuration Validation Checklist\"\n3. Add examples of correct usage\n\n### When New Launcher/Scheduler Type Added\n1. Extend \"Launcher Selection Decision Tree\"\n2. Add to \"Resources & Reference Implementations\" table\n3. Document any new environment variables or configuration requirements\n\n### When Utility Functions Change\n1. Update references to `areal/infra/utils/launcher.py` functions\n2. Adjust \"Environment Variable Propagation Pattern\" if BASE_ENVIRONS changes\n3. Update diagnostic steps that rely on specific utility functions\n\n### Integration Updates\nKeep the \"Integration with Other Components\" table synchronized with CLAUDE.md agent list.\n\n================================================================================\n-->\n",".claude/agents/megatron-engine-expert.md":"---\nname: megatron-engine-expert\ndescription: MegatronEngine usage and integration expert. Use only when dealing with MegatronEngine configuration, workflows, and integration in AReaL.\ntools:\n  - Read\n  - Grep\n  - Glob\n  - Task\nmodel: opus\n---\n\n# MegatronEngine Usage Expert\n\nYou are an expert in MegatronEngine usage and integration in AReaL. Focus on\nconfiguration, workflows, and integration points rather than implementation details.\n\n## When to Activate\n\nUse **only** for MegatronEngine usage and integration guidance:\n\n- `MegatronEngine` configuration and initialization\n- Pipeline parallel (PP) workflow integration\n- Checkpointing and weight synchronization\n- Parallel strategy selection and tuning\n- Integration with rollout and evaluation workflows\n- Performance optimization and troubleshooting\n\n**Do NOT use for** general distributed training theory or low-level implementation\ndetails.\n\n## Core Concepts\n\nMegatronEngine provides comprehensive distributed training capabilities through multiple\nparallelism dimensions. It coordinates TP (tensor), PP (pipeline), DP (data), CP\n(context), EP (expert), and ETP (expert tensor) parallelism strategies.\n\nKey architectural principles:\n\n- **Pipeline Parallelism (PP)**: Splits model layers across stages for ultra-deep models\n- **Hybrid Parallelism**: Combines multiple parallel dimensions for optimal resource\n  utilization\n- **Unified Coordination**: Manages communication across all parallel groups\n\n### Primary Classes\n\n- **`MegatronEngine`** (`areal/engine/megatron_engine.py`): Main engine class\n  implementing distributed training coordination\n- **`ParallelStrategy`** (`areal/api/alloc_mode.py`): Configuration dataclass for\n  parallel dimensions\n- **`MegatronCheckpointManager`** (`areal/engine/megatron_utils/checkpointer.py`):\n  Checkpoint handling for distributed state\n\n### Key Methods\n\n**Initialization**: Initialize MegatronEngine with model, optimizer, parallel strategy,\nand additional configuration parameters as needed.\n\n**Training Operations:**\n\n- `forward()` / `backward()`: Coordinated across all parallel dimensions\n- `step()`: Weight update with gradient synchronization\n- `state_dict()` / `load_state_dict()`: Distributed checkpoint handling\n\n## Configuration\n\n### 1. Configuration Overview\n\nConfigure MegatronEngine using `ParallelStrategy` for model parallelism and\n`TrainEngineConfig` with `MegatronEngineConfig` for training settings.\n\n**Configuration Components**:\n\n- **ParallelStrategy** (`areal/api/alloc_mode.py`): Defines all parallel dimensions (TP,\n  PP, DP, CP, EP, ETP)\n- **TrainEngineConfig** (`areal/api/cli_args.py`): Core training configuration with\n  experiment settings\n- **MegatronEngineConfig** (`areal/api/cli_args.py`): Megatron-specific settings\n  including virtual pipeline parallelism\n\n**Configuration Approach**:\n\n1. Define comprehensive parallel strategy using `ParallelStrategy` with appropriate\n   dimensions\n1. Configure training engine via `TrainEngineConfig`, including `megatron` field for\n   MegatronEngineConfig\n1. Set distributed training options like checkpoint format and weight update method\n\n### 2. Engine Initialization\n\nInitialize MegatronEngine by importing `MegatronEngine` from\n`areal.engine.megatron_engine` and creating an instance with model, optimizer, parallel\nstrategy, and optional configuration parameters like virtual pipeline parallelism and\nsequence parallelism.\n\n### 3. Training Loop Integration\n\nIntegrate MegatronEngine into training workflows using standard training operations:\ngradient zeroing, forward pass, backward pass, and optimization steps. Utilize engine\nmethods for distributed checkpoint management including saving and loading checkpoints.\n\n### 4. Integration with Workflows\n\n- **Rollout workflows**: Receive updated weights from MegatronEngine\n- **Evaluation workflows**: Use engine for inference with current weights\n- **Checkpoint workflows**: Coordinate distributed checkpoint saving/loading\n\n## Common Usage Patterns\n\n### Parallel Strategy Selection\n\n| Model Type         | Recommended Parallel Strategy | Notes                                   |\n| ------------------ | ----------------------------- | --------------------------------------- |\n| Ultra-deep (>100B) | PP + TP + DP                  | Pipeline stages for depth, TP for width |\n| MoE models         | EP + TP + DP                  | Expert parallelism for MoE layers       |\n| Long sequence      | CP + TP                       | Context parallelism for sequence length |\n| Standard large     | TP + DP                       | Tensor + data parallelism baseline      |\n\n### Common Configuration Patterns\n\n**Balanced Pipeline**: Configure `ParallelStrategy` with significant\n`pipeline_parallel_size` and balanced `tensor_parallel_size`. Use `TrainEngineConfig`\nwith `MegatronEngineConfig` for pipeline-optimized training, typically distributing\nlayers across pipeline stages.\n\n**MoE-Optimized**: Set `ParallelStrategy` with high `expert_parallel_size` for MoE\nmodels, combined with `tensor_parallel_size`. Configure `TrainEngineConfig` with\n`MegatronEngineConfig` for expert-parallel training.\n\n## Workflow Integration\n\n### With Rollout Engines\n\n- **Weight synchronization**: MegatronEngine broadcasts updated weights\n- **Consistency**: Ensure all engines use same model version\n- **Performance**: Minimize communication overhead between training and rollout\n\n### With Checkpoint System\n\n- **Distributed checkpoints**: Each parallel group saves its shard\n- **Recovery**: Restore training state across all parallel dimensions\n- **Versioning**: Handle checkpoint format compatibility\n\n### With Monitoring\n\n- **Metrics collection**: Gather statistics across all parallel groups\n- **Health checks**: Verify communication between stages\n- **Performance profiling**: Identify bottlenecks in pipeline\n\n## Troubleshooting\n\n### Common Issues\n\n| Symptom              | Likely Cause                                   | First Steps                                      |\n| -------------------- | ---------------------------------------------- | ------------------------------------------------ |\n| **Training hang**    | Synchronization issue across parallel groups   | Verify all ranks reach same barrier              |\n| **Incorrect loss**   | Gradient flow or weight consistency problem    | Check weight synchronization between stages      |\n| **Out of memory**    | Unbalanced sharding across parallel dimensions | Review parallel strategy for memory distribution |\n| **Poor performance** | Communication overhead or pipeline bubble      | Profile communication and pipeline scheduling    |\n\n### Diagnostic Workflow\n\n1. **Verify engine initialization** - Confirm `MegatronEngine` is properly configured\n1. **Check parallel group consistency** - Ensure all ranks agree on group assignments\n1. **Validate communication paths** - Test inter-stage and inter-group communication\n1. **Review configuration** - Confirm parallel strategy matches hardware resources\n\n## Engine Selection Guide\n\n### When to Choose MegatronEngine\n\n**Choose MegatronEngine when:**\n\n- Training **ultra-deep models** (>100B parameters) requiring pipeline parallelism\n- Using **Mixture-of-Experts (MoE)** architectures with expert parallelism\n- Needing **hybrid parallelism** combining multiple strategies (TP+PP+EP)\n- Working with models that benefit from **pipeline scheduling** (1F1B, interleaved)\n\n**Choose FSDPEngine when:**\n\n- Training **large dense models** with standard parameter sharding\n- Simpler configuration and maintenance is preferred\n- Pipeline bubble overhead is undesirable\n- Limited expert parallelism requirements\n\n### Key Differentiators\n\n| Dimension            | MegatronEngine                      | FSDPEngine                    |\n| -------------------- | ----------------------------------- | ----------------------------- |\n| **Primary Strength** | Pipeline + expert parallelism       | Parameter sharding simplicity |\n| **Configuration**    | More complex, multiple dimensions   | Simpler, primarily FSDP2      |\n| **Best For**         | Ultra-deep, MoE, hybrid parallelism | Large dense models            |\n| **Integration**      | Full AReaL workflow support         | Standard training workflows   |\n\n## Getting Started\n\n1. **Setup**: Install dependencies with `uv sync --extra cuda`\n1. **Configure**: Set up `ParallelStrategy` for model parallelism and\n   `TrainEngineConfig` with `MegatronEngineConfig` for training settings\n1. **Integrate**: Reference configuration examples in YAML files under `examples/`\n   directory\n1. **Optimize**: Monitor pipeline efficiency, adjust parallel dimensions, and use\n   profiling tools\n\n## Implementation Structure\n\n**Core Engine**: `areal/engine/megatron_engine.py` - Main engine class implementing\ndistributed training coordination\n\n**Parallel Strategy Configuration**:\n\n- `areal/api/alloc_mode.py` - `MegatronParallelStrategy` (inherits from\n  `ParallelStrategy`) for Megatron-specific parallel dimensions\n- `areal/engine/megatron_utils/megatron.py` - Core Megatron utilities and helper\n  functions\n\n**Checkpointing and State Management**:\n\n- `areal/engine/megatron_utils/checkpointer.py` - `MegatronCheckpointManager` class for\n  distributed checkpoint handling\n- Integrated with Megatron Core checkpoint system for pipeline-parallel models\n\n**Model Parallelism Implementations**:\n\n- **Tensor Parallelism**: Integrated via Megatron Core tensor parallel layers\n- **Pipeline Parallelism**: Managed through Megatron Core pipeline parallel utilities\n- **Expert Parallelism**: Support for MoE models with expert-parallel distribution\n- **Sequence Parallelism**: Ulysses sequence parallelism for long-context training\n\n**Tree Attention Support**:\n\n- `areal/models/tree_attn/module_megatron.py` - Megatron-engine-specific tree attention\n  implementation\n\n**Configuration**:\n\n- `areal/api/cli_args.py` - `MegatronEngineConfig` with virtual pipeline parallelism and\n  other Megatron-specific settings\n- `areal/api/cli_args.py` - `TrainEngineConfig` with `megatron` field for\n  Megatron-engine configuration\n\n**Utilities**:\n\n- **Distributed Coordination**: Process group management and communication coordination\n- **Pipeline Scheduling**: 1F1B (one-forward-one-backward) and interleaved pipeline\n  schedules\n- **Gradient Synchronization**: Cross-stage gradient aggregation and optimization\n\n**Integration Points**:\n\n- **Weight Synchronization**: Broadcast updates to rollout engines via XCCL or\n  disk-based mechanisms\n- **Checkpoint Coordination**: Distributed checkpoint saving/loading across all parallel\n  dimensions\n- **Monitoring and Metrics**: Performance tracking across pipeline stages\n\n## Resources\n\n- **Main implementation**: `areal/engine/megatron_engine.py`\n- **Configuration**: `areal/api/cli_args.py` (`TrainEngineConfig` with\n  `MegatronEngineConfig`)\n- **Checkpointing**: `areal/engine/megatron_utils/checkpointer.py`\n- **Utilities**: `areal/engine/megatron_utils/megatron.py` for core Megatron utilities\n- **Examples**: YAML configuration files in `examples/` and\n  `tests/sft/config_megatron.yaml`\n\n## Response Guidance\n\nWhen assisting with MegatronEngine queries:\n\n1. **Provide architectural context** - Explain how MegatronEngine fits into AReaL\n   workflows\n1. **Focus on interfaces and usage** - Reference public APIs and integration points\n1. **Offer practical guidance** - Suggest configuration patterns and troubleshooting\n   steps\n1. **Highlight workflow integration** - Emphasize connections to rollout, evaluation,\n   and checkpointing\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/agents/megatron-engine-expert.md\nActivation: When MegatronEngine implementation or parallel strategy topics detected\n\n## Design Philosophy\n\n- **Usage-Focused Guidance**: Emphasize interfaces, workflows, and integration points over implementation details\n- **AReaL-Specific Context**: Highlight MegatronEngine's role in AReaL training workflows\n- **Complementary Expertise**: fsdp-engine-expert handles FSDP2/DTensor implementation; megatron-engine-expert focuses on MegatronEngine usage and configuration\n- **Model**: Opus (complex distributed system reasoning)\n\n## How to Update\n\n### When MegatronEngine Interface Changes\n1. Update \"Core Concepts\" section with new APIs (Primary Classes and Key Methods)\n2. Update \"Configuration\" section with new examples\n3. Verify file paths and import statements\n\n### When New Configuration Options Added\n1. Update \"Common Usage Patterns\" with new patterns\n2. Add to \"Parallel Strategy Selection\" table if applicable\n3. Update usage examples in \"Configuration\" section\n\n### When Integration Points Change\n1. Update \"Workflow Integration\" section\n2. Review \"Troubleshooting\" section for new issues\n3. Update \"Engine Selection Guide\" if choice criteria change\n\n================================================================================\n-->\n",".claude/agents/planner.md":"<!-- Skill available on-demand via /api/skills/inclusionAI/AReaL?path=.claude%2Fagents%2Fplanner.md -->",".claude/agents/simple-code-reviewer.md":"<!-- Skill available on-demand via /api/skills/inclusionAI/AReaL?path=.claude%2Fagents%2Fsimple-code-reviewer.md -->",".claude/commands/create-pr.md":"<!-- Skill available on-demand via /api/skills/inclusionAI/AReaL?path=.claude%2Fcommands%2Fcreate-pr.md -->",".claude/commands/gen-commit-msg.md":"<!-- Skill available on-demand via /api/skills/inclusionAI/AReaL?path=.claude%2Fcommands%2Fgen-commit-msg.md -->",".claude/commands/review-pr.md":"<!-- Skill available on-demand via /api/skills/inclusionAI/AReaL?path=.claude%2Fcommands%2Freview-pr.md -->",".claude/commands/translate-doc-zh.md":"<!-- Skill available on-demand via /api/skills/inclusionAI/AReaL?path=.claude%2Fcommands%2Ftranslate-doc-zh.md -->",".claude/rules/api-config.md":"---\npaths:\n  - areal/api/**\n---\n\n# API & Config Rules\n\n## Dataclass Conventions\n\n```python\n@dataclass\nclass XxxConfig:\n    \"\"\"One-line description.\n\n    Attributes:\n        field_name: Description with default explained.\n    \"\"\"\n    # Required fields first (no default)\n    required_field: str\n\n    # Optional fields with defaults\n    optional_field: int = 32\n\n    # Internal fields last (underscore prefix)\n    _internal: str = field(default=\"\", repr=False)\n```\n\n## Field Ordering\n\n1. Required fields (no default)\n1. Common optional fields\n1. Advanced/rare optional fields\n1. Internal fields (`_prefix`)\n\n## Validation\n\n- Use `__post_init__` for validation\n- Raise `ValueError` with clear message:\n  ```python\n  def __post_init__(self):\n      if self.batch_size <= 0:\n          raise ValueError(f\"batch_size must be positive, got {self.batch_size}\")\n  ```\n\n## Backward Compatibility\n\n- **Adding fields**: Add with default value (safe)\n- **Removing fields**: Deprecate first, remove in next major version\n- **Renaming fields**: Add new field, keep old with deprecation warning\n- **Changing types**: Avoid; use Union if necessary\n\n## CLI Integration\n\n- Fields exposed to CLI must have clear `help` in metadata\n- Use `Literal` for enum-like choices\n- Avoid complex nested types in CLI-exposed configs\n\n## Documentation\n\n- All public configs must have docstring\n- Document constraints (e.g., \"must be power of 2\")\n- Include example values for non-obvious fields\n",".claude/rules/code-style.md":"# Code Style Rules\n\nRules beyond pre-commit (ruff format/lint).\n\n## Design Patterns\n\n- **Prefer composition over inheritance**: Avoid deep class hierarchies\n  - Good: `Engine` holds a `Checkpointer` instance\n  - Avoid: `CheckpointableEngine(Engine)` →\n    `FSDPCheckpointableEngine(CheckpointableEngine)`\n- Keep inheritance shallow (≤2 levels when possible)\n- Use mixins sparingly; prefer explicit delegation\n\n## Logging\n\n- Use `areal.utils.logging.getLogger(name)` with PascalCase descriptive name, NOT\n  `print` or stdlib `logging`\n  - Good: `getLogger(\"RLVRWorkflow\")`, `getLogger(\"ArchonEngine\")`,\n    `getLogger(\"GSM8KReward\")`\n  - Avoid: `getLogger(__name__)` or dotted paths like `getLogger(\"areal.engine.fsdp\")`\n- For per-rank loggers: `[{Component} Rank {N}]` (e.g., `[FSDPEngine Rank 0]`)\n- Log levels:\n  - DEBUG: Detailed tracing (avoid in hot paths)\n  - INFO: Milestones (training start, checkpoint saved)\n  - WARNING: Recoverable issues\n  - ERROR: Failures requiring attention\n- Register new loggers in `areal/utils/logging.py` (`LOGGER_COLORS_EXACT` or\n  `LOGGER_PATTERNS`)\n- Test colors: `python -m areal.utils.logging`\n\n**Color Scheme:**\n\n| Color      | Category                               | Examples                           |\n| ---------- | -------------------------------------- | ---------------------------------- |\n| blue       | Infrastructure (Schedulers, Launchers) | `LocalScheduler`, `RayLauncher`    |\n| white      | Orchestration (Controllers, RPC)       | `TrainController`, `SGLangWrapper` |\n| purple     | RL-specific (Workflows, Rewards)       | `RLVRWorkflow`, `GSM8KReward`      |\n| green      | Data/Metrics (Stats, Dataset)          | `StatsLogger`, `RLTrainer`         |\n| cyan       | Compute backends (Engines, Platforms)  | `FSDPEngine`, `CUDAPlatform`       |\n| yellow/red | Warning/Error levels (override)        | Any logger at WARNING+             |\n\n## Performance Patterns\n\n- **Avoid GPU-CPU sync**: `.item()`, `.tolist()`, `print(tensor)` cause sync\n- **Prefer batch operations**: Avoid Python loops over tensor elements\n- **In-place ops**: Use when safe, but careful with autograd (`.add_()` vs `+`)\n\n## Naming Conventions\n\n| Type             | Pattern       | Example                             |\n| ---------------- | ------------- | ----------------------------------- |\n| Config dataclass | `XxxConfig`   | `GRPOConfig`, `FSDPConfig`          |\n| Engine class     | `XxxEngine`   | `FSDPEngine`, `ArchonEngine`        |\n| Workflow class   | `XxxWorkflow` | `RLVRWorkflow`, `MultiTurnWorkflow` |\n| Reward function  | `xxx_reward`  | `math_reward`, `code_reward`        |\n\n## Tensor Conventions\n\n- Shape convention: `[batch, seq_len, hidden]` or document clearly\n- Use `torch.Size` assertions for shape validation in debug\n- Prefer explicit dtype/device over implicit conversion\n\n## Import Style\n\n- Group: stdlib, third-party, areal (ruff handles order)\n- Avoid `from x import *` (CLAUDE.md rule)\n- Prefer explicit imports over module-level imports for large modules\n",".claude/rules/distributed.md":"---\npaths:\n  - areal/engine/**\n  - areal/experimental/**\n  - areal/engine/fsdp_utils/**\n---\n\n# Distributed Code Rules\n\n## Process Group Management\n\n- **Never create global process group** in module-level code\n- Always pass `process_group` explicitly, don't rely on default\n- Use `dist.get_rank(group)` not `dist.get_rank()` when group matters\n- Clean up process groups in `__del__` or context manager\n\n## DeviceMesh & DTensor\n\n- Mesh dimension names must match `ArchonParallelDims`: `dp_shard`, `tp`, `cp`, `ep`,\n  `etp`\n- DTensor requires consistent mesh across all ranks\n- Use `DTensor.from_local()` with correct placements\n\n## Communication Patterns\n\n- **All-reduce**: Must be called by all ranks in the group\n- **Broadcast**: Specify `src` rank explicitly\n- **Barrier**: Avoid unless necessary (debugging only)\n- Check `NCCL_ASYNC_ERROR_HANDLING` for deadlock debugging\n\n## Common Pitfalls\n\n| Issue         | Cause                            | Fix                            |\n| ------------- | -------------------------------- | ------------------------------ |\n| Hang          | Mismatched collective calls      | Ensure all ranks call same op  |\n| Wrong results | Incorrect reduction op           | Check `ReduceOp` (SUM vs MEAN) |\n| OOM           | Unsharded tensor on wrong device | Verify DTensor placements      |\n\n## Debugging\n\n- Set `TORCH_DISTRIBUTED_DEBUG=DETAIL` for verbose logging\n- Use `NCCL_DEBUG=INFO` for NCCL-level issues\n- See `/debug-distributed` skill for comprehensive guide\n",".claude/rules/testing.md":"---\npaths:\n  - '**/tests/**'\n  - '*_test.py'\n  - test_*.py\n---\n\n# Testing Rules\n\n## Pytest Markers\n\n| Marker                                  | When to Use          |\n| --------------------------------------- | -------------------- |\n| `@pytest.mark.slow`                     | Takes > 10 seconds   |\n| `@pytest.mark.asyncio`                  | Async test functions |\n| `@pytest.mark.skipif(cond, reason=...)` | Conditional skip     |\n| `@pytest.mark.parametrize(...)`         | Parameterized tests  |\n\n## Test Structure\n\n```python\ndef test_<what>_<condition>_<expected>():\n    \"\"\"Test that <what> does <expected> when <condition>.\"\"\"\n    # Arrange\n    ...\n    # Act\n    ...\n    # Assert\n    ...\n```\n\n## Mocking Distributed\n\n- Use `torch.distributed.fake_pg` for unit tests\n- Mock `dist.get_rank()` and `dist.get_world_size()` explicitly\n- Don't mock internals of FSDP/DTensor - use integration tests\n\n## GPU Test Constraints\n\n- **Always skip gracefully** when GPU unavailable:\n  ```python\n  CUDA_AVAILABLE = torch.cuda.is_available()\n\n  @pytest.mark.skipif(not CUDA_AVAILABLE, reason=\"CUDA not available\")\n  def test_gpu_feature():\n      ...\n  ```\n- Clean up GPU memory: `torch.cuda.empty_cache()` in fixtures\n- Use smallest possible model/batch for unit tests\n\n## Fixtures\n\n- Prefer `tmp_path` over manual temp directories\n- Use `monkeypatch` for environment variables\n- Scope expensive fixtures appropriately (`session` > `module` > `function`)\n\n## Assertions\n\n- Use `torch.testing.assert_close()` for tensor comparison\n- Specify `rtol`/`atol` explicitly for numerical tests\n- Avoid bare `assert tensor.equal()` - no useful error message\n",".claude/skills/add-archon-model/SKILL.md":"---\nname: add-archon-model\ndescription: Guide for adding a new model to the Archon engine. Use when user wants to add support for a new HuggingFace model architecture in ArchonEngine.\n---\n\n# Add Archon Model\n\nAdd support for a new HuggingFace model architecture in the Archon training engine.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a model to Archon?\"\n- User wants to support a new model family (e.g., Llama, Mistral, DeepSeek) in\n  ArchonEngine\n- User mentions adding a new `ModelSpec` or model type for Archon\n\n## Prerequisites\n\nBefore starting, ensure:\n\n- The target model is available on HuggingFace (has `config.json` with `model_type`)\n- You know the HuggingFace model ID (e.g., `meta-llama/Llama-3-8B`)\n- The model uses a standard transformer architecture (decoder-only)\n\n## Step-by-Step Guide\n\n### Step 1: Analyze the Target Model Architecture\n\nRead the HuggingFace model's source code to extract key architecture information.\n\n**Action**: Fetch and analyze the model's HuggingFace configuration and modeling files.\n\n1. Read the model's `config.json` (via `AutoConfig.from_pretrained`) to identify:\n\n   - `model_type` string (this is the key used for registry lookup)\n   - All architecture hyperparameters (hidden_size, num_layers, etc.)\n   - Any model-specific fields (e.g., `qk_norm`, `attention_bias`, MoE fields)\n\n1. Read the HuggingFace `modeling_*.py` source to identify:\n\n   - **Attention variant**: Does it have Q/K norm? Attention bias? Sliding window?\n     Multi-latent attention?\n   - **FFN variant**: SwiGLU (gate_proj + up_proj + down_proj)? GeGLU? Standard MLP?\n   - **MoE support**: Does it have MoE layers? What router type? Shared experts?\n   - **RoPE variant**: Standard RoPE? YaRN? NTK-aware scaling? What is the inv_freq\n     formula?\n   - **Normalization**: RMSNorm or LayerNorm? Pre-norm or post-norm? Elementwise affine?\n   - **Weight tying**: Does `tie_word_embeddings` appear in config?\n   - **State dict key names**: What are the HF weight key naming conventions?\n\n1. Summarize findings in a checklist like:\n\n```\nTarget model: <name>\nHF model_type: \"<model_type>\" (and variants like \"<model_type>_moe\" if applicable)\nAttention: [standard GQA / with QK norm / with bias / sliding window / ...]\nFFN: [SwiGLU / GeGLU / standard MLP / ...]\nMoE: [no / yes - num_experts, top_k, shared_experts]\nRoPE: [standard / YaRN / NTK-aware / ...]\nNorm: [RMSNorm / LayerNorm] with [pre-norm / post-norm]\nWeight tying: [yes / no]\n```\n\n### Step 2: Select the Reference Model\n\nChoose the closest existing implementation as a starting point:\n\n| Target characteristics               | Reference | Why                                     |\n| ------------------------------------ | --------- | --------------------------------------- |\n| Dense-only, standard GQA, no QK norm | `qwen2`   | Simplest baseline, pure dense           |\n| Has QK norm, or has MoE support      | `qwen3`   | Supports QK norm + MoE + shared experts |\n\n**Action**: Copy the reference model directory as the starting point:\n\n```\nareal/experimental/models/archon/<model>/\n  __init__.py\n  spec.py\n  model/\n    args.py\n    model.py\n    rope.py\n    state_dict_adapter.py\n  infra/\n    parallelize.py\n```\n\n### Step 3: Implement `args.py`\n\nAdapt `<Model>ModelArgs` to match the target model's HuggingFace config fields.\n\n**Key changes from reference**:\n\n1. Update the `@dataclass` fields to match the target model's hyperparameters:\n\n   - Field names should use Archon conventions (`dim`, `n_layers`, `n_heads`,\n     `n_kv_heads`, `vocab_size`, `head_dim`, `hidden_dim`, `norm_eps`, `rope_theta`,\n     etc.)\n   - Default values should match the smallest variant of the target model\n   - Add model-specific fields (e.g., `attention_bias`, `qk_norm`, `sliding_window`)\n\n1. Update `from_hf_config()` to correctly map HuggingFace config attributes:\n\n   - Use `getattr(hf_config, \"field_name\", default)` for optional fields\n   - Handle variant-specific fields (e.g., MoE fields only present in MoE variants)\n   - The method must return an instance of the model args class\n\n**Critical**: Verify every field mapping against the HF model's `config.json`. Incorrect\nmappings here cause silent errors downstream.\n\n**Base class contract** (`BaseModelArgs`):\n\n```python\n@dataclass\nclass <Model>ModelArgs(BaseModelArgs):\n    # ... model-specific fields ...\n\n    @classmethod\n    def from_hf_config(\n        cls,\n        hf_config: PretrainedConfig,\n        is_critic: bool = False,\n        **kwargs,\n    ) -> <Model>ModelArgs:\n        # Map HF config fields to Archon model args\n        ...\n```\n\n### Step 4: Implement `model.py`\n\nAdapt the model architecture to match the target model.\n\n**Key components to adapt**:\n\n1. **Normalization** (`RMSNorm` or similar):\n\n   - Check if `elementwise_affine` is configurable\n   - Check the epsilon default value\n   - If the model uses `LayerNorm`, implement accordingly\n\n1. **Attention** module:\n\n   - Q/K/V projection: Check bias presence (`nn.Linear(..., bias=True/False)`)\n   - QK norm: Add `q_norm`/`k_norm` if the model has them, remove if it doesn't\n   - GQA: `n_kv_heads` \\< `n_heads` for grouped-query attention\n   - Ulysses SP: Keep the `set_cp_group` / `_sp_enabled` pattern from the reference\n   - Output projection: Check bias presence\n\n1. **FeedForward** module:\n\n   - SwiGLU: `w2(silu(w1(x)) * w3(x))` -- most common for modern LLMs\n   - Check bias in linear layers\n   - For MoE models: `MoE` module replaces `FeedForward` on designated layers\n\n1. **TransformerBlock**: Pre-norm (most modern LLMs) vs post-norm\n\n   - MoE layer detection via `_is_moe_layer()` if applicable\n\n1. **Top-level Model** (`<Model>Model(BaseArchonModel)`):\n\n   - `tok_embeddings`, `layers` (as `ModuleDict`), `norm`, `output`/`score`\n   - `init_weights()`: Match initialization scheme from HF\n   - `init_buffers()`: RoPE cache + MoE buffers\n   - `forward()`: Must follow `BaseArchonModel` signature:\n     `(tokens, positions, cu_seqlens, max_seqlen, tree_attn_meta=None) -> Tensor`\n\n**Base class contract** (`BaseArchonModel`):\n\n```python\nclass <Model>Model(BaseArchonModel):\n    def forward(self, tokens, positions, cu_seqlens, max_seqlen, tree_attn_meta=None) -> torch.Tensor: ...\n    def init_weights(self) -> None: ...\n    def init_buffers(self, buffer_device) -> None: ...\n```\n\n### Step 5: Implement `rope.py`\n\nHandle the rotary position embedding variant.\n\n**Options**:\n\n1. **Standard RoPE** (same as qwen2/qwen3): Re-export from qwen2:\n\n   ```python\n   from areal.experimental.models.archon.qwen2.model.rope import (\n       apply_rotary_emb,\n       precompute_rope_cache,\n       repeat_kv,\n       reshape_for_broadcast,\n       rotate_half,\n   )\n   ```\n\n1. **Custom RoPE** (YaRN, NTK-aware, etc.): Implement custom `precompute_rope_cache()`\n   and `apply_rotary_emb()` functions. The key difference is usually in how `inv_freq`\n   is computed (scaling factors, interpolation, etc.).\n\n### Step 6: Implement `state_dict_adapter.py`\n\nMap between HuggingFace and Archon weight key names.\n\n**This is the most error-prone step.** The adapter must correctly handle:\n\n1. **Key name mapping** (`from_hf_map` dict):\n\n   - Embedding: `model.embed_tokens.weight` -> `tok_embeddings.weight`\n   - Attention: `model.layers.{}.self_attn.q_proj.weight` ->\n     `layers.{}.attention.wq.weight`\n   - FFN: `model.layers.{}.mlp.gate_proj.weight` -> `layers.{}.feed_forward.w1.weight`\n   - Norms: `model.layers.{}.input_layernorm.weight` ->\n     `layers.{}.attention_norm.weight`\n   - Output: `lm_head.weight` -> `output.weight`\n   - Skip keys (set to `None`): `rotary_emb.inv_freq` (computed at runtime)\n   - Model-specific keys: bias terms, QK norm weights, etc.\n\n1. **Reverse mapping** (`to_hf_map`): Auto-generated from `from_hf_map`\n\n1. **MoE expert weights** (if applicable): 3D\\<->2D conversion for expert weights. Copy\n   the MoE handling from qwen3 if the model has MoE.\n\n1. **Weight tying**: Skip `output.weight` during `to_hf()` if `tie_word_embeddings=True`\n\n**Verification approach**: After implementation, the adapter should satisfy:\n\n```python\n# Roundtrip: archon -> hf -> archon preserves all keys\nhf_sd = adapter.to_hf(archon_sd)\nroundtrip_sd = adapter.from_hf(hf_sd)\nassert set(roundtrip_sd.keys()) == set(archon_sd.keys())\n```\n\n**Base class contract** (`BaseStateDictAdapter`):\n\n```python\nclass <Model>StateDictAdapter(BaseStateDictAdapter):\n    def from_hf(self, hf_state_dict) -> dict[str, Any]: ...\n    def to_hf(self, archon_state_dict) -> dict[str, Any]: ...\n    def convert_single_to_hf(self, name, tensor) -> list[tuple[str, torch.Tensor]]: ...\n```\n\n### Step 7: Implement `parallelize.py`\n\nDefine the parallelization strategy for the model.\n\n**The parallelize function** applies parallelism in this order:\n\n1. TP (Tensor Parallelism) -- shard attention/FFN across devices\n1. EP (Expert Parallelism) -- for MoE models only\n1. CP (Context Parallelism / Ulysses SP) -- sequence parallelism\n1. AC (Activation Checkpointing) -- memory optimization\n1. torch.compile -- compilation optimization\n1. FSDP (Fully Sharded Data Parallelism) -- data parallelism\n\n**Key adaptations by model architecture**:\n\n- **Attention with QK norm**: wq/wk use `use_local_output=False` (DTensor output for\n  norm), add `SequenceParallel(sequence_dim=2)` for q_norm/k_norm\n- **Attention without QK norm**: wq/wk/wv all use `use_local_output=True`\n- **Attention with bias**: Bias terms follow the same parallel plan as their weights\n- **MoE layers**: Separate TP plan for MoE input/output, router gate, and expert\n  weights. Copy from qwen3's `apply_moe_ep_tp()` and `apply_non_moe_tp()`\n- **Dense-only models**: Simpler plan without MoE handling. Copy from qwen2\n\n**Function signature** (must match `ParallelizeFn` protocol):\n\n```python\ndef parallelize_<model>(\n    model: nn.Module,\n    parallel_dims: ArchonParallelDims,\n    param_dtype: torch.dtype = torch.bfloat16,\n    reduce_dtype: torch.dtype = torch.float32,\n    loss_parallel: bool = True,\n    cpu_offload: bool = False,\n    reshard_after_forward_policy: str = \"default\",\n    ac_config: ActivationCheckpointConfig | None = None,\n    enable_compile: bool = True,\n) -> nn.Module:\n```\n\n### Step 8: Create `spec.py` and Register\n\nAssemble the `ModelSpec` and register it.\n\n```python\nfrom areal.experimental.models.archon.model_spec import ModelSpec, register_model_spec\nfrom areal.experimental.models.archon.pipeline_parallel import pipeline_llm\nfrom areal.experimental.models.archon.<model>.infra.parallelize import parallelize_<model>\nfrom areal.experimental.models.archon.<model>.model.args import <Model>ModelArgs\nfrom areal.experimental.models.archon.<model>.model.model import <Model>Model\nfrom areal.experimental.models.archon.<model>.model.state_dict_adapter import (\n    <Model>StateDictAdapter,\n)\n\n<MODEL>_SPEC = ModelSpec(\n    name=\"<Model>\",\n    model_class=<Model>Model,\n    model_args_class=<Model>ModelArgs,\n    state_dict_adapter_class=<Model>StateDictAdapter,\n    parallelize_fn=parallelize_<model>,\n    supported_model_types=frozenset({\"<model_type>\"}),  # From HF config.json\n    pipelining_fn=pipeline_llm,\n)\n\n# Auto-register when module is imported\nregister_model_spec(<MODEL>_SPEC)\n\n__all__ = [\"<MODEL>_SPEC\"]\n```\n\n**Note**: `supported_model_types` should include all HF `model_type` strings that this\nimplementation handles (e.g., `{\"qwen3\", \"qwen3_moe\"}` for Qwen3).\n\n### Step 9: Register in `__init__.py`\n\nAdd the import to `areal/experimental/models/archon/__init__.py`:\n\n```python\nfrom areal.experimental.models.archon.<model> import spec as <model>_spec  # noqa: F401\n```\n\nThis triggers auto-registration when the module is imported.\n\n### Step 10: Verify and Test\n\nVerification should be done in stages, adapting based on available hardware and the test\npatterns in `tests/experimental/archon/`.\n\n**Before writing tests**, examine the existing test files to understand current\npatterns:\n\n```\ntests/experimental/archon/\n  conftest.py             -- Pytest configuration (version checks)\n  utils.py                -- Shared utilities (model loading, comparison)\n  test_qwen3_args.py      -- Args unit tests (CPU-only)\n  test_state_dict_adapter.py  -- State dict roundtrip tests\n  test_weight_sync.py     -- Weight completeness tests (meta device)\n  test_forward.py         -- Forward precision comparison (single GPU)\n  ...\n```\n\n**Test stages** (write tests appropriate for the model's complexity):\n\n#### Stage 1: Args Tests (CPU-only, always write these)\n\nTest `from_hf_config()` with mock HuggingFace configs:\n\n```python\n# Pattern: Create mock PretrainedConfig, verify args mapping\nfrom unittest.mock import MagicMock\n\ndef test_args_from_hf_config():\n    hf_config = MagicMock()\n    hf_config.hidden_size = 4096\n    hf_config.num_hidden_layers = 32\n    # ... set all required fields\n    args = <Model>ModelArgs.from_hf_config(hf_config)\n    assert args.dim == 4096\n    assert args.n_layers == 32\n```\n\n#### Stage 2: State Dict Adapter Tests (CPU-only)\n\nTest key mapping roundtrip:\n\n```python\ndef test_state_dict_roundtrip():\n    # Create adapter with mock config\n    adapter = <Model>StateDictAdapter(mock_config)\n    # Create fake archon state dict with expected keys\n    archon_sd = {\"tok_embeddings.weight\": torch.randn(vocab, dim), ...}\n    # Roundtrip\n    hf_sd = adapter.to_hf(archon_sd)\n    roundtrip = adapter.from_hf(hf_sd)\n    assert set(roundtrip.keys()) == set(archon_sd.keys())\n```\n\n#### Stage 3: Weight Completeness (meta device, CPU-only)\n\nVerify all model parameters have HF mappings:\n\n```python\ndef test_weight_completeness():\n    # Create model on meta device\n    with torch.device(\"meta\"):\n        model = <Model>Model(args)\n    adapter = <Model>StateDictAdapter(hf_config)\n    # Check every archon param has a HF mapping\n    for name, _ in model.named_parameters():\n        hf_pairs = adapter.convert_single_to_hf(name, torch.empty(0))\n        assert len(hf_pairs) > 0, f\"No HF mapping for {name}\"\n```\n\n#### Stage 4: Forward Precision (single GPU, if available)\n\nCompare Archon model output against HuggingFace reference:\n\n```python\n@pytest.mark.skipif(not torch.cuda.is_available(), reason=\"Requires CUDA\")\ndef test_forward_matches_hf():\n    # Load both HF and Archon models\n    # Run forward on same input\n    # Compare logits within tolerance\n```\n\n**Important**: Do NOT hardcode the test categories. Inspect the existing test files in\n`tests/experimental/archon/` and follow the same patterns, fixtures, and markers. Adapt\ntest scope to the model's specific features (e.g., add MoE-specific tests only if the\nmodel has MoE).\n\n## Reference Implementations\n\n| Model | Directory                                 | Features                                                |\n| ----- | ----------------------------------------- | ------------------------------------------------------- |\n| Qwen2 | `areal/experimental/models/archon/qwen2/` | Dense, attention bias, no QK norm                       |\n| Qwen3 | `areal/experimental/models/archon/qwen3/` | Dense + MoE, QK norm, no attention bias, shared experts |\n\n## Architecture Decision Map\n\n| Feature             | qwen2    | qwen3                      | What to check in target model                            |\n| ------------------- | -------- | -------------------------- | -------------------------------------------------------- |\n| Attention bias      | Yes      | No                         | `attention_bias` in HF config                            |\n| QK norm             | No       | Yes                        | `qk_norm` in HF config or QKNorm module in modeling file |\n| MoE                 | No       | Yes                        | `num_experts`/`num_local_experts` in HF config           |\n| Shared experts      | No       | Yes                        | `num_shared_experts` in HF config                        |\n| Decoder sparse step | No       | Yes                        | `decoder_sparse_step` in HF config                       |\n| Weight tying        | Both     | Both                       | `tie_word_embeddings` in HF config                       |\n| RoPE                | Standard | Standard (re-export qwen2) | Check inv_freq formula in HF modeling code               |\n\n## Common Mistakes\n\n- Not mapping all HF keys in `state_dict_adapter.py` (causes silent weight drops)\n- Wrong `from_hf_config()` field mapping (uses wrong HF config attribute name)\n- Forgetting to handle `None` keys in `from_hf_map` (keys to skip like\n  `rotary_emb.inv_freq`)\n- Missing MoE expert weight 3D\\<->2D conversion when model has MoE\n- Wrong TP plan for attention with/without QK norm (`use_local_output` must match)\n- Forgetting to add import line in `areal/experimental/models/archon/__init__.py`\n- Not including all `model_type` variants in `supported_model_types` frozenset\n- Using `print` instead of `areal.utils.logging.getLogger()`\n\n## File Checklist\n\nAfter completion, verify all files exist and are consistent:\n\n- [ ] `areal/experimental/models/archon/<model>/__init__.py`\n- [ ] `areal/experimental/models/archon/<model>/spec.py` -- ModelSpec + register\n- [ ] `areal/experimental/models/archon/<model>/model/args.py` -- ModelArgs +\n  from_hf_config\n- [ ] `areal/experimental/models/archon/<model>/model/model.py` -- Model + Attention +\n  FFN\n- [ ] `areal/experimental/models/archon/<model>/model/rope.py` -- RoPE (or re-export)\n- [ ] `areal/experimental/models/archon/<model>/model/state_dict_adapter.py` -- Key\n  mapping\n- [ ] `areal/experimental/models/archon/<model>/infra/parallelize.py` -- Parallel\n  strategy\n- [ ] `areal/experimental/models/archon/__init__.py` -- Import line added\n- [ ] `tests/experimental/archon/test_<model>_*.py` -- Tests\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/skills/add-archon-model/SKILL.md\nInvocation: /add-archon-model <model_name>\n\n## Purpose\n\nSemi-automated guide for adding new model architectures to the Archon training engine.\nUnlike simpler skills (add-reward, add-dataset), this skill actively guides Claude to:\n1. Analyze HuggingFace source code to extract architecture details\n2. Select the closest reference implementation (qwen2 or qwen3)\n3. Generate code skeletons adapted to the target architecture\n4. Create appropriate tests based on existing test patterns\n\n## How to Update\n\n### When New Reference Models Are Added\n1. Add to \"Reference Implementations\" table\n2. Update \"Architecture Decision Map\" with new feature columns\n3. Update Step 2 (reference selection) with new options\n\n### When Base Classes Change\n1. Update contract signatures in Steps 3, 4, 6, 7\n2. Update file checklist if new files are required\n\n### When ModelSpec Changes\n1. Update Step 8 with new ModelSpec fields\n2. Update spec.py template\n\n### When Test Patterns Change\n1. Update Step 10 with new test patterns\n2. Do NOT hardcode test categories -- keep it flexible\n\n### Important Design Decisions\n- This skill is SEMI-AUTOMATED: Claude should read HF source and generate code,\n  not just provide templates for the user to fill in manually\n- The skill references existing test files rather than hardcoding test categories,\n  ensuring it stays current as the test suite evolves\n- Reference model selection (qwen2 vs qwen3) is based on MoE and QK norm presence\n\n================================================================================\n-->\n",".claude/skills/add-dataset/SKILL.md":"---\nname: add-dataset\ndescription: Guide for adding a new dataset loader to AReaL. Use when user wants to add a new dataset.\n---\n\n# Add Dataset\n\nAdd a new dataset loader to AReaL.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a dataset?\"\n- User wants to integrate a new dataset\n- User mentions creating a dataset loader\n\n## Step-by-Step Guide\n\n### Step 1: Create Dataset File\n\nCreate `areal/dataset/<name>.py`:\n\n```python\nfrom datasets import Dataset, load_dataset\n\n\ndef get_<name>_sft_dataset(\n    path: str,\n    split: str,\n    tokenizer,\n    max_length: int | None = None,\n) -> Dataset:\n    \"\"\"Load dataset for SFT training.\n\n    Args:\n        path: Path to dataset (HuggingFace hub or local path)\n        split: Dataset split (train/validation/test)\n        tokenizer: Tokenizer for processing\n        max_length: Maximum sequence length (optional)\n\n    Returns:\n        HuggingFace Dataset with processed samples\n    \"\"\"\n    dataset = load_dataset(path=path, split=split)\n\n    def process(sample):\n        # Tokenize the full sequence (prompt + response)\n        seq_token = tokenizer.encode(\n            sample[\"question\"] + sample[\"answer\"] + tokenizer.eos_token\n        )\n        prompt_token = tokenizer.encode(sample[\"question\"])\n        # Loss mask: 0 for prompt, 1 for response\n        loss_mask = [0] * len(prompt_token) + [1] * (len(seq_token) - len(prompt_token))\n        return {\"input_ids\": seq_token, \"loss_mask\": loss_mask}\n\n    dataset = dataset.map(process).remove_columns([\"question\", \"answer\"])\n\n    if max_length is not None:\n        dataset = dataset.filter(lambda x: len(x[\"input_ids\"]) <= max_length)\n\n    return dataset\n\n\ndef get_<name>_rl_dataset(\n    path: str,\n    split: str,\n    tokenizer,\n    max_length: int | None = None,\n) -> Dataset:\n    \"\"\"Load dataset for RL training.\n\n    Args:\n        path: Path to dataset\n        split: Dataset split\n        tokenizer: Tokenizer for length filtering\n        max_length: Maximum sequence length\n\n    Returns:\n        HuggingFace Dataset with prompts and answers for reward computation\n    \"\"\"\n    dataset = load_dataset(path=path, split=split)\n\n    def process(sample):\n        messages = [\n            {\n                \"role\": \"user\",\n                \"content\": sample[\"question\"],\n            }\n        ]\n        return {\"messages\": messages, \"answer\": sample[\"answer\"]}\n\n    dataset = dataset.map(process).remove_columns([\"question\"])\n\n    if max_length is not None:\n\n        def filter_length(sample):\n            content = sample[\"messages\"][0][\"content\"]\n            tokens = tokenizer.encode(content)\n            return len(tokens) <= max_length\n\n        dataset = dataset.filter(filter_length)\n\n    return dataset\n```\n\n### Step 2: Register in __init__.py\n\nUpdate `areal/dataset/__init__.py`:\n\n```python\n# Add to VALID_DATASETS\nVALID_DATASETS = [\n    # ... existing datasets\n    \"<name>\",\n]\n\n# Add to _get_custom_dataset function\ndef _get_custom_dataset(name: str, ...):\n    # ... existing code\n    elif name == \"<name>\":\n        from areal.dataset.<name> import get_<name>_sft_dataset, get_<name>_rl_dataset\n        if dataset_type == \"sft\":\n            return get_<name>_sft_dataset(path, split, max_length, tokenizer)\n        else:\n            return get_<name>_rl_dataset(path, split, max_length, tokenizer)\n```\n\n### Step 3: Add Config (Optional)\n\nIf the dataset needs special configuration, add to `areal/api/cli_args.py`:\n\n```python\n@dataclass\nclass TrainDatasetConfig:\n    # ... existing fields\n    <name>_specific_field: Optional[str] = None\n```\n\n### Step 4: Add Tests\n\nCreate `tests/test_<name>_dataset.py`:\n\n```python\nimport pytest\nfrom areal.dataset.<name> import get_<name>_sft_dataset, get_<name>_rl_dataset\n\ndef test_sft_dataset_loads(tokenizer):\n    dataset = get_<name>_sft_dataset(\"path/to/data\", split=\"train\", tokenizer=tokenizer)\n    assert len(dataset) > 0\n    assert \"input_ids\" in dataset.column_names\n    assert \"loss_mask\" in dataset.column_names\n\ndef test_rl_dataset_loads(tokenizer):\n    dataset = get_<name>_rl_dataset(\"path/to/data\", split=\"train\", tokenizer=tokenizer)\n    assert len(dataset) > 0\n    assert \"messages\" in dataset.column_names\n    assert \"answer\" in dataset.column_names\n```\n\n## Reference Implementations\n\n| Dataset    | File                               | Description              |\n| ---------- | ---------------------------------- | ------------------------ |\n| GSM8K      | `areal/dataset/gsm8k.py`           | Math word problems       |\n| Geometry3K | `areal/dataset/geometry3k.py`      | Geometry problems        |\n| CLEVR      | `areal/dataset/clevr_count_70k.py` | Visual counting          |\n| HH-RLHF    | `areal/dataset/hhrlhf.py`          | Helpfulness/Harmlessness |\n| TORL       | `areal/dataset/torl_data.py`       | Tool-use RL              |\n\n## Required Fields\n\n### SFT Dataset\n\n```python\n{\n    \"messages\": [\n        {\"role\": \"user\", \"content\": \"...\"},\n        {\"role\": \"assistant\", \"content\": \"...\"},\n    ]\n}\n```\n\n### RL Dataset\n\n```python\n{\n    \"messages\": [\n        {\"role\": \"user\", \"content\": \"...\"},\n    ],\n    \"answer\": \"ground_truth_for_reward\",\n    # Optional metadata for reward function\n}\n```\n\n## Common Mistakes\n\n- ❌ Returning `List[Dict]` instead of HuggingFace `Dataset`\n- ❌ Using Python loops instead of `dataset.map()`/`filter()`\n- ❌ Missing `\"messages\"` field for RL datasets\n- ❌ Wrong message format (should be list of dicts with `role` and `content`)\n- ❌ Not registering in `__init__.py`\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/skills/add-dataset/SKILL.md\nInvocation: /add-dataset <name>\n\n## Purpose\n\nStep-by-step guide for adding new dataset loaders.\n\n## How to Update\n\n### When Dataset API Changes\n1. Update the code templates\n2. Update required fields section\n3. Update registration example\n\n### When New Dataset Types Added\n1. Add to \"Reference Implementations\" table\n2. Add any new required fields\n\n================================================================================\n-->\n",".claude/skills/add-reward/SKILL.md":"---\nname: add-reward\ndescription: Guide for adding a new reward function to AReaL. Use when user wants to create a reward function.\n---\n\n# Add Reward\n\nAdd a new reward function to AReaL.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a reward function?\"\n- User wants to implement custom rewards\n- User mentions reward computation\n\n## Step-by-Step Guide\n\n### Step 1: Create Reward File\n\nCreate `areal/reward/<name>.py`:\n\n```python\nfrom typing import Any\n\nfrom areal.utils import logging\n\nlogger = logging.getLogger(\"MyReward\")\n\n\ndef <name>_reward_fn(\n    prompt: str,\n    completions: str,\n    prompt_ids,\n    completion_ids,\n    answer: str | None = None,\n    **kwargs: Any,\n) -> float:\n    \"\"\"Compute reward for a single completion.\n\n    Args:\n        prompt: Prompt string\n        completions: Completion string (model output)\n        prompt_ids: Tokenized prompt IDs\n        completion_ids: Tokenized completion IDs\n        answer: Ground truth answer from dataset (optional)\n        **kwargs: Additional data from dataset\n\n    Returns:\n        Reward value (float), typically 0.0 or 1.0\n    \"\"\"\n    try:\n        # Extract answer from completion\n        extracted = _extract_answer(completions)\n\n        # Compare with ground truth\n        if answer is not None and extracted == str(answer):\n            return 1.0\n        return 0.0\n    except Exception:\n        logger.warning(\"Exception in reward computation\", exc_info=True)\n        return 0.0\n\n\ndef _extract_answer(completion: str) -> str:\n    \"\"\"Extract the answer from a completion string.\n\n    Implement your extraction logic here.\n    \"\"\"\n    # Example: Extract content from \\boxed{}\n    import re\n\n    match = re.search(r\"\\\\boxed\\{([^}]+)\\}\", completion)\n    if match:\n        return match.group(1).strip()\n    return completion.strip()\n```\n\n### Step 2: Register in __init__.py\n\nUpdate `areal/reward/__init__.py`:\n\n```python\n# Add to VALID_REWARD_FN\nVALID_REWARD_FN = [\n    # ... existing reward functions\n    \"<name>\",\n]\n\n# Add to get_reward_fn function\ndef get_reward_fn(name: str, **kwargs):\n    # ... existing code\n    elif name == \"<name>\":\n        from areal.reward.<name> import <name>_reward_fn\n        return <name>_reward_fn\n```\n\n### Step 3: Handle Blocking Operations\n\nIf your reward function uses blocking operations (e.g., API calls, model inference), the\nworkflow will wrap it with `AsyncRewardWrapper`:\n\n```python\n# In your workflow\nfrom areal.reward import AsyncRewardWrapper\n\nself.reward_fn = AsyncRewardWrapper(reward_fn)\n\n# Then call it asynchronously\nrewards = await self.reward_fn(prompt, completions, **data)\n```\n\n### Step 4: Add Tests\n\nCreate `tests/test_<name>_reward.py`:\n\n```python\nimport pytest\nfrom areal.reward.<name> import <name>_reward_fn\n\ndef test_reward_correct_answer():\n    reward = <name>_reward_fn(\n        prompt=\"What is 2+2?\",\n        completions=\"The answer is \\\\boxed{4}\",\n        prompt_ids=None,\n        completion_ids=None,\n        answer=\"4\",\n    )\n    assert reward == 1.0\n\ndef test_reward_wrong_answer():\n    reward = <name>_reward_fn(\n        prompt=\"What is 2+2?\",\n        completions=\"The answer is \\\\boxed{5}\",\n        prompt_ids=None,\n        completion_ids=None,\n        answer=\"4\",\n    )\n    assert reward == 0.0\n```\n\n## Reference Implementations\n\n| Reward     | File                              | Description                  |\n| ---------- | --------------------------------- | ---------------------------- |\n| GSM8K      | `areal/reward/gsm8k.py`           | Math answer verification     |\n| Geometry3K | `areal/reward/geometry3k.py`      | Geometry answer verification |\n| CLEVR      | `areal/reward/clevr_count_70k.py` | Counting verification        |\n| MathVerify | `areal/reward/math_verify.py`     | General math verification    |\n\n## Function Signature\n\nAll reward functions must follow this signature:\n\n```python\ndef reward_fn(\n    prompt: str,               # Input prompt string\n    completions: str,          # Model completion string\n    prompt_ids,                # Tokenized prompt\n    completion_ids,            # Tokenized completion\n    **kwargs: Any,             # Additional data from dataset (e.g., answer)\n) -> float:                    # Reward value (typically 0.0 or 1.0)\n```\n\n**Note**: The reward function is called once per sample. Batching is handled by\n`AsyncRewardWrapper` in the workflow.\n\n## Key Requirements\n\n1. **Deterministic**: Same inputs should produce same outputs\n1. **Return float**: Output is a single float value per sample\n1. **No blocking in async context**: Use `AsyncRewardWrapper` if needed\n1. **Logging**: Use `areal.utils.logging`, not `print`\n1. **Handle exceptions**: Return 0.0 on error, don't raise\n\n## Common Mistakes\n\n- ❌ Returning a tensor instead of a float\n- ❌ Expecting batched inputs (reward is called per sample)\n- ❌ Non-deterministic behavior\n- ❌ Blocking operations without `AsyncRewardWrapper`\n- ❌ Raising exceptions instead of returning 0.0\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/skills/add-reward/SKILL.md\nInvocation: /add-reward <name>\n\n## Purpose\n\nStep-by-step guide for adding new reward functions.\n\n## How to Update\n\n### When Reward API Changes\n1. Update the function signature section\n2. Update the code template\n3. Update key requirements\n\n### When New Reward Patterns Emerge\n1. Add to \"Reference Implementations\" table\n2. Add examples for new patterns\n\n================================================================================\n-->\n",".claude/skills/add-unit-tests/SKILL.md":"---\nname: add-unit-tests\ndescription: Guide for adding unit tests to AReaL. Use when user wants to add tests for new functionality or increase test coverage.\n---\n\n# Add Unit Tests\n\nAdd unit tests to AReaL following the project's testing conventions.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add tests?\"\n- User wants to increase test coverage\n- User needs to write tests for new functionality\n- User wants to understand AReaL testing patterns\n\n## Step-by-Step Guide\n\n### Step 1: Understand Test Types\n\nAReaL has two main test categories:\n\n| Test Type             | Purpose                            | Location Pattern                   | How It Runs                                |\n| --------------------- | ---------------------------------- | ---------------------------------- | ------------------------------------------ |\n| **Unit Tests**        | Test individual functions/modules  | `tests/test_<module>_<feature>.py` | Directly via pytest                        |\n| **Distributed Tests** | Test distributed/parallel behavior | `tests/torchrun/run_*.py`          | Via torchrun (called by pytest subprocess) |\n\n**Note**: All tests are invoked via pytest. Distributed tests use `torchrun` but are\nstill called from pytest test files.\n\n### Step 2: Create Test File Structure\n\nCreate test file with naming convention: `test_<module>_<feature>.py`\n\n```python\nimport pytest\nimport torch\n\n# Import the module to test\nfrom areal.dataset.gsm8k import get_gsm8k_sft_dataset\nfrom tests.utils import get_dataset_path  # Optional test utilities\n# For mocking tokenizer: from unittest.mock import MagicMock\n```\n\n### Step 3: Write Test Functions\n\nFollow Arrange-Act-Assert pattern:\n\n```python\ndef test_function_under_condition_returns_expected():\n    \"\"\"Test that function returns expected value under condition.\"\"\"\n    # Arrange\n    input_data = 5\n    expected_output = 10\n\n    # Act\n    result = function_under_test(input_data)\n\n    # Assert\n    assert result == expected_output\n```\n\n### Step 4: Add Pytest Markers and CI Strategy\n\nUse appropriate pytest markers:\n\n| Marker                                  | When to Use                                                  |\n| --------------------------------------- | ------------------------------------------------------------ |\n| `@pytest.mark.slow`                     | Test takes > 10 seconds (excluded from CI by default)        |\n| `@pytest.mark.ci`                       | Slow test that must run in CI (use with `@pytest.mark.slow`) |\n| `@pytest.mark.asyncio`                  | Async test functions                                         |\n| `@pytest.mark.skipif(cond, reason=...)` | Conditional skip                                             |\n| `@pytest.mark.parametrize(...)`         | Parameterized tests                                          |\n\n**CI Test Strategy**:\n\n- `@pytest.mark.slow`: Excluded from CI by default (CI runs `pytest -m \"not slow\"`)\n- `@pytest.mark.slow` + `@pytest.mark.ci`: Slow but must run in CI\n- No marker: Runs in CI (fast unit tests)\n\n```python\n@pytest.mark.asyncio\nasync def test_async_function():\n    result = await async_function()\n    assert result == expected\n\n@pytest.mark.skipif(not torch.cuda.is_available(), reason=\"CUDA not available\")\ndef test_gpu_feature():\n    tensor = torch.tensor([1, 2, 3], device=\"cuda\")\n    # ... assertions\n\n@pytest.mark.parametrize(\"batch_size\", [1, 4, 16])\ndef test_with_parameters(batch_size):\n    # Parameterized test\n\n@pytest.mark.slow\ndef test_slow_function():\n    # Excluded from CI by default\n\n@pytest.mark.slow\n@pytest.mark.ci\ndef test_slow_but_required_in_ci():\n    # Slow but must run in CI\n```\n\n### Step 5: Mock Distributed Environment\n\nFor unit tests that need distributed mocks:\n\n```python\nimport torch.distributed as dist\n\ndef test_distributed_function(monkeypatch):\n    monkeypatch.setattr(dist, \"get_rank\", lambda: 0)\n    monkeypatch.setattr(dist, \"get_world_size\", lambda: 2)\n    result = distributed_function()\n    assert result == expected\n```\n\n### Step 6: Handle GPU Dependencies\n\nAlways skip gracefully when GPU unavailable:\n\n```python\nCUDA_AVAILABLE = torch.cuda.is_available()\n\n@pytest.mark.skipif(not CUDA_AVAILABLE, reason=\"CUDA not available\")\ndef test_gpu_function():\n    tensor = torch.tensor([1, 2, 3], device=\"cuda\")\n    # ... assertions\n```\n\n## Key Requirements (Based on testing.md)\n\n### Mocking Distributed\n\n- Use `torch.distributed.fake_pg` for unit tests\n- Mock `dist.get_rank()` and `dist.get_world_size()` explicitly\n- Don't mock internals of FSDP/DTensor\n\n### GPU Test Constraints\n\n- **Always skip gracefully** when GPU unavailable\n- Clean up GPU memory: `torch.cuda.empty_cache()` in fixtures\n- Use smallest possible model/batch for unit tests\n\n### Assertions\n\n- Use `torch.testing.assert_close()` for tensor comparison\n- Specify `rtol`/`atol` explicitly for numerical tests\n- Avoid bare `assert tensor.equal()` - no useful error message\n\n## Reference Implementations\n\n| Test File                        | Description                            | Key Patterns                                      |\n| -------------------------------- | -------------------------------------- | ------------------------------------------------- |\n| `tests/test_utils.py`            | Utility function tests                 | Fixtures, parametrized tests                      |\n| `tests/test_examples.py`         | Integration tests with dataset loading | Dataset path resolution, success pattern matching |\n| `tests/test_fsdp_engine_nccl.py` | Distributed tests                      | Torchrun integration                              |\n\n## Common Mistakes\n\n- ❌ **Missing test file registration**: Ensure file follows `test_*.py` naming\n- ❌ **GPU dependency without skip**: Always use `@pytest.mark.skipif` for GPU tests\n- ❌ **Incorrect tensor comparisons**: Use `torch.testing.assert_close()` not\n  `assert tensor.equal()`\n- ❌ **Memory leaks in GPU tests**: Clean up with `torch.cuda.empty_cache()`\n- ❌ **Mocking too much**: Don't mock FSDP/DTensor internals\n- ❌ **Unclear test names**: Follow `test_<what>_<condition>_<expected>` pattern\n- ❌ **No docstrings**: Add descriptive docstrings to test functions\n\n## Integration with Other Skills\n\nThis skill complements other AReaL development skills:\n\n- **After `/add-dataset`**: Add tests for new dataset loaders\n- **After `/add-workflow`**: Add tests for new workflows\n- **After `/add-reward`**: Add tests for new reward functions\n- **With `planner` agent**: Reference this skill when planning test implementation\n\n## Running Tests\n\n```bash\n# First check GPU availability (many tests require GPU)\npython -c \"import torch; print('GPU available:', torch.cuda.is_available())\"\n\n# Run specific test file\nuv run pytest tests/test_<name>.py\n\n# Skip slow tests (CI default)\nuv run pytest -m \"not slow\"\n\n# Run with verbose output\nuv run pytest -v\n\n# Run distributed tests (requires torchrun and multi-GPU)\n# Note: Usually invoked via pytest test files\ntorchrun --nproc_per_node=2 tests/torchrun/run_<test>.py\n```\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/skills/add-unit-tests/SKILL.md\nInvocation: /add-unit-tests\n\n## Purpose\n\nStep-by-step guide for adding unit tests to AReaL.\n\n## How to Update\n\n### When Testing Conventions Change\n1. Update \"Key Requirements\" section based on `testing.md`\n2. Update test examples to match new patterns\n3. Update reference implementations\n\n### When Test Types Need Update\n1. Update \"Understand Test Types\" table (currently two main types)\n2. Add new examples if needed\n3. Update common mistakes\n\n### Integration with Other Skills\nEnsure references to other skills (`/add-dataset`, `/add-workflow`, `/add-reward`) remain accurate.\n\n================================================================================\n-->\n",".claude/skills/add-workflow/SKILL.md":"---\nname: add-workflow\ndescription: Guide for adding a new RolloutWorkflow to AReaL. Use when user wants to create a new workflow.\n---\n\n# Add Workflow\n\nAdd a new RolloutWorkflow implementation to AReaL.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a workflow?\"\n- User wants to create a new RolloutWorkflow\n- User mentions implementing a custom rollout\n\n## Prerequisites\n\nBefore starting, ensure you understand:\n\n- The workflow's purpose and requirements\n- Input/output data format\n- Reward function to use\n\n## Step-by-Step Guide\n\n### Step 1: Create Workflow File\n\nCreate `areal/workflow/<name>.py`:\n\n```python\nimport uuid\nfrom typing import Any, Callable\n\nimport torch\n\nfrom areal.api.cli_args import GenerationHyperparameters\nfrom areal.api.engine_api import InferenceEngine\nfrom areal.api.io_struct import ModelRequest, ModelResponse\nfrom areal.api.reward_api import AsyncRewardWrapper\nfrom areal.api.workflow_api import RolloutWorkflow\nfrom areal.utils import logging\n\nlogger = logging.getLogger(\"MyWorkflow\")\n\n\nclass MyWorkflow(RolloutWorkflow):\n    \"\"\"Description of your workflow.\"\"\"\n\n    def __init__(\n        self,\n        gconfig: GenerationHyperparameters,\n        tokenizer,\n        reward_fn: Callable,\n    ):\n        self.gconfig = gconfig.new_with_stop_and_pad_token_ids(tokenizer)\n        self.tokenizer = tokenizer\n        self.async_reward_fn = AsyncRewardWrapper(reward_fn)\n\n    async def arun_episode(\n        self,\n        engine: InferenceEngine,\n        data: dict[str, Any],\n    ) -> dict[str, torch.Tensor]:\n        \"\"\"Run a single episode. MUST be async and non-blocking.\"\"\"\n\n        # 1. Prepare input_ids from data\n        input_ids = self.tokenizer.apply_chat_template(\n            data[\"messages\"],\n            tokenize=True,\n            add_generation_prompt=True,\n        )\n\n        # 2. Build ModelRequest\n        req = ModelRequest(\n            rid=uuid.uuid4().hex,\n            input_ids=list(input_ids),\n            gconfig=self.gconfig.new(n_samples=1),\n            tokenizer=self.tokenizer,\n        )\n\n        # 3. Generate completion (async)\n        resp: ModelResponse = await engine.agenerate(req)\n\n        # 4. Compute reward (async)\n        prompt_str = self.tokenizer.decode(input_ids)\n        completion_str = self.tokenizer.decode(resp.output_tokens)\n        reward = await self.async_reward_fn(\n            prompt_str,\n            completion_str,\n            resp.input_tokens,\n            resp.output_tokens,\n            **data,\n        )\n\n        # 5. Return results in expected format\n        return {\n            \"input_ids\": torch.tensor(resp.input_tokens),\n            \"output_ids\": torch.tensor(resp.output_tokens),\n            \"reward\": torch.tensor(reward),\n        }\n```\n\n### Step 2: Register in __init__.py\n\nAdd to `areal/workflow/__init__.py`:\n\n```python\nfrom areal.workflow.<name> import MyWorkflow\n\n__all__ = [\n    # ... existing exports\n    \"MyWorkflow\",\n]\n```\n\n### Step 3: Update Entry Script\n\nUpdate your training script to use the new workflow:\n\n```python\ntrainer.train(\n    workflow=\"areal.workflow.<name>.MyWorkflow\",\n    # ... other args\n)\n```\n\n### Step 4: Add Tests\n\nCreate `tests/test_<name>_workflow.py`:\n\n```python\nimport pytest\nfrom areal.workflow.<name> import MyWorkflow\n\n@pytest.mark.asyncio\nasync def test_workflow_basic():\n    # Test basic functionality\n    pass\n```\n\n## Reference Implementations\n\n| Workflow           | File                            | Description                |\n| ------------------ | ------------------------------- | -------------------------- |\n| MultiTurnWorkflow  | `areal/workflow/multi_turn.py`  | Multi-turn conversation    |\n| RLVRWorkflow       | `areal/workflow/rlvr.py`        | RL with verifiable rewards |\n| VisionRLVRWorkflow | `areal/workflow/vision_rlvr.py` | Vision + RLVR              |\n\n## Key Requirements\n\n1. **Async**: `arun_episode` must be `async def` and non-blocking\n1. **No sync I/O**: Use `aiofiles` for file operations\n1. **Wrap rewards**: Use `AsyncRewardWrapper` for reward functions\n1. **Tensor format**: Output tensors should be `[batch, seq_len, ...]`\n1. **Use helpers**: `concat_padded_tensors` for combining outputs\n\n## Common Mistakes\n\n- ❌ Using `open()` instead of `aiofiles.open()`\n- ❌ Forgetting to `await` async calls\n- ❌ Not wrapping reward function with `AsyncRewardWrapper`\n- ❌ Wrong tensor shape conventions\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/skills/add-workflow/SKILL.md\nInvocation: /add-workflow <name>\n\n## Purpose\n\nStep-by-step guide for adding new RolloutWorkflow implementations.\n\n## How to Update\n\n### When Workflow API Changes\n1. Update the code template in Step 1\n2. Update the required imports\n3. Update the method signature if changed\n\n### When New Patterns Emerge\n1. Add to \"Reference Implementations\" table\n2. Update \"Key Requirements\" if new requirements added\n\n================================================================================\n-->\n",".claude/skills/commit-conventions/SKILL.md":"---\nname: commit-conventions\ndescription: AReaL commit message conventions. MUST load on every git commit -- provides Conventional Commits format with scope inference from file paths.\n---\n\n# Commit Conventions\n\nCommit message conventions and scope inference rules for the AReaL repository.\n\n## When to Use\n\n**ALWAYS load this skill when making any git commit in AReaL.** This includes:\n\n- Direct commits (`git commit`)\n- Commits during PR creation (`/create-pr`)\n- Commits delegated via Agent tool with `skills: [\"commit-conventions\"]`\n- Any agent workflow that produces a commit\n\n## Commit Message Format\n\n```\n<type>(<scope>): <subject>\n\n<body>\n\n[Optional sections:]\nKey changes:\n- change 1\n- change 2\n\nRefs: #123, #456\n```\n\n## Type Selection\n\n| Type       | When to Use                      |\n| ---------- | -------------------------------- |\n| `feat`     | New feature or capability        |\n| `fix`      | Bug fix                          |\n| `docs`     | Documentation only               |\n| `gov`      | Governance or maintainer changes |\n| `style`    | Formatting/style-only changes    |\n| `refactor` | Code change without feature/fix  |\n| `perf`     | Performance improvement          |\n| `test`     | Adding or fixing tests           |\n| `build`    | Build system or dependencies     |\n| `ci`       | CI pipeline or workflow changes  |\n| `chore`    | Build, deps, config changes      |\n| `revert`   | Revert a previous commit         |\n\n## Scope Inference\n\nInfer scope from the **primary** changed file paths:\n\n| File Path Pattern                                            | Scope                          |\n| ------------------------------------------------------------ | ------------------------------ |\n| `areal/workflow/`                                            | `workflow`                     |\n| `areal/engine/`                                              | `engine`                       |\n| `areal/reward/`                                              | `reward`                       |\n| `areal/dataset/`                                             | `dataset`                      |\n| `areal/api/`                                                 | `api`                          |\n| `areal/utils/`                                               | `utils`                        |\n| `areal/infra/`                                               | `infra`                        |\n| `areal/trainer/`                                             | `trainer`                      |\n| `areal/models/`                                              | `models`                       |\n| `areal/experimental/`                                        | `archon`                       |\n| `docs/`                                                      | `docs`                         |\n| `examples/`                                                  | `examples`                     |\n| `AGENTS.md`, `.agents/`, `.claude/`, `.codex/`, `.opencode/` | `agents`                       |\n| Multiple areas                                               | Omit scope or use broader term |\n\n## Rules\n\n- **Subject**: imperative mood, ~50-72 chars, no trailing period\n- **Body**: explain \"why\" not \"what\", wrap at 72 chars\n- **Key changes**: bullet list of main modifications (for complex commits with 3+ files)\n- **Refs**: reference issues/PRs if applicable\n\n## Examples\n\n**Single file fix:**\n\n```\nfix(reward): handle empty completion in gsm8k\n\nReturn 0 reward instead of raising exception when\ncompletion string is empty after extraction.\n```\n\n**Multi-file feature:**\n\n```\nfeat(engine): add CPU offload support to ArchonEngine\n\nEnable torch_memory_saver for model offloading during\nrollout phase to reduce GPU memory pressure.\n\nKey changes:\n- Add offload/onload methods to ArchonEngine\n- Integrate with weight update flow\n- Handle ROCm compatibility\n```\n\n**Docs only:**\n\n```\ndocs: update algorithm comparison table\n\nAdd SAPO and GSPO to the algorithm family documentation\nwith configuration examples.\n```\n\n**Agent/tooling changes:**\n\n```\nchore(agents): port review-pr command to OpenCode\n\nAdd OpenCode-native commands with task() category\ndelegation instead of hardcoded model names.\n\nKey changes:\n- Create .opencode/command/ with review-pr, create-pr\n- Replace hardcoded model routing with platform-native review routing\n- Add expert subagent consultation patterns\n```\n\n**Governance/maintainer changes:**\n\n```\ngov(agents): add maintainer ownership for service modules\n\nUpdate CODEOWNERS and maintainer references to reflect\ncurrent governance responsibilities.\n\nKey changes:\n- Add maintainers for agent_service and infra ownership\n- Align governance docs with updated reviewer responsibilities\n```\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/skills/commit-conventions/SKILL.md\nInvocation: Automatically loaded on every git commit via load_skills\n\n## Purpose\n\nProvides Conventional Commits format with AReaL-specific scope inference\nfrom file paths. Unlike other skills, this one is NOT user-triggered --\nit is loaded by the system/agent on every commit operation.\n\n## How to Update\n\n### When New Modules Are Added\n1. Add the file path pattern and scope to the \"Scope Inference\" table\n2. Keep table sorted by areal/ subpackages first, then top-level dirs\n\n### When Commit Types Change\n1. Update the \"Type Selection\" table\n2. Add/update examples to illustrate the new type\n\n### When Adding Examples\n1. Each example should demonstrate a distinct commit pattern\n2. Keep examples realistic -- use actual AReaL module names\n3. Show both subject-only and subject+body+key-changes variants\n\n### Important Design Decisions\n- This skill is ALWAYS loaded (not optional) -- keep it concise to\n  minimize token overhead on every commit\n- Scope inference is path-based, not content-based -- simpler and\n  more deterministic\n- \"Multiple areas\" -> omit scope rather than invent a new one\n\n================================================================================\n-->\n",".claude/skills/debug-distributed/SKILL.md":"---\nname: debug-distributed\ndescription: Guide for debugging distributed training issues in AReaL. Use when user encounters hangs, wrong results, OOM, or communication errors.\n---\n\n# Debug Distributed Training\n\nDebugging guide for distributed training issues in AReaL (FSDP2, TP, CP, EP).\n\n## When to Use\n\nThis skill is triggered when:\n\n- Training hangs or deadlocks\n- Results differ across ranks or are numerically wrong\n- OOM errors in distributed settings\n- NCCL/communication errors or device mesh issues\n\n## Debugging Principles\n\n### Minimal Reproduction\n\n**Always follow the minimal demo principle**: Reproduce with the least amount of code to\nnarrow down the issue faster.\n\n```python\n# Bad: Debug in full training loop\n# Good: Create minimal script\nimport torch\nimport torch.distributed as dist\n\ndist.init_process_group(\"nccl\")\nrank = dist.get_rank()\n\n# Reproduce the exact operation that fails\ntensor = torch.ones(10).cuda()\ndist.all_reduce(tensor)  # <-- Isolate the failing op\nprint(f\"Rank {rank}: {tensor}\")\n```\n\n**Reduction strategy:**\n\n1. Remove unrelated model components\n1. Use small tensor sizes\n1. Reduce world_size to minimum (e.g., 2 GPUs)\n1. Remove torch.compile if possible\n1. Disable activation checkpointing\n\n## Step-by-Step Debugging Guide\n\n### 1. Hang Debugging (Deadlocks, Synchronization)\n\n**Environment Variables for Debugging**:\n\n```bash\n# Full debug logging\nexport TORCH_DISTRIBUTED_DEBUG=DETAIL\nexport NCCL_DEBUG=INFO\nexport NCCL_DEBUG_SUBSYS=ALL\n\n# torch.compile debugging\nexport TORCH_LOGS=\"+dynamo,recompiles\"\nexport TORCHDYNAMO_VERBOSE=1\n```\n\n**Dump Call Stack with py-spy** (for hung processes):\n\n```bash\n# Find process IDs\nps aux | grep python\n\n# Dump call stack of specific rank\npy-spy dump --pid <PID>\n\n# Record flame graph for performance analysis\npy-spy record -o profile.svg --pid <PID> --duration 30\n```\n\n**Common Causes**:\n\n1. **Mismatched Collectives**: One rank calls `all_reduce`, another doesn't.\n1. **Wrong Process Group**: Using wrong group for collective.\n1. **Tensor Shape Mismatch**: Different shapes across ranks.\n\n**Debug Steps**:\n\n```python\n# Verify group membership\nmesh = parallel_dims.get_mesh(\"dp_shard_cp\")\ngroup = mesh.get_group()\nprint(f\"Rank {dist.get_rank()}: group size = {dist.get_world_size(group)}\")\n\n# Print shapes on all ranks\nprint(f\"Rank {dist.get_rank()}: tensor.shape = {tensor.shape}\")\ndist.barrier()\n```\n\n**Timeout Adjustment** (for debugging only):\n\n```python\nfrom areal.engine.core.distributed import patch_dist_group_timeout\nfrom datetime import timedelta\npatch_dist_group_timeout(timedelta(minutes=30))\n```\n\n### 2. Wrong Results (Gradient, Reduction Issues)\n\n**Check DTensor Placements**:\n\n```python\nfrom torch.distributed.tensor import DTensor\nif isinstance(param, DTensor):\n    print(f\"Param {name}: placements={param.placements}, mesh={param.device_mesh}\")\n```\n\n**Verify Gradient Reduction**:\n\n```python\nfor name, param in model.named_parameters():\n    if param.grad is not None:\n        print(f\"Rank {dist.get_rank()}: {name} grad_sum = {param.grad.sum().item()}\")\n```\n\n### 3. OOM Issues (Memory, Sharding)\n\n**Check Memory Usage**:\n\n```python\nprint(f\"Rank {dist.get_rank()}: \"\n      f\"allocated={torch.cuda.memory_allocated()/1e9:.2f}GB, \"\n      f\"reserved={torch.cuda.memory_reserved()/1e9:.2f}GB\")\n```\n\n**Check FSDP Coverage**:\n\n```python\nfor name, param in model.named_parameters():\n    is_dtensor = isinstance(param, DTensor)\n    print(f\"{name}: is_dtensor={is_dtensor}, shape={param.shape}\")\n```\n\n### 4. Communication Errors\n\n| Error                     | Cause                | Solution                           |\n| ------------------------- | -------------------- | ---------------------------------- |\n| `NCCL WARN Cuda failure`  | GPU communication    | Check NCCL version, GPU topology   |\n| `RuntimeError: Timed out` | Rank synchronization | Increase timeout, check code paths |\n| `Invalid device mesh`     | Mesh configuration   | Verify world_size = dp * tp * cp   |\n\n## Debugging Tools\n\n### Environment Variables Reference\n\n| Variable                          | Purpose                                |\n| --------------------------------- | -------------------------------------- |\n| `TORCH_DISTRIBUTED_DEBUG=DETAIL`  | Detailed distributed logging           |\n| `NCCL_DEBUG=INFO`                 | NCCL communication logging             |\n| `NCCL_DEBUG_SUBSYS=ALL`           | All NCCL subsystems                    |\n| `TORCH_LOGS=\"+dynamo,recompiles\"` | torch.compile logging                  |\n| `TORCHDYNAMO_VERBOSE=1`           | Dynamo verbose output                  |\n| `CUDA_LAUNCH_BLOCKING=1`          | Synchronous CUDA (slow, for debugging) |\n\n### py-spy for Call Stack Analysis\n\n```bash\n# Install\npip install py-spy\n\n# Dump call stack of hung process\npy-spy dump --pid <PID>\n\n# Dump all Python processes\npgrep -f python | xargs -I {} py-spy dump --pid {}\n\n# Record flame graph\npy-spy record -o profile.svg --pid <PID> --duration 30\n```\n\n### Rank-Conditional Printing\n\n```python\ndef print_all_ranks(msg):\n    for r in range(dist.get_world_size()):\n        if dist.get_rank() == r:\n            print(f\"[Rank {r}] {msg}\")\n        dist.barrier()\n```\n\n### Check Device Mesh\n\n```python\ndef debug_mesh(parallel_dims):\n    mesh = parallel_dims.world_mesh\n    for dim_name in mesh.mesh_dim_names:\n        submesh = parallel_dims.get_mesh(dim_name)\n        if submesh:\n            print(f\"Rank {dist.get_rank()}: {dim_name} size={submesh.size()}\")\n```\n\n### Validate Tensor Consistency\n\n```python\ndef check_tensor_consistency(tensor, name, group=None):\n    local_sum = tensor.sum().item()\n    tensor_sums = [None] * dist.get_world_size(group)\n    dist.all_gather_object(tensor_sums, local_sum, group=group)\n    if dist.get_rank() == 0 and len(set(tensor_sums)) > 1:\n        print(f\"WARNING: {name} inconsistent: {tensor_sums}\")\n```\n\n## Key Files Reference\n\n| Component       | File                                                          |\n| --------------- | ------------------------------------------------------------- |\n| Parallel Dims   | `areal/experimental/models/archon/parallel_dims.py`           |\n| Expert Parallel | `areal/experimental/models/archon/expert_parallel.py`         |\n| Ulysses (CP)    | `areal/experimental/models/archon/ulysses.py`                 |\n| FSDP/TP Apply   | `areal/experimental/models/archon/qwen2/infra/parallelize.py` |\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/skills/debug-distributed/SKILL.md\nInvocation: /debug-distributed\n\n## Purpose\n\nDebugging guide for distributed training issues.\nCovers FSDP2, Tensor Parallelism, Context Parallelism, and Expert Parallelism.\n\n## How to Update\n\n### When Adding New Parallelism Features\n1. Add section for the parallelism type\n2. Document common error patterns and debugging snippets\n\n### When PyTorch Distributed APIs Change\n1. Update DTensor/DeviceMesh examples\n2. Update environment variable references\n\n### When New Error Patterns Emerge\n1. Add to \"Common Errors and Solutions\" table\n2. Reference relevant source files\n\n================================================================================\n-->\n",".opencode/skills/add-archon-model/SKILL.md":"---\nname: add-archon-model\ndescription: Guide for adding a new model to the Archon engine. Use when user wants to add support for a new HuggingFace model architecture in ArchonEngine.\n---\n\n# Add Archon Model\n\nAdd support for a new HuggingFace model architecture in the Archon training engine.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a model to Archon?\"\n- User wants to support a new model family (e.g., Llama, Mistral, DeepSeek) in\n  ArchonEngine\n- User mentions adding a new `ModelSpec` or model type for Archon\n\n## Prerequisites\n\nBefore starting, ensure:\n\n- The target model is available on HuggingFace (has `config.json` with `model_type`)\n- You know the HuggingFace model ID (e.g., `meta-llama/Llama-3-8B`)\n- The model uses a standard transformer architecture (decoder-only)\n\n## Step-by-Step Guide\n\n### Step 1: Analyze the Target Model Architecture\n\nRead the HuggingFace model's source code to extract key architecture information.\n\n**Action**: Fetch and analyze the model's HuggingFace configuration and modeling files.\n\n1. Read the model's `config.json` (via `AutoConfig.from_pretrained`) to identify:\n\n   - `model_type` string (this is the key used for registry lookup)\n   - All architecture hyperparameters (hidden_size, num_layers, etc.)\n   - Any model-specific fields (e.g., `qk_norm`, `attention_bias`, MoE fields)\n\n1. Read the HuggingFace `modeling_*.py` source to identify:\n\n   - **Attention variant**: Does it have Q/K norm? Attention bias? Sliding window?\n     Multi-latent attention?\n   - **FFN variant**: SwiGLU (gate_proj + up_proj + down_proj)? GeGLU? Standard MLP?\n   - **MoE support**: Does it have MoE layers? What router type? Shared experts?\n   - **RoPE variant**: Standard RoPE? YaRN? NTK-aware scaling? What is the inv_freq\n     formula?\n   - **Normalization**: RMSNorm or LayerNorm? Pre-norm or post-norm? Elementwise affine?\n   - **Weight tying**: Does `tie_word_embeddings` appear in config?\n   - **State dict key names**: What are the HF weight key naming conventions?\n\n1. Summarize findings in a checklist like:\n\n```\nTarget model: <name>\nHF model_type: \"<model_type>\" (and variants like \"<model_type>_moe\" if applicable)\nAttention: [standard GQA / with QK norm / with bias / sliding window / ...]\nFFN: [SwiGLU / GeGLU / standard MLP / ...]\nMoE: [no / yes - num_experts, top_k, shared_experts]\nRoPE: [standard / YaRN / NTK-aware / ...]\nNorm: [RMSNorm / LayerNorm] with [pre-norm / post-norm]\nWeight tying: [yes / no]\n```\n\n### Step 2: Select the Reference Model\n\nChoose the closest existing implementation as a starting point:\n\n| Target characteristics               | Reference | Why                                     |\n| ------------------------------------ | --------- | --------------------------------------- |\n| Dense-only, standard GQA, no QK norm | `qwen2`   | Simplest baseline, pure dense           |\n| Has QK norm, or has MoE support      | `qwen3`   | Supports QK norm + MoE + shared experts |\n\n**Action**: Copy the reference model directory as the starting point:\n\n```\nareal/experimental/models/archon/<model>/\n  __init__.py\n  spec.py\n  model/\n    args.py\n    model.py\n    rope.py\n    state_dict_adapter.py\n  infra/\n    parallelize.py\n```\n\n### Step 3: Implement `args.py`\n\nAdapt `<Model>ModelArgs` to match the target model's HuggingFace config fields.\n\n**Key changes from reference**:\n\n1. Update the `@dataclass` fields to match the target model's hyperparameters:\n\n   - Field names should use Archon conventions (`dim`, `n_layers`, `n_heads`,\n     `n_kv_heads`, `vocab_size`, `head_dim`, `hidden_dim`, `norm_eps`, `rope_theta`,\n     etc.)\n   - Default values should match the smallest variant of the target model\n   - Add model-specific fields (e.g., `attention_bias`, `qk_norm`, `sliding_window`)\n\n1. Update `from_hf_config()` to correctly map HuggingFace config attributes:\n\n   - Use `getattr(hf_config, \"field_name\", default)` for optional fields\n   - Handle variant-specific fields (e.g., MoE fields only present in MoE variants)\n   - The method must return an instance of the model args class\n\n**Critical**: Verify every field mapping against the HF model's `config.json`. Incorrect\nmappings here cause silent errors downstream.\n\n**Base class contract** (`BaseModelArgs`):\n\n```python\n@dataclass\nclass <Model>ModelArgs(BaseModelArgs):\n    # ... model-specific fields ...\n\n    @classmethod\n    def from_hf_config(\n        cls,\n        hf_config: PretrainedConfig,\n        is_critic: bool = False,\n        **kwargs,\n    ) -> <Model>ModelArgs:\n        # Map HF config fields to Archon model args\n        ...\n```\n\n### Step 4: Implement `model.py`\n\nAdapt the model architecture to match the target model.\n\n**Key components to adapt**:\n\n1. **Normalization** (`RMSNorm` or similar):\n\n   - Check if `elementwise_affine` is configurable\n   - Check the epsilon default value\n   - If the model uses `LayerNorm`, implement accordingly\n\n1. **Attention** module:\n\n   - Q/K/V projection: Check bias presence (`nn.Linear(..., bias=True/False)`)\n   - QK norm: Add `q_norm`/`k_norm` if the model has them, remove if it doesn't\n   - GQA: `n_kv_heads` \\< `n_heads` for grouped-query attention\n   - Ulysses SP: Keep the `set_cp_group` / `_sp_enabled` pattern from the reference\n   - Output projection: Check bias presence\n\n1. **FeedForward** module:\n\n   - SwiGLU: `w2(silu(w1(x)) * w3(x))` -- most common for modern LLMs\n   - Check bias in linear layers\n   - For MoE models: `MoE` module replaces `FeedForward` on designated layers\n\n1. **TransformerBlock**: Pre-norm (most modern LLMs) vs post-norm\n\n   - MoE layer detection via `_is_moe_layer()` if applicable\n\n1. **Top-level Model** (`<Model>Model(BaseArchonModel)`):\n\n   - `tok_embeddings`, `layers` (as `ModuleDict`), `norm`, `output`/`score`\n   - `init_weights()`: Match initialization scheme from HF\n   - `init_buffers()`: RoPE cache + MoE buffers\n   - `forward()`: Must follow `BaseArchonModel` signature:\n     `(tokens, positions, cu_seqlens, max_seqlen, tree_attn_meta=None) -> Tensor`\n\n**Base class contract** (`BaseArchonModel`):\n\n```python\nclass <Model>Model(BaseArchonModel):\n    def forward(self, tokens, positions, cu_seqlens, max_seqlen, tree_attn_meta=None) -> torch.Tensor: ...\n    def init_weights(self) -> None: ...\n    def init_buffers(self, buffer_device) -> None: ...\n```\n\n### Step 5: Implement `rope.py`\n\nHandle the rotary position embedding variant.\n\n**Options**:\n\n1. **Standard RoPE** (same as qwen2/qwen3): Re-export from qwen2:\n\n   ```python\n   from areal.experimental.models.archon.qwen2.model.rope import (\n       apply_rotary_emb,\n       precompute_rope_cache,\n       repeat_kv,\n       reshape_for_broadcast,\n       rotate_half,\n   )\n   ```\n\n1. **Custom RoPE** (YaRN, NTK-aware, etc.): Implement custom `precompute_rope_cache()`\n   and `apply_rotary_emb()` functions. The key difference is usually in how `inv_freq`\n   is computed (scaling factors, interpolation, etc.).\n\n### Step 6: Implement `state_dict_adapter.py`\n\nMap between HuggingFace and Archon weight key names.\n\n**This is the most error-prone step.** The adapter must correctly handle:\n\n1. **Key name mapping** (`from_hf_map` dict):\n\n   - Embedding: `model.embed_tokens.weight` -> `tok_embeddings.weight`\n   - Attention: `model.layers.{}.self_attn.q_proj.weight` ->\n     `layers.{}.attention.wq.weight`\n   - FFN: `model.layers.{}.mlp.gate_proj.weight` -> `layers.{}.feed_forward.w1.weight`\n   - Norms: `model.layers.{}.input_layernorm.weight` ->\n     `layers.{}.attention_norm.weight`\n   - Output: `lm_head.weight` -> `output.weight`\n   - Skip keys (set to `None`): `rotary_emb.inv_freq` (computed at runtime)\n   - Model-specific keys: bias terms, QK norm weights, etc.\n\n1. **Reverse mapping** (`to_hf_map`): Auto-generated from `from_hf_map`\n\n1. **MoE expert weights** (if applicable): 3D\\<->2D conversion for expert weights. Copy\n   the MoE handling from qwen3 if the model has MoE.\n\n1. **Weight tying**: Skip `output.weight` during `to_hf()` if `tie_word_embeddings=True`\n\n**Verification approach**: After implementation, the adapter should satisfy:\n\n```python\n# Roundtrip: archon -> hf -> archon preserves all keys\nhf_sd = adapter.to_hf(archon_sd)\nroundtrip_sd = adapter.from_hf(hf_sd)\nassert set(roundtrip_sd.keys()) == set(archon_sd.keys())\n```\n\n**Base class contract** (`BaseStateDictAdapter`):\n\n```python\nclass <Model>StateDictAdapter(BaseStateDictAdapter):\n    def from_hf(self, hf_state_dict) -> dict[str, Any]: ...\n    def to_hf(self, archon_state_dict) -> dict[str, Any]: ...\n    def convert_single_to_hf(self, name, tensor) -> list[tuple[str, torch.Tensor]]: ...\n```\n\n### Step 7: Implement `parallelize.py`\n\nDefine the parallelization strategy for the model.\n\n**The parallelize function** applies parallelism in this order:\n\n1. TP (Tensor Parallelism) -- shard attention/FFN across devices\n1. EP (Expert Parallelism) -- for MoE models only\n1. CP (Context Parallelism / Ulysses SP) -- sequence parallelism\n1. AC (Activation Checkpointing) -- memory optimization\n1. torch.compile -- compilation optimization\n1. FSDP (Fully Sharded Data Parallelism) -- data parallelism\n\n**Key adaptations by model architecture**:\n\n- **Attention with QK norm**: wq/wk use `use_local_output=False` (DTensor output for\n  norm), add `SequenceParallel(sequence_dim=2)` for q_norm/k_norm\n- **Attention without QK norm**: wq/wk/wv all use `use_local_output=True`\n- **Attention with bias**: Bias terms follow the same parallel plan as their weights\n- **MoE layers**: Separate TP plan for MoE input/output, router gate, and expert\n  weights. Copy from qwen3's `apply_moe_ep_tp()` and `apply_non_moe_tp()`\n- **Dense-only models**: Simpler plan without MoE handling. Copy from qwen2\n\n**Function signature** (must match `ParallelizeFn` protocol):\n\n```python\ndef parallelize_<model>(\n    model: nn.Module,\n    parallel_dims: ArchonParallelDims,\n    param_dtype: torch.dtype = torch.bfloat16,\n    reduce_dtype: torch.dtype = torch.float32,\n    loss_parallel: bool = True,\n    cpu_offload: bool = False,\n    reshard_after_forward_policy: str = \"default\",\n    ac_config: ActivationCheckpointConfig | None = None,\n    enable_compile: bool = True,\n) -> nn.Module:\n```\n\n### Step 8: Create `spec.py` and Register\n\nAssemble the `ModelSpec` and register it.\n\n```python\nfrom areal.experimental.models.archon.model_spec import ModelSpec, register_model_spec\nfrom areal.experimental.models.archon.pipeline_parallel import pipeline_llm\nfrom areal.experimental.models.archon.<model>.infra.parallelize import parallelize_<model>\nfrom areal.experimental.models.archon.<model>.model.args import <Model>ModelArgs\nfrom areal.experimental.models.archon.<model>.model.model import <Model>Model\nfrom areal.experimental.models.archon.<model>.model.state_dict_adapter import (\n    <Model>StateDictAdapter,\n)\n\n<MODEL>_SPEC = ModelSpec(\n    name=\"<Model>\",\n    model_class=<Model>Model,\n    model_args_class=<Model>ModelArgs,\n    state_dict_adapter_class=<Model>StateDictAdapter,\n    parallelize_fn=parallelize_<model>,\n    supported_model_types=frozenset({\"<model_type>\"}),  # From HF config.json\n    pipelining_fn=pipeline_llm,\n)\n\n# Auto-register when module is imported\nregister_model_spec(<MODEL>_SPEC)\n\n__all__ = [\"<MODEL>_SPEC\"]\n```\n\n**Note**: `supported_model_types` should include all HF `model_type` strings that this\nimplementation handles (e.g., `{\"qwen3\", \"qwen3_moe\"}` for Qwen3).\n\n### Step 9: Register in `__init__.py`\n\nAdd the import to `areal/experimental/models/archon/__init__.py`:\n\n```python\nfrom areal.experimental.models.archon.<model> import spec as <model>_spec  # noqa: F401\n```\n\nThis triggers auto-registration when the module is imported.\n\n### Step 10: Verify and Test\n\nVerification should be done in stages, adapting based on available hardware and the test\npatterns in `tests/experimental/archon/`.\n\n**Before writing tests**, examine the existing test files to understand current\npatterns:\n\n```\ntests/experimental/archon/\n  conftest.py             -- Pytest configuration (version checks)\n  utils.py                -- Shared utilities (model loading, comparison)\n  test_qwen3_args.py      -- Args unit tests (CPU-only)\n  test_state_dict_adapter.py  -- State dict roundtrip tests\n  test_weight_sync.py     -- Weight completeness tests (meta device)\n  test_forward.py         -- Forward precision comparison (single GPU)\n  ...\n```\n\n**Test stages** (write tests appropriate for the model's complexity):\n\n#### Stage 1: Args Tests (CPU-only, always write these)\n\nTest `from_hf_config()` with mock HuggingFace configs:\n\n```python\n# Pattern: Create mock PretrainedConfig, verify args mapping\nfrom unittest.mock import MagicMock\n\ndef test_args_from_hf_config():\n    hf_config = MagicMock()\n    hf_config.hidden_size = 4096\n    hf_config.num_hidden_layers = 32\n    # ... set all required fields\n    args = <Model>ModelArgs.from_hf_config(hf_config)\n    assert args.dim == 4096\n    assert args.n_layers == 32\n```\n\n#### Stage 2: State Dict Adapter Tests (CPU-only)\n\nTest key mapping roundtrip:\n\n```python\ndef test_state_dict_roundtrip():\n    # Create adapter with mock config\n    adapter = <Model>StateDictAdapter(mock_config)\n    # Create fake archon state dict with expected keys\n    archon_sd = {\"tok_embeddings.weight\": torch.randn(vocab, dim), ...}\n    # Roundtrip\n    hf_sd = adapter.to_hf(archon_sd)\n    roundtrip = adapter.from_hf(hf_sd)\n    assert set(roundtrip.keys()) == set(archon_sd.keys())\n```\n\n#### Stage 3: Weight Completeness (meta device, CPU-only)\n\nVerify all model parameters have HF mappings:\n\n```python\ndef test_weight_completeness():\n    # Create model on meta device\n    with torch.device(\"meta\"):\n        model = <Model>Model(args)\n    adapter = <Model>StateDictAdapter(hf_config)\n    # Check every archon param has a HF mapping\n    for name, _ in model.named_parameters():\n        hf_pairs = adapter.convert_single_to_hf(name, torch.empty(0))\n        assert len(hf_pairs) > 0, f\"No HF mapping for {name}\"\n```\n\n#### Stage 4: Forward Precision (single GPU, if available)\n\nCompare Archon model output against HuggingFace reference:\n\n```python\n@pytest.mark.skipif(not torch.cuda.is_available(), reason=\"Requires CUDA\")\ndef test_forward_matches_hf():\n    # Load both HF and Archon models\n    # Run forward on same input\n    # Compare logits within tolerance\n```\n\n**Important**: Do NOT hardcode the test categories. Inspect the existing test files in\n`tests/experimental/archon/` and follow the same patterns, fixtures, and markers. Adapt\ntest scope to the model's specific features (e.g., add MoE-specific tests only if the\nmodel has MoE).\n\n## Reference Implementations\n\n| Model | Directory                                 | Features                                                |\n| ----- | ----------------------------------------- | ------------------------------------------------------- |\n| Qwen2 | `areal/experimental/models/archon/qwen2/` | Dense, attention bias, no QK norm                       |\n| Qwen3 | `areal/experimental/models/archon/qwen3/` | Dense + MoE, QK norm, no attention bias, shared experts |\n\n## Architecture Decision Map\n\n| Feature             | qwen2    | qwen3                      | What to check in target model                            |\n| ------------------- | -------- | -------------------------- | -------------------------------------------------------- |\n| Attention bias      | Yes      | No                         | `attention_bias` in HF config                            |\n| QK norm             | No       | Yes                        | `qk_norm` in HF config or QKNorm module in modeling file |\n| MoE                 | No       | Yes                        | `num_experts`/`num_local_experts` in HF config           |\n| Shared experts      | No       | Yes                        | `num_shared_experts` in HF config                        |\n| Decoder sparse step | No       | Yes                        | `decoder_sparse_step` in HF config                       |\n| Weight tying        | Both     | Both                       | `tie_word_embeddings` in HF config                       |\n| RoPE                | Standard | Standard (re-export qwen2) | Check inv_freq formula in HF modeling code               |\n\n## Common Mistakes\n\n- Not mapping all HF keys in `state_dict_adapter.py` (causes silent weight drops)\n- Wrong `from_hf_config()` field mapping (uses wrong HF config attribute name)\n- Forgetting to handle `None` keys in `from_hf_map` (keys to skip like\n  `rotary_emb.inv_freq`)\n- Missing MoE expert weight 3D\\<->2D conversion when model has MoE\n- Wrong TP plan for attention with/without QK norm (`use_local_output` must match)\n- Forgetting to add import line in `areal/experimental/models/archon/__init__.py`\n- Not including all `model_type` variants in `supported_model_types` frozenset\n- Using `print` instead of `areal.utils.logging.getLogger()`\n\n## File Checklist\n\nAfter completion, verify all files exist and are consistent:\n\n- [ ] `areal/experimental/models/archon/<model>/__init__.py`\n- [ ] `areal/experimental/models/archon/<model>/spec.py` -- ModelSpec + register\n- [ ] `areal/experimental/models/archon/<model>/model/args.py` -- ModelArgs +\n  from_hf_config\n- [ ] `areal/experimental/models/archon/<model>/model/model.py` -- Model + Attention +\n  FFN\n- [ ] `areal/experimental/models/archon/<model>/model/rope.py` -- RoPE (or re-export)\n- [ ] `areal/experimental/models/archon/<model>/model/state_dict_adapter.py` -- Key\n  mapping\n- [ ] `areal/experimental/models/archon/<model>/infra/parallelize.py` -- Parallel\n  strategy\n- [ ] `areal/experimental/models/archon/__init__.py` -- Import line added\n- [ ] `tests/experimental/archon/test_<model>_*.py` -- Tests\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .opencode/skills/add-archon-model/SKILL.md\nInvocation: /add-archon-model <model_name>\n\n## Purpose\n\nSemi-automated guide for adding new model architectures to the Archon training engine.\nUnlike simpler skills (add-reward, add-dataset), this skill actively guides Claude to:\n1. Analyze HuggingFace source code to extract architecture details\n2. Select the closest reference implementation (qwen2 or qwen3)\n3. Generate code skeletons adapted to the target architecture\n4. Create appropriate tests based on existing test patterns\n\n## How to Update\n\n### When New Reference Models Are Added\n1. Add to \"Reference Implementations\" table\n2. Update \"Architecture Decision Map\" with new feature columns\n3. Update Step 2 (reference selection) with new options\n\n### When Base Classes Change\n1. Update contract signatures in Steps 3, 4, 6, 7\n2. Update file checklist if new files are required\n\n### When ModelSpec Changes\n1. Update Step 8 with new ModelSpec fields\n2. Update spec.py template\n\n### When Test Patterns Change\n1. Update Step 10 with new test patterns\n2. Do NOT hardcode test categories -- keep it flexible\n\n### Important Design Decisions\n- This skill is SEMI-AUTOMATED: Claude should read HF source and generate code,\n  not just provide templates for the user to fill in manually\n- The skill references existing test files rather than hardcoding test categories,\n  ensuring it stays current as the test suite evolves\n- Reference model selection (qwen2 vs qwen3) is based on MoE and QK norm presence\n\n================================================================================\n-->\n",".opencode/skills/add-dataset/SKILL.md":"---\nname: add-dataset\ndescription: Guide for adding a new dataset loader to AReaL. Use when user wants to add a new dataset.\n---\n\n# Add Dataset\n\nAdd a new dataset loader to AReaL.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a dataset?\"\n- User wants to integrate a new dataset\n- User mentions creating a dataset loader\n\n## Step-by-Step Guide\n\n### Step 1: Create Dataset File\n\nCreate `areal/dataset/<name>.py`:\n\n```python\nfrom datasets import Dataset, load_dataset\n\n\ndef get_<name>_sft_dataset(\n    path: str,\n    split: str,\n    tokenizer,\n    max_length: int | None = None,\n) -> Dataset:\n    \"\"\"Load dataset for SFT training.\n\n    Args:\n        path: Path to dataset (HuggingFace hub or local path)\n        split: Dataset split (train/validation/test)\n        tokenizer: Tokenizer for processing\n        max_length: Maximum sequence length (optional)\n\n    Returns:\n        HuggingFace Dataset with processed samples\n    \"\"\"\n    dataset = load_dataset(path=path, split=split)\n\n    def process(sample):\n        # Tokenize the full sequence (prompt + response)\n        seq_token = tokenizer.encode(\n            sample[\"question\"] + sample[\"answer\"] + tokenizer.eos_token\n        )\n        prompt_token = tokenizer.encode(sample[\"question\"])\n        # Loss mask: 0 for prompt, 1 for response\n        loss_mask = [0] * len(prompt_token) + [1] * (len(seq_token) - len(prompt_token))\n        return {\"input_ids\": seq_token, \"loss_mask\": loss_mask}\n\n    dataset = dataset.map(process).remove_columns([\"question\", \"answer\"])\n\n    if max_length is not None:\n        dataset = dataset.filter(lambda x: len(x[\"input_ids\"]) <= max_length)\n\n    return dataset\n\n\ndef get_<name>_rl_dataset(\n    path: str,\n    split: str,\n    tokenizer,\n    max_length: int | None = None,\n) -> Dataset:\n    \"\"\"Load dataset for RL training.\n\n    Args:\n        path: Path to dataset\n        split: Dataset split\n        tokenizer: Tokenizer for length filtering\n        max_length: Maximum sequence length\n\n    Returns:\n        HuggingFace Dataset with prompts and answers for reward computation\n    \"\"\"\n    dataset = load_dataset(path=path, split=split)\n\n    def process(sample):\n        messages = [\n            {\n                \"role\": \"user\",\n                \"content\": sample[\"question\"],\n            }\n        ]\n        return {\"messages\": messages, \"answer\": sample[\"answer\"]}\n\n    dataset = dataset.map(process).remove_columns([\"question\"])\n\n    if max_length is not None:\n\n        def filter_length(sample):\n            content = sample[\"messages\"][0][\"content\"]\n            tokens = tokenizer.encode(content)\n            return len(tokens) <= max_length\n\n        dataset = dataset.filter(filter_length)\n\n    return dataset\n```\n\n### Step 2: Register in __init__.py\n\nUpdate `areal/dataset/__init__.py`:\n\n```python\n# Add to VALID_DATASETS\nVALID_DATASETS = [\n    # ... existing datasets\n    \"<name>\",\n]\n\n# Add to _get_custom_dataset function\ndef _get_custom_dataset(name: str, ...):\n    # ... existing code\n    elif name == \"<name>\":\n        from areal.dataset.<name> import get_<name>_sft_dataset, get_<name>_rl_dataset\n        if dataset_type == \"sft\":\n            return get_<name>_sft_dataset(path, split, max_length, tokenizer)\n        else:\n            return get_<name>_rl_dataset(path, split, max_length, tokenizer)\n```\n\n### Step 3: Add Config (Optional)\n\nIf the dataset needs special configuration, add to `areal/api/cli_args.py`:\n\n```python\n@dataclass\nclass TrainDatasetConfig:\n    # ... existing fields\n    <name>_specific_field: Optional[str] = None\n```\n\n### Step 4: Add Tests\n\nCreate `tests/test_<name>_dataset.py`:\n\n```python\nimport pytest\nfrom areal.dataset.<name> import get_<name>_sft_dataset, get_<name>_rl_dataset\n\ndef test_sft_dataset_loads(tokenizer):\n    dataset = get_<name>_sft_dataset(\"path/to/data\", split=\"train\", tokenizer=tokenizer)\n    assert len(dataset) > 0\n    assert \"input_ids\" in dataset.column_names\n    assert \"loss_mask\" in dataset.column_names\n\ndef test_rl_dataset_loads(tokenizer):\n    dataset = get_<name>_rl_dataset(\"path/to/data\", split=\"train\", tokenizer=tokenizer)\n    assert len(dataset) > 0\n    assert \"messages\" in dataset.column_names\n    assert \"answer\" in dataset.column_names\n```\n\n## Reference Implementations\n\n| Dataset    | File                               | Description              |\n| ---------- | ---------------------------------- | ------------------------ |\n| GSM8K      | `areal/dataset/gsm8k.py`           | Math word problems       |\n| Geometry3K | `areal/dataset/geometry3k.py`      | Geometry problems        |\n| CLEVR      | `areal/dataset/clevr_count_70k.py` | Visual counting          |\n| HH-RLHF    | `areal/dataset/hhrlhf.py`          | Helpfulness/Harmlessness |\n| TORL       | `areal/dataset/torl_data.py`       | Tool-use RL              |\n\n## Required Fields\n\n### SFT Dataset\n\n```python\n{\n    \"messages\": [\n        {\"role\": \"user\", \"content\": \"...\"},\n        {\"role\": \"assistant\", \"content\": \"...\"},\n    ]\n}\n```\n\n### RL Dataset\n\n```python\n{\n    \"messages\": [\n        {\"role\": \"user\", \"content\": \"...\"},\n    ],\n    \"answer\": \"ground_truth_for_reward\",\n    # Optional metadata for reward function\n}\n```\n\n## Common Mistakes\n\n- Returning `List[Dict]` instead of HuggingFace `Dataset`\n- Using Python loops instead of `dataset.map()`/`filter()`\n- Missing `\"messages\"` field for RL datasets\n- Wrong message format (should be list of dicts with `role` and `content`)\n- Not registering in `__init__.py`\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .opencode/skills/add-dataset/SKILL.md\nInvocation: /add-dataset <name>\n\n## Purpose\n\nStep-by-step guide for adding new dataset loaders.\n\n## How to Update\n\n### When Dataset API Changes\n1. Update the code templates\n2. Update required fields section\n3. Update registration example\n\n### When New Dataset Types Added\n1. Add to \"Reference Implementations\" table\n2. Add any new required fields\n\n================================================================================\n-->\n",".opencode/skills/add-reward/SKILL.md":"---\nname: add-reward\ndescription: Guide for adding a new reward function to AReaL. Use when user wants to create a reward function.\n---\n\n# Add Reward\n\nAdd a new reward function to AReaL.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a reward function?\"\n- User wants to implement custom rewards\n- User mentions reward computation\n\n## Step-by-Step Guide\n\n### Step 1: Create Reward File\n\nCreate `areal/reward/<name>.py`:\n\n```python\nfrom typing import Any\n\nfrom areal.utils import logging\n\nlogger = logging.getLogger(\"MyReward\")\n\n\ndef <name>_reward_fn(\n    prompt: str,\n    completions: str,\n    prompt_ids,\n    completion_ids,\n    answer: str | None = None,\n    **kwargs: Any,\n) -> float:\n    \"\"\"Compute reward for a single completion.\n\n    Args:\n        prompt: Prompt string\n        completions: Completion string (model output)\n        prompt_ids: Tokenized prompt IDs\n        completion_ids: Tokenized completion IDs\n        answer: Ground truth answer from dataset (optional)\n        **kwargs: Additional data from dataset\n\n    Returns:\n        Reward value (float), typically 0.0 or 1.0\n    \"\"\"\n    try:\n        # Extract answer from completion\n        extracted = _extract_answer(completions)\n\n        # Compare with ground truth\n        if answer is not None and extracted == str(answer):\n            return 1.0\n        return 0.0\n    except Exception:\n        logger.warning(\"Exception in reward computation\", exc_info=True)\n        return 0.0\n\n\ndef _extract_answer(completion: str) -> str:\n    \"\"\"Extract the answer from a completion string.\n\n    Implement your extraction logic here.\n    \"\"\"\n    # Example: Extract content from \\boxed{}\n    import re\n\n    match = re.search(r\"\\\\boxed\\{([^}]+)\\}\", completion)\n    if match:\n        return match.group(1).strip()\n    return completion.strip()\n```\n\n### Step 2: Register in __init__.py\n\nUpdate `areal/reward/__init__.py`:\n\n```python\n# Add to VALID_REWARD_FN\nVALID_REWARD_FN = [\n    # ... existing reward functions\n    \"<name>\",\n]\n\n# Add to get_reward_fn function\ndef get_reward_fn(name: str, **kwargs):\n    # ... existing code\n    elif name == \"<name>\":\n        from areal.reward.<name> import <name>_reward_fn\n        return <name>_reward_fn\n```\n\n### Step 3: Handle Blocking Operations\n\nIf your reward function uses blocking operations (e.g., API calls, model inference), the\nworkflow will wrap it with `AsyncRewardWrapper`:\n\n```python\n# In your workflow\nfrom areal.reward import AsyncRewardWrapper\n\nself.reward_fn = AsyncRewardWrapper(reward_fn)\n\n# Then call it asynchronously\nrewards = await self.reward_fn(prompt, completions, **data)\n```\n\n### Step 4: Add Tests\n\nCreate `tests/test_<name>_reward.py`:\n\n```python\nimport pytest\nfrom areal.reward.<name> import <name>_reward_fn\n\ndef test_reward_correct_answer():\n    reward = <name>_reward_fn(\n        prompt=\"What is 2+2?\",\n        completions=\"The answer is \\\\boxed{4}\",\n        prompt_ids=None,\n        completion_ids=None,\n        answer=\"4\",\n    )\n    assert reward == 1.0\n\ndef test_reward_wrong_answer():\n    reward = <name>_reward_fn(\n        prompt=\"What is 2+2?\",\n        completions=\"The answer is \\\\boxed{5}\",\n        prompt_ids=None,\n        completion_ids=None,\n        answer=\"4\",\n    )\n    assert reward == 0.0\n```\n\n## Reference Implementations\n\n| Reward     | File                              | Description                  |\n| ---------- | --------------------------------- | ---------------------------- |\n| GSM8K      | `areal/reward/gsm8k.py`           | Math answer verification     |\n| Geometry3K | `areal/reward/geometry3k.py`      | Geometry answer verification |\n| CLEVR      | `areal/reward/clevr_count_70k.py` | Counting verification        |\n| MathVerify | `areal/reward/math_verify.py`     | General math verification    |\n\n## Function Signature\n\nAll reward functions must follow this signature:\n\n```python\ndef reward_fn(\n    prompt: str,               # Input prompt string\n    completions: str,          # Model completion string\n    prompt_ids,                # Tokenized prompt\n    completion_ids,            # Tokenized completion\n    **kwargs: Any,             # Additional data from dataset (e.g., answer)\n) -> float:                    # Reward value (typically 0.0 or 1.0)\n```\n\n**Note**: The reward function is called once per sample. Batching is handled by\n`AsyncRewardWrapper` in the workflow.\n\n## Key Requirements\n\n1. **Deterministic**: Same inputs should produce same outputs\n1. **Return float**: Output is a single float value per sample\n1. **No blocking in async context**: Use `AsyncRewardWrapper` if needed\n1. **Logging**: Use `areal.utils.logging`, not `print`\n1. **Handle exceptions**: Return 0.0 on error, don't raise\n\n## Common Mistakes\n\n- Returning a tensor instead of a float\n- Expecting batched inputs (reward is called per sample)\n- Non-deterministic behavior\n- Blocking operations without `AsyncRewardWrapper`\n- Raising exceptions instead of returning 0.0\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .opencode/skills/add-reward/SKILL.md\nInvocation: /add-reward <name>\n\n## Purpose\n\nStep-by-step guide for adding new reward functions.\n\n## How to Update\n\n### When Reward API Changes\n1. Update the function signature section\n2. Update the code template\n3. Update key requirements\n\n### When New Reward Patterns Emerge\n1. Add to \"Reference Implementations\" table\n2. Add examples for new patterns\n\n================================================================================\n-->\n",".opencode/skills/add-unit-tests/SKILL.md":"---\nname: add-unit-tests\ndescription: Guide for adding unit tests to AReaL. Use when user wants to add tests for new functionality or increase test coverage.\n---\n\n# Add Unit Tests\n\nAdd unit tests to AReaL following the project's testing conventions.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add tests?\"\n- User wants to increase test coverage\n- User needs to write tests for new functionality\n- User wants to understand AReaL testing patterns\n\n## Step-by-Step Guide\n\n### Step 1: Understand Test Types\n\nAReaL has two main test categories:\n\n| Test Type             | Purpose                            | Location Pattern                   | How It Runs                                |\n| --------------------- | ---------------------------------- | ---------------------------------- | ------------------------------------------ |\n| **Unit Tests**        | Test individual functions/modules  | `tests/test_<module>_<feature>.py` | Directly via pytest                        |\n| **Distributed Tests** | Test distributed/parallel behavior | `tests/torchrun/run_*.py`          | Via torchrun (called by pytest subprocess) |\n\n**Note**: All tests are invoked via pytest. Distributed tests use `torchrun` but are\nstill called from pytest test files.\n\n### Step 2: Create Test File Structure\n\nCreate test file with naming convention: `test_<module>_<feature>.py`\n\n```python\nimport pytest\nimport torch\n\n# Import the module to test\nfrom areal.dataset.gsm8k import get_gsm8k_sft_dataset\nfrom tests.utils import get_dataset_path  # Optional test utilities\n# For mocking tokenizer: from unittest.mock import MagicMock\n```\n\n### Step 3: Write Test Functions\n\nFollow Arrange-Act-Assert pattern:\n\n```python\ndef test_function_under_condition_returns_expected():\n    \"\"\"Test that function returns expected value under condition.\"\"\"\n    # Arrange\n    input_data = 5\n    expected_output = 10\n\n    # Act\n    result = function_under_test(input_data)\n\n    # Assert\n    assert result == expected_output\n```\n\n### Step 4: Add Pytest Markers and CI Strategy\n\nUse appropriate pytest markers:\n\n| Marker                                  | When to Use                                                  |\n| --------------------------------------- | ------------------------------------------------------------ |\n| `@pytest.mark.slow`                     | Test takes > 10 seconds (excluded from CI by default)        |\n| `@pytest.mark.ci`                       | Slow test that must run in CI (use with `@pytest.mark.slow`) |\n| `@pytest.mark.asyncio`                  | Async test functions                                         |\n| `@pytest.mark.skipif(cond, reason=...)` | Conditional skip                                             |\n| `@pytest.mark.parametrize(...)`         | Parameterized tests                                          |\n\n**CI Test Strategy**:\n\n- `@pytest.mark.slow`: Excluded from CI by default (CI runs `pytest -m \"not slow\"`)\n- `@pytest.mark.slow` + `@pytest.mark.ci`: Slow but must run in CI\n- No marker: Runs in CI (fast unit tests)\n\n```python\n@pytest.mark.asyncio\nasync def test_async_function():\n    result = await async_function()\n    assert result == expected\n\n@pytest.mark.skipif(not torch.cuda.is_available(), reason=\"CUDA not available\")\ndef test_gpu_feature():\n    tensor = torch.tensor([1, 2, 3], device=\"cuda\")\n    # ... assertions\n\n@pytest.mark.parametrize(\"batch_size\", [1, 4, 16])\ndef test_with_parameters(batch_size):\n    # Parameterized test\n\n@pytest.mark.slow\ndef test_slow_function():\n    # Excluded from CI by default\n\n@pytest.mark.slow\n@pytest.mark.ci\ndef test_slow_but_required_in_ci():\n    # Slow but must run in CI\n```\n\n### Step 5: Mock Distributed Environment\n\nFor unit tests that need distributed mocks:\n\n```python\nimport torch.distributed as dist\n\ndef test_distributed_function(monkeypatch):\n    monkeypatch.setattr(dist, \"get_rank\", lambda: 0)\n    monkeypatch.setattr(dist, \"get_world_size\", lambda: 2)\n    result = distributed_function()\n    assert result == expected\n```\n\n### Step 6: Handle GPU Dependencies\n\nAlways skip gracefully when GPU unavailable:\n\n```python\nCUDA_AVAILABLE = torch.cuda.is_available()\n\n@pytest.mark.skipif(not CUDA_AVAILABLE, reason=\"CUDA not available\")\ndef test_gpu_function():\n    tensor = torch.tensor([1, 2, 3], device=\"cuda\")\n    # ... assertions\n```\n\n## Key Requirements (Based on testing.md)\n\n### Mocking Distributed\n\n- Use `torch.distributed.fake_pg` for unit tests\n- Mock `dist.get_rank()` and `dist.get_world_size()` explicitly\n- Don't mock internals of FSDP/DTensor\n\n### GPU Test Constraints\n\n- **Always skip gracefully** when GPU unavailable\n- Clean up GPU memory: `torch.cuda.empty_cache()` in fixtures\n- Use smallest possible model/batch for unit tests\n\n### Assertions\n\n- Use `torch.testing.assert_close()` for tensor comparison\n- Specify `rtol`/`atol` explicitly for numerical tests\n- Avoid bare `assert tensor.equal()` - no useful error message\n\n## Reference Implementations\n\n| Test File                        | Description                            | Key Patterns                                      |\n| -------------------------------- | -------------------------------------- | ------------------------------------------------- |\n| `tests/test_utils.py`            | Utility function tests                 | Fixtures, parametrized tests                      |\n| `tests/test_examples.py`         | Integration tests with dataset loading | Dataset path resolution, success pattern matching |\n| `tests/test_fsdp_engine_nccl.py` | Distributed tests                      | Torchrun integration                              |\n\n## Common Mistakes\n\n- **Missing test file registration**: Ensure file follows `test_*.py` naming\n- **GPU dependency without skip**: Always use `@pytest.mark.skipif` for GPU tests\n- **Incorrect tensor comparisons**: Use `torch.testing.assert_close()` not\n  `assert tensor.equal()`\n- **Memory leaks in GPU tests**: Clean up with `torch.cuda.empty_cache()`\n- **Mocking too much**: Don't mock FSDP/DTensor internals\n- **Unclear test names**: Follow `test_<what>_<condition>_<expected>` pattern\n- **No docstrings**: Add descriptive docstrings to test functions\n\n## Integration with Other Skills\n\nThis skill complements other AReaL development skills:\n\n- **After `/add-dataset`**: Add tests for new dataset loaders\n- **After `/add-workflow`**: Add tests for new workflows\n- **After `/add-reward`**: Add tests for new reward functions\n- **With expert agents**: Reference this skill when planning test implementation\n\n## Running Tests\n\n```bash\n# First check GPU availability (many tests require GPU)\npython -c \"import torch; print('GPU available:', torch.cuda.is_available())\"\n\n# Run specific test file\nuv run pytest tests/test_<name>.py\n\n# Skip slow tests (CI default)\nuv run pytest -m \"not slow\"\n\n# Run with verbose output\nuv run pytest -v\n\n# Run distributed tests (requires torchrun and multi-GPU)\n# Note: Usually invoked via pytest test files\ntorchrun --nproc_per_node=2 tests/torchrun/run_<test>.py\n```\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .opencode/skills/add-unit-tests/SKILL.md\nInvocation: /add-unit-tests\n\n## Purpose\n\nStep-by-step guide for adding unit tests to AReaL.\n\n## How to Update\n\n### When Testing Conventions Change\n1. Update \"Key Requirements\" section based on `testing.md`\n2. Update test examples to match new patterns\n3. Update reference implementations\n\n### When Test Types Need Update\n1. Update \"Understand Test Types\" table (currently two main types)\n2. Add new examples if needed\n3. Update common mistakes\n\n### Integration with Other Skills\nEnsure references to other skills (`/add-dataset`, `/add-workflow`, `/add-reward`) remain accurate.\n\n================================================================================\n-->\n",".opencode/skills/add-workflow/SKILL.md":"---\nname: add-workflow\ndescription: Guide for adding a new RolloutWorkflow to AReaL. Use when user wants to create a new workflow.\n---\n\n# Add Workflow\n\nAdd a new RolloutWorkflow implementation to AReaL.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a workflow?\"\n- User wants to create a new RolloutWorkflow\n- User mentions implementing a custom rollout\n\n## Prerequisites\n\nBefore starting, ensure you understand:\n\n- The workflow's purpose and requirements\n- Input/output data format\n- Reward function to use\n\n## Step-by-Step Guide\n\n### Step 1: Create Workflow File\n\nCreate `areal/workflow/<name>.py`:\n\n```python\nimport uuid\nfrom typing import Any, Callable\n\nimport torch\n\nfrom areal.api.cli_args import GenerationHyperparameters\nfrom areal.api.engine_api import InferenceEngine\nfrom areal.api.io_struct import ModelRequest, ModelResponse\nfrom areal.api.reward_api import AsyncRewardWrapper\nfrom areal.api.workflow_api import RolloutWorkflow\nfrom areal.utils import logging\n\nlogger = logging.getLogger(\"MyWorkflow\")\n\n\nclass MyWorkflow(RolloutWorkflow):\n    \"\"\"Description of your workflow.\"\"\"\n\n    def __init__(\n        self,\n        gconfig: GenerationHyperparameters,\n        tokenizer,\n        reward_fn: Callable,\n    ):\n        self.gconfig = gconfig.new_with_stop_and_pad_token_ids(tokenizer)\n        self.tokenizer = tokenizer\n        self.async_reward_fn = AsyncRewardWrapper(reward_fn)\n\n    async def arun_episode(\n        self,\n        engine: InferenceEngine,\n        data: dict[str, Any],\n    ) -> dict[str, Any] | None | dict[str, InteractionWithTokenLogpReward]:\n        \"\"\"Run a single episode. MUST be async and non-blocking.\"\"\"\n\n        # 1. Prepare input_ids from data\n        input_ids = self.tokenizer.apply_chat_template(\n            data[\"messages\"],\n            tokenize=True,\n            add_generation_prompt=True,\n        )\n\n        # 2. Build ModelRequest\n        req = ModelRequest(\n            rid=uuid.uuid4().hex,\n            input_ids=list(input_ids),\n            gconfig=self.gconfig.new(n_samples=1),\n            tokenizer=self.tokenizer,\n        )\n\n        # 3. Generate completion (async)\n        resp: ModelResponse = await engine.agenerate(req)\n\n        # 4. Compute reward (async)\n        prompt_str = self.tokenizer.decode(input_ids)\n        completion_str = self.tokenizer.decode(resp.output_tokens)\n        reward = await self.async_reward_fn(\n            prompt_str,\n            completion_str,\n            resp.input_tokens,\n            resp.output_tokens,\n            **data,\n        )\n\n        # 5. Return results in expected format\n        return {\n            \"input_ids\": torch.tensor(resp.input_tokens),\n            \"output_ids\": torch.tensor(resp.output_tokens),\n            \"reward\": torch.tensor(reward),\n        }\n```\n\n### Step 2: Register in __init__.py\n\nAdd to `areal/workflow/__init__.py`:\n\n```python\nfrom areal.workflow.<name> import MyWorkflow\n\n__all__ = [\n    # ... existing exports\n    \"MyWorkflow\",\n]\n```\n\n### Step 3: Update Entry Script\n\nUpdate your training script to use the new workflow:\n\n```python\ntrainer.train(\n    workflow=\"areal.workflow.<name>.MyWorkflow\",\n    # ... other args\n)\n```\n\n### Step 4: Add Tests\n\nCreate `tests/test_<name>_workflow.py`:\n\n```python\nimport pytest\nfrom areal.workflow.<name> import MyWorkflow\n\n@pytest.mark.asyncio\nasync def test_workflow_basic():\n    # Test basic functionality\n    pass\n```\n\n## Reference Implementations\n\n| Workflow           | File                            | Description                |\n| ------------------ | ------------------------------- | -------------------------- |\n| MultiTurnWorkflow  | `areal/workflow/multi_turn.py`  | Multi-turn conversation    |\n| RLVRWorkflow       | `areal/workflow/rlvr.py`        | RL with verifiable rewards |\n| VisionRLVRWorkflow | `areal/workflow/vision_rlvr.py` | Vision + RLVR              |\n\n## Key Requirements\n\n1. **Async**: `arun_episode` must be `async def` and non-blocking\n1. **No sync I/O**: Use `aiofiles` for file operations\n1. **Wrap rewards**: Use `AsyncRewardWrapper` for reward functions\n1. **Tensor format**: Output tensors should be `[batch, seq_len, ...]`\n1. **Use helpers**: `concat_padded_tensors` for combining outputs\n\n## Common Mistakes\n\n- Using `open()` instead of `aiofiles.open()`\n- Forgetting to `await` async calls\n- Not wrapping reward function with `AsyncRewardWrapper`\n- Wrong tensor shape conventions\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .opencode/skills/add-workflow/SKILL.md\nInvocation: /add-workflow <name>\n\n## Purpose\n\nStep-by-step guide for adding new RolloutWorkflow implementations.\n\n## How to Update\n\n### When Workflow API Changes\n1. Update the code template in Step 1\n2. Update the required imports\n3. Update the method signature if changed\n\n### When New Patterns Emerge\n1. Add to \"Reference Implementations\" table\n2. Update \"Key Requirements\" if new requirements added\n\n================================================================================\n-->\n",".opencode/skills/commit-conventions/SKILL.md":"---\nname: commit-conventions\ndescription: AReaL commit message conventions. MUST load on every git commit -- provides Conventional Commits format with scope inference from file paths.\n---\n\n# Commit Conventions\n\nCommit message conventions and scope inference rules for the AReaL repository.\n\n## When to Use\n\n**ALWAYS load this skill when making any git commit in AReaL.** This includes:\n\n- Direct commits (`git commit`)\n- Commits during PR creation (`/create-pr`)\n- Commits delegated via `task(load_skills=[\"commit-conventions\"], ...)`\n- Any agent workflow that produces a commit\n\n## Commit Message Format\n\n```\n<type>(<scope>): <subject>\n\n<body>\n\n[Optional sections:]\nKey changes:\n- change 1\n- change 2\n\nRefs: #123, #456\n```\n\n## Type Selection\n\n| Type       | When to Use                      |\n| ---------- | -------------------------------- |\n| `feat`     | New feature or capability        |\n| `fix`      | Bug fix                          |\n| `docs`     | Documentation only               |\n| `gov`      | Governance or maintainer changes |\n| `style`    | Formatting/style-only changes    |\n| `refactor` | Code change without feature/fix  |\n| `perf`     | Performance improvement          |\n| `test`     | Adding or fixing tests           |\n| `build`    | Build system or dependencies     |\n| `ci`       | CI pipeline or workflow changes  |\n| `chore`    | Build, deps, config changes      |\n| `revert`   | Revert a previous commit         |\n\n## Scope Inference\n\nInfer scope from the **primary** changed file paths:\n\n| File Path Pattern                                            | Scope                          |\n| ------------------------------------------------------------ | ------------------------------ |\n| `areal/workflow/`                                            | `workflow`                     |\n| `areal/engine/`                                              | `engine`                       |\n| `areal/reward/`                                              | `reward`                       |\n| `areal/dataset/`                                             | `dataset`                      |\n| `areal/api/`                                                 | `api`                          |\n| `areal/utils/`                                               | `utils`                        |\n| `areal/infra/`                                               | `infra`                        |\n| `areal/trainer/`                                             | `trainer`                      |\n| `areal/models/`                                              | `models`                       |\n| `areal/experimental/`                                        | `archon`                       |\n| `docs/`                                                      | `docs`                         |\n| `examples/`                                                  | `examples`                     |\n| `AGENTS.md`, `.agents/`, `.claude/`, `.codex/`, `.opencode/` | `agents`                       |\n| Multiple areas                                               | Omit scope or use broader term |\n\n## Rules\n\n- **Subject**: imperative mood, ~50-72 chars, no trailing period\n- **Body**: explain \"why\" not \"what\", wrap at 72 chars\n- **Key changes**: bullet list of main modifications (for complex commits with 3+ files)\n- **Refs**: reference issues/PRs if applicable\n\n## Examples\n\n**Single file fix:**\n\n```\nfix(reward): handle empty completion in gsm8k\n\nReturn 0 reward instead of raising exception when\ncompletion string is empty after extraction.\n```\n\n**Multi-file feature:**\n\n```\nfeat(engine): add CPU offload support to ArchonEngine\n\nEnable torch_memory_saver for model offloading during\nrollout phase to reduce GPU memory pressure.\n\nKey changes:\n- Add offload/onload methods to ArchonEngine\n- Integrate with weight update flow\n- Handle ROCm compatibility\n```\n\n**Docs only:**\n\n```\ndocs: update algorithm comparison table\n\nAdd SAPO and GSPO to the algorithm family documentation\nwith configuration examples.\n```\n\n**Agent/tooling changes:**\n\n```\nchore(agents): port review-pr command to OpenCode\n\nAdd OpenCode-native commands with task() category\ndelegation instead of hardcoded model names.\n\nKey changes:\n- Create .opencode/command/ with review-pr, create-pr\n- Replace hardcoded model routing with platform-native review routing\n- Add expert subagent consultation patterns\n```\n\n**Governance/maintainer changes:**\n\n```\ngov(agents): add maintainer ownership for service modules\n\nUpdate CODEOWNERS and maintainer references to reflect\ncurrent governance responsibilities.\n\nKey changes:\n- Add maintainers for agent_service and infra ownership\n- Align governance docs with updated reviewer responsibilities\n```\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .opencode/skills/commit-conventions/SKILL.md\nInvocation: Automatically loaded on every git commit via load_skills\n\n## Purpose\n\nProvides Conventional Commits format with AReaL-specific scope inference\nfrom file paths. Unlike other skills, this one is NOT user-triggered --\nit is loaded by the system/agent on every commit operation.\n\n## How to Update\n\n### When New Modules Are Added\n1. Add the file path pattern and scope to the \"Scope Inference\" table\n2. Keep table sorted by areal/ subpackages first, then top-level dirs\n\n### When Commit Types Change\n1. Update the \"Type Selection\" table\n2. Add/update examples to illustrate the new type\n\n### When Adding Examples\n1. Each example should demonstrate a distinct commit pattern\n2. Keep examples realistic -- use actual AReaL module names\n3. Show both subject-only and subject+body+key-changes variants\n\n### Important Design Decisions\n- This skill is ALWAYS loaded (not optional) -- keep it concise to\n  minimize token overhead on every commit\n- Scope inference is path-based, not content-based -- simpler and\n  more deterministic\n- \"Multiple areas\" -> omit scope rather than invent a new one\n\n================================================================================\n-->\n",".opencode/skills/debug-distributed/SKILL.md":"---\nname: debug-distributed\ndescription: Guide for debugging distributed training issues in AReaL. Use when user encounters hangs, wrong results, OOM, or communication errors.\n---\n\n# Debug Distributed Training\n\nDebugging guide for distributed training issues in AReaL (FSDP2, TP, CP, EP).\n\n## When to Use\n\nThis skill is triggered when:\n\n- Training hangs or deadlocks\n- Results differ across ranks or are numerically wrong\n- OOM errors in distributed settings\n- NCCL/communication errors or device mesh issues\n\n## Debugging Principles\n\n### Minimal Reproduction\n\n**Always follow the minimal demo principle**: Reproduce with the least amount of code to\nnarrow down the issue faster.\n\n```python\n# Bad: Debug in full training loop\n# Good: Create minimal script\nimport torch\nimport torch.distributed as dist\n\ndist.init_process_group(\"nccl\")\nrank = dist.get_rank()\n\n# Reproduce the exact operation that fails\ntensor = torch.ones(10).cuda()\ndist.all_reduce(tensor)  # <-- Isolate the failing op\nprint(f\"Rank {rank}: {tensor}\")\n```\n\n**Reduction strategy:**\n\n1. Remove unrelated model components\n1. Use small tensor sizes\n1. Reduce world_size to minimum (e.g., 2 GPUs)\n1. Remove torch.compile if possible\n1. Disable activation checkpointing\n\n## Step-by-Step Debugging Guide\n\n### 1. Hang Debugging (Deadlocks, Synchronization)\n\n**Environment Variables for Debugging**:\n\n```bash\n# Full debug logging\nexport TORCH_DISTRIBUTED_DEBUG=DETAIL\nexport NCCL_DEBUG=INFO\nexport NCCL_DEBUG_SUBSYS=ALL\n\n# torch.compile debugging\nexport TORCH_LOGS=\"+dynamo,recompiles\"\nexport TORCHDYNAMO_VERBOSE=1\n```\n\n**Dump Call Stack with py-spy** (for hung processes):\n\n```bash\n# Find process IDs\nps aux | grep python\n\n# Dump call stack of specific rank\npy-spy dump --pid <PID>\n\n# Record flame graph for performance analysis\npy-spy record -o profile.svg --pid <PID> --duration 30\n```\n\n**Common Causes**:\n\n1. **Mismatched Collectives**: One rank calls `all_reduce`, another doesn't.\n1. **Wrong Process Group**: Using wrong group for collective.\n1. **Tensor Shape Mismatch**: Different shapes across ranks.\n\n**Debug Steps**:\n\n```python\n# Verify group membership\nmesh = parallel_dims.get_mesh(\"dp_shard_cp\")\ngroup = mesh.get_group()\nprint(f\"Rank {dist.get_rank()}: group size = {dist.get_world_size(group)}\")\n\n# Print shapes on all ranks\nprint(f\"Rank {dist.get_rank()}: tensor.shape = {tensor.shape}\")\ndist.barrier()\n```\n\n**Timeout Adjustment** (for debugging only):\n\n```python\nfrom areal.engine.core.distributed import patch_dist_group_timeout\nfrom datetime import timedelta\npatch_dist_group_timeout(timedelta(minutes=30))\n```\n\n### 2. Wrong Results (Gradient, Reduction Issues)\n\n**Check DTensor Placements**:\n\n```python\nfrom torch.distributed.tensor import DTensor\nif isinstance(param, DTensor):\n    print(f\"Param {name}: placements={param.placements}, mesh={param.device_mesh}\")\n```\n\n**Verify Gradient Reduction**:\n\n```python\nfor name, param in model.named_parameters():\n    if param.grad is not None:\n        print(f\"Rank {dist.get_rank()}: {name} grad_sum = {param.grad.sum().item()}\")\n```\n\n### 3. OOM Issues (Memory, Sharding)\n\n**Check Memory Usage**:\n\n```python\nprint(f\"Rank {dist.get_rank()}: \"\n      f\"allocated={torch.cuda.memory_allocated()/1e9:.2f}GB, \"\n      f\"reserved={torch.cuda.memory_reserved()/1e9:.2f}GB\")\n```\n\n**Check FSDP Coverage**:\n\n```python\nfor name, param in model.named_parameters():\n    is_dtensor = isinstance(param, DTensor)\n    print(f\"{name}: is_dtensor={is_dtensor}, shape={param.shape}\")\n```\n\n### 4. Communication Errors\n\n| Error                     | Cause                | Solution                           |\n| ------------------------- | -------------------- | ---------------------------------- |\n| `NCCL WARN Cuda failure`  | GPU communication    | Check NCCL version, GPU topology   |\n| `RuntimeError: Timed out` | Rank synchronization | Increase timeout, check code paths |\n| `Invalid device mesh`     | Mesh configuration   | Verify world_size = dp * tp * cp   |\n\n## Debugging Tools\n\n### Environment Variables Reference\n\n| Variable                          | Purpose                                |\n| --------------------------------- | -------------------------------------- |\n| `TORCH_DISTRIBUTED_DEBUG=DETAIL`  | Detailed distributed logging           |\n| `NCCL_DEBUG=INFO`                 | NCCL communication logging             |\n| `NCCL_DEBUG_SUBSYS=ALL`           | All NCCL subsystems                    |\n| `TORCH_LOGS=\"+dynamo,recompiles\"` | torch.compile logging                  |\n| `TORCHDYNAMO_VERBOSE=1`           | Dynamo verbose output                  |\n| `CUDA_LAUNCH_BLOCKING=1`          | Synchronous CUDA (slow, for debugging) |\n\n### py-spy for Call Stack Analysis\n\n```bash\n# Install\npip install py-spy\n\n# Dump call stack of hung process\npy-spy dump --pid <PID>\n\n# Dump all Python processes\npgrep -f python | xargs -I {} py-spy dump --pid {}\n\n# Record flame graph\npy-spy record -o profile.svg --pid <PID> --duration 30\n```\n\n### Rank-Conditional Printing\n\n```python\ndef print_all_ranks(msg):\n    for r in range(dist.get_world_size()):\n        if dist.get_rank() == r:\n            print(f\"[Rank {r}] {msg}\")\n        dist.barrier()\n```\n\n### Check Device Mesh\n\n```python\ndef debug_mesh(parallel_dims):\n    mesh = parallel_dims.world_mesh\n    for dim_name in mesh.mesh_dim_names:\n        submesh = parallel_dims.get_mesh(dim_name)\n        if submesh:\n            print(f\"Rank {dist.get_rank()}: {dim_name} size={submesh.size()}\")\n```\n\n### Validate Tensor Consistency\n\n```python\ndef check_tensor_consistency(tensor, name, group=None):\n    local_sum = tensor.sum().item()\n    tensor_sums = [None] * dist.get_world_size(group)\n    dist.all_gather_object(tensor_sums, local_sum, group=group)\n    if dist.get_rank() == 0 and len(set(tensor_sums)) > 1:\n        print(f\"WARNING: {name} inconsistent: {tensor_sums}\")\n```\n\n## Key Files Reference\n\n| Component       | File                                                          |\n| --------------- | ------------------------------------------------------------- |\n| Parallel Dims   | `areal/experimental/models/archon/parallel_dims.py`           |\n| Expert Parallel | `areal/experimental/models/archon/expert_parallel.py`         |\n| Ulysses (CP)    | `areal/experimental/models/archon/ulysses.py`                 |\n| FSDP/TP Apply   | `areal/experimental/models/archon/qwen2/infra/parallelize.py` |\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .opencode/skills/debug-distributed/SKILL.md\nInvocation: /debug-distributed\n\n## Purpose\n\nDebugging guide for distributed training issues.\nCovers FSDP2, Tensor Parallelism, Context Parallelism, and Expert Parallelism.\n\n## How to Update\n\n### When Adding New Parallelism Features\n1. Add section for the parallelism type\n2. Document common error patterns and debugging snippets\n\n### When PyTorch Distributed APIs Change\n1. Update DTensor/DeviceMesh examples\n2. Update environment variable references\n\n### When New Error Patterns Emerge\n1. Add to \"Common Errors and Solutions\" table\n2. Reference relevant source files\n\n================================================================================\n-->\n","AGENTS.md":"<!-- Go-to brief for AI coding agents working on AReaL. -->\n\n# AGENTS.md -- AReaL Agent Operations Guide\n\n## Quick reference\n\n**Tech stack**: Python 3.12+ | PyTorch | FSDP2 / Megatron / Archon | SGLang / vLLM\n\n```bash\n# Environment\nuv sync --extra cuda            # CUDA + SGLang inference (default); for vLLM: cp pyproject.vllm.toml pyproject.toml && cp uv.vllm.lock uv.lock && uv sync --extra cuda\nuv sync --extra sandbox         # Daytona cloud sandbox backend (optional)\nsource .venv/bin/activate        # activate venv BEFORE pre-commit or git commit if venv exists\npre-commit install --install-hooks  # hooks: Ruff, clang-format, mdformat, nbstripout, conventional-commits\npre-commit run --all-files       # lint + format everything\n\n# Tests\nuv run pytest tests/test_<topic>.py\n\n# CLI docs\nuv run python docs/generate_cli_docs.py\n\n# Docs build (canonical, release-aligned)\n./docs/build_all.sh\n# Do NOT use `jupyter-book build docs/en|docs/zh` directly for final preview/release,\n# because it skips AReaL-specific static setup and output packaging.\n```\n\n**Hard rules** -- never violate:\n\n- No wildcard imports (`from x import *`).\n- No hardcoded secrets, paths, or endpoints.\n- No skipping pre-commit hooks.\n- No guessing cluster configs or rebuilding CUDA/driver stacks.\n- Integration tests require multi-node hardware -- explain skips explicitly.\n\n**Always do**:\n\n- Read relevant files before modifying code.\n- Run `pre-commit run --all-files` before committing.\n- Follow existing code patterns in the same module.\n- Add tests for new functionality.\n- Ask for decisions and clarifications with short, structured options instead of broad\n  open-ended questions. Use the platform's native question/clarification tool if\n  available.\n\n**Ask first** before:\n\n- Modifying config structures in `areal/api/cli_args.py`.\n- Adding new dependencies.\n- Changing launcher or scheduler logic.\n- Deleting or renaming public APIs.\n\nWhen unsure, leave a `TODO(agent)` comment and note the constraint in your response.\n\n______________________________________________________________________\n\n## Repository map\n\n```\nareal/                     Core Python package\n|-- api/                   Config dataclasses, contracts, IO structs\n|-- dataset/               Stateful dataset loaders (GSM8K, Geometry3K, CLEVR, ...)\n|-- engine/                Training backends (FSDP2, Megatron) + inference adapters\n|-- experimental/          Prototype engines/workflows (Archon MoE engine)\n|-- infra/                 Launchers (Local/Ray/Slurm), schedulers, utilities\n|-- models/                Model adapters (Megatron-Core, Transformers, custom heads)\n|-- reward/                Built-in reward functions + math parsers\n|-- tests/                 Unit/integration test suites\n|-- trainer/               High-level orchestrators (PPOTrainer, SFTTrainer)\n|-- utils/                 Cross-cutting helpers (logging, data, checkpoints, RL ops)\n+-- workflow/              RolloutWorkflow implementations (RLVR, multi-turn, vision)\n\ndocs/                      Jupyter Book docs (https://areal-project.github.io/AReaL/)\nexamples/                  Training scripts and launcher recipes\n```\n\n______________________________________________________________________\n\n## Code style & patterns\n\n- **Composition over inheritance** -- keep hierarchies \\<= 2 levels; prefer delegation.\n\n| Type             | Pattern         | Example                                   |\n| ---------------- | --------------- | ----------------------------------------- |\n| Config dataclass | `XxxConfig`     | `GRPOConfig`, `FSDPEngineConfig`          |\n| Engine class     | `XxxEngine`     | `FSDPEngine`, `ArchonEngine`              |\n| Workflow class   | `XxxWorkflow`   | `RLVRWorkflow`, `MultiTurnWorkflow`       |\n| Reward function  | `xxx_reward_fn` | `gsm8k_reward_fn`, `geometry3k_reward_fn` |\n\n**Logging**: `areal.utils.logging.getLogger(name)` with **PascalCase** names -- never\n`print` or `logging.__name__`. Per-rank format: `[{Component} Rank {N}]`. Register new\nloggers with color in `areal/utils/logging.py`.\n\n**Performance**:\n\n- No GPU-CPU sync in hot paths (`.item()`, `.tolist()`, `print(tensor)`).\n- Batch ops over Python loops on tensor elements.\n- Explicit `dtype`/`device`; `torch.Size` assertions for shape validation.\n\n**Typing & imports**: explicit type hints; reuse `areal/api/cli_args.py` dataclasses; no\nwildcard imports; heavy optional deps inside functions.\n\n**Async**: rollout workflows must stay non-blocking (`await` + `aiofiles`); no sync I/O\nin `arun_episode`.\n\n______________________________________________________________________\n\n## Domain experts & skills\n\nFire the appropriate **expert subagent** or **load a skill** based on what you're\nworking on. Experts are read-only consultants with deep domain knowledge; skills are\nstep-by-step implementation guides.\n\n| Working on...                | Fire subagent      | Load skill          |\n| ---------------------------- | ------------------ | ------------------- |\n| FSDP engine code             | `fsdp-expert`      | --                  |\n| Archon engine / new model    | `archon-expert`    | `add-archon-model`  |\n| Megatron engine code         | `megatron-expert`  | --                  |\n| RL algorithms / PPO / GRPO   | `algorithm-expert` | --                  |\n| Launcher / scheduler / infra | `launcher-expert`  | `debug-distributed` |\n| New reward function          | --                 | `add-reward`        |\n| New dataset loader           | --                 | `add-dataset`       |\n| New rollout workflow         | --                 | `add-workflow`      |\n| Unit tests                   | --                 | `add-unit-tests`    |\n| Distributed debugging        | --                 | `debug-distributed` |\n\n**How to invoke experts and skills** (platform-specific):\n\n| Platform | Fire expert subagent                                                               | Load skill                                         |\n| -------- | ---------------------------------------------------------------------------------- | -------------------------------------------------- |\n| OpenCode | `task(subagent_type=\"<name>\", load_skills=[], run_in_background=true, prompt=\"…\")` | `skill(name=\"<name>\")` or `load_skills=[\"<name>\"]` |\n| Codex    | Invoke registered subagent by canonical name (see `.codex/config.toml`)            | Reference `.agents/skills/<name>/SKILL.md`         |\n\n**Harness layout**:\n\n| Component         | OpenCode                                | Codex                                                  |\n| ----------------- | --------------------------------------- | ------------------------------------------------------ |\n| Root instructions | `AGENTS.md`                             | `AGENTS.md`                                            |\n| Agent configs     | `.opencode/agents/*.md` (frontmatter)   | `.codex/config.toml` + `.codex/agents/*.toml` + `*.md` |\n| Skills            | `.opencode/skills/` + `.agents/skills/` | `.agents/skills/<name>/SKILL.md`                       |\n\nDirectly executable workflows (both platforms): `add-workflow`, `review-pr`,\n`create-pr`, `translate-doc-zh`.\n\n______________________________________________________________________\n\n## Core concepts\n\n**Trainer** orchestrator (`areal/trainer/`, `PPOTrainer`, `SFTTrainer`): manages the\ntraining loop, dataset loading, and workflow execution. Entry point:\n`examples/math/gsm8k_rl.py`.\n\n**Rollout workflows** (`areal/workflow/`, `RolloutWorkflow.arun_episode`): define how\nepisodes are generated. Use `add-workflow` skill for step-by-step guide.\n\n**Engines**: *Inference engines* handle async generation via `engine.agenerate()` and\nmanage weight updates. *Training engines* consume rollout tensors, compute PPO/GRPO\nupdates, and broadcast weight versions (FSDP2, Megatron, or Archon).\n\n**Weight versioning**: async workflows require version alignment via `WeightUpdateMeta`\n(`areal/api/engine_api.py`). Critical for correctness across distributed training.\n\n**Observability**: emit metrics via `stats_tracker.get()`, persist artifacts under\n`dump_dir`, checkpoint via `areal/utils/saver.py` / `recover.py`.\n\n**Launcher / scheduler**: training requires cluster setup (local / Ray / Slurm) via\nconfigs in `areal/infra/launcher/`. See `launcher-expert` for deployment guidance.\n\n______________________________________________________________________\n\n## API & config rules\n\n*Applies to: `areal/api/**`*\n\n- **Field ordering**: required -> common optional -> rare optional -> internal (`_`\n  prefix).\n- **Validation**: `__post_init__` with `ValueError` and clear message.\n- **Backward compat**: add fields with defaults; deprecate before removing; avoid type\n  changes.\n- **CLI**: use `Literal` for enum choices; all public configs need docstrings with\n  constraints.\n\n______________________________________________________________________\n\n## Distributed code rules\n\n*Applies to: `areal/engine/**`, `areal/experimental/**`*\n\n- Never create global process groups at module level; always pass `process_group`\n  explicitly.\n- `dist.get_rank(group)` not `dist.get_rank()` when group matters.\n- DeviceMesh dimensions must match `ArchonParallelDims`: `dp_shard`, `tp`, `cp`, `ep`,\n  `etp`.\n- All-reduce: all ranks must call. Broadcast: explicit `src`. Barrier: debugging only.\n\n| Issue         | Cause                            | Fix                       |\n| ------------- | -------------------------------- | ------------------------- |\n| Hang          | Mismatched collective calls      | All ranks call same op    |\n| Wrong results | Incorrect `ReduceOp`             | Check SUM vs MEAN         |\n| OOM           | Unsharded tensor on wrong device | Verify DTensor placements |\n\nDebug env vars: `TORCH_DISTRIBUTED_DEBUG=DETAIL`, `NCCL_DEBUG=INFO`,\n`CUDA_LAUNCH_BLOCKING=1`. See the `debug-distributed` skill for the full workflow.\n\n______________________________________________________________________\n\n## Testing rules\n\n*Applies to: `**/tests/**`, `test_*.py`*\n\n| Marker                                  | When                             |\n| --------------------------------------- | -------------------------------- |\n| `@pytest.mark.slow`                     | > 10s (excluded from default CI) |\n| `@pytest.mark.slow` + `@pytest.mark.ci` | Slow but must run in CI          |\n| `@pytest.mark.asyncio`                  | Async tests                      |\n\n- Naming: `test_<what>_<condition>_<expected>()` with Arrange/Act/Assert.\n- GPU: skip gracefully (`@pytest.mark.skipif(not CUDA_AVAILABLE, reason=\"...\")`).\n- Distributed mocking: `torch.distributed.fake_pg`; don't mock FSDP/DTensor internals.\n- Assertions: `torch.testing.assert_close()` with explicit `rtol`/`atol`; prefer\n  `tmp_path`, `monkeypatch`.\n\n| Suite       | Command                       | GPU       |\n| ----------- | ----------------------------- | --------- |\n| Unit        | `pytest tests/test_*.py`      | No        |\n| GRPO        | `pytest tests/grpo/`          | Yes       |\n| FSDP        | `pytest tests/test_fsdp_*.py` | Yes       |\n| Distributed | `pytest tests/torchrun/`      | Multi-GPU |\n\n______________________________________________________________________\n\n## Collaboration & review\n\n- **Branches**: kebab-case (`feature/multi-turn-metrics`, `bugfix/fsdp-weight-sync`).\n- **Commits**: Conventional Commits (e.g., `feat:`, `fix:`, `docs:`, `gov:`), ~72 char\n  subject, imperative voice. Squash WIP before PR.\n- **Pre-merge**: full pre-commit stack; doc-only edits need at least `mdformat --check`.\n- **PRs**: tie to issue, highlight risk areas, list test commands executed, note skipped\n  suites with reasons.\n\n| Skill                | Purpose                                                |\n| -------------------- | ------------------------------------------------------ |\n| `create-pr`          | Rebase, squash, and create or update a PR              |\n| `commit-conventions` | Commit message conventions to load before `git commit` |\n| `review-pr`          | Dynamic PR review with targeted expert consultation    |\n| `translate-doc-zh`   | Translate English docs to Chinese                      |\n\n______________________________________________________________________\n\n## Reference material\n\n- **Docs portal**: <https://areal-project.github.io/AReaL/>\n- **Quickstart**: `docs/tutorial/quickstart.md`\n- **Architecture**: `docs/tutorial/gsm8k_grpo.md`\n- **Customization**: `docs/customization/*.md`\n- **Algorithms**: `docs/algorithms/*.md`\n- **Best practices**: `docs/best_practices/*.md`\n- **CLI reference**: `docs/cli_reference.md`\n- **Agent workflow**: `docs/customization/agent.md`\n","CLAUDE.md":"# CLAUDE.md - AReaL\n\n## WHAT: Project Overview\n\nAReaL is a distributed RL training framework for LLM alignment via reinforcement\nlearning.\n\n**Tech Stack**: Python 3.12+ | PyTorch | FSDP2/Megatron | SGLang/vLLM\n\n**Core Directories**:\n\n- `areal/` - Core package\n  - `api/` - Config dataclasses, workflow/engine contracts\n  - `engine/` - FSDP2, Megatron, SGLang/vLLM adapters\n    - `fsdp_utils/` - FSDP2-specific utilities (checkpoint, grad, optimizer, parallel)\n    - `megatron_utils/` - Megatron/FP8 utilities (checkpoint, pipeline, quantization)\n    - `core/` - Engine-shared utilities (distributed, lock, model, offload)\n  - `infra/` - Infrastructure (launcher, scheduler, RPC)\n    - `utils/` - Infrastructure utilities (launcher, proc, http, concurrent, slurm, ray)\n  - `workflow/` - RolloutWorkflow implementations\n  - `reward/` - Reward functions\n  - `dataset/` - Dataset loaders\n  - `utils/` - Cross-cutting utilities (logging, data, checkpoints, network, RL\n    functional)\n- `examples/` - Training scripts and configs\n- `docs/` - Jupyter Book source\n\n## WHY: Purpose\n\n- Enable efficient RL training for LLM alignment at scale\n- Async rollout + distributed training for high throughput\n- Modular design: workflows, engines, rewards, and datasets are independently extensible\n\n## HOW: Core Commands\n\n```bash\n# Check environment\npython --version              # Requires 3.12+\nuv --version                  # Install: https://docs.astral.sh/uv/\n\n# Sync dependencies\nuv sync --extra cuda          # CUDA + SGLang inference (default)\n# For vLLM: cp pyproject.vllm.toml pyproject.toml && cp uv.vllm.lock uv.lock && uv sync --extra cuda\nuv sync --group dev           # Include dev/test packages\nuv run python3 areal/tools/validate_installation.py  # Validate installation\n\n# Pre-commit hooks\npre-commit install --install-hooks  # Set up hooks (run once)\npre-commit run --all-files    # Format and lint\n\n# Run tests\n# First check GPU availability (many tests require GPU)\npython -c \"import torch; print('GPU available:', torch.cuda.is_available())\"\nuv run pytest tests/test_<topic>.py\n\n# Generate CLI docs\nuv run python docs/generate_cli_docs.py\n\n# Build docs (canonical, release-aligned)\n./docs/build_all.sh\n# Do NOT use `jupyter-book build docs/en|docs/zh` directly for final preview/release,\n# because it skips AReaL-specific static setup and output packaging.\n```\n\n## Boundaries\n\n### Constraints\n\n- Designed for distributed GPU clusters; assume containerized execution\n- Integration tests require multi-node hardware; explain skips when unavailable\n- Secrets and endpoints are managed outside the repo\n\n### Always Do\n\n- Read relevant files before modifying code\n- Run `pre-commit run --all-files` before committing\n- Follow existing code patterns in the same module\n- Add tests for new functionality\n\n### Ask First\n\n- Modifying config structures in `areal/api/cli_args.py`\n- Adding new dependencies\n- Changing launcher or scheduler logic\n- Deleting or renaming public APIs\n- Running GPU/distributed tests (check GPU first:\n  `python -c \"import torch; print('GPU available:', torch.cuda.is_available())\"`)\n\n### Never Do\n\n- Hardcode secrets, paths, or endpoints\n- Skip pre-commit hooks\n- Guess cluster configs or rebuild CUDA/driver stacks\n- Use wildcard imports (`from x import *`)\n\n## Progressive Disclosure: Detailed Guides\n\n| Task                   | Reference                                                     |\n| ---------------------- | ------------------------------------------------------------- |\n| Add Workflow           | `docs/customization/agent.md`, `areal/workflow/multi_turn.py` |\n| Add Dataset            | `docs/customization/`, `areal/dataset/gsm8k.py`               |\n| Add Reward             | `areal/api/reward_api.py`, `areal/reward/geometry3k.py`       |\n| Add Archon Model       | `areal/experimental/models/archon/qwen2/`, `qwen3/`           |\n| Algorithm Details      | `docs/algorithms/*.md`                                        |\n| R3 Router Replay       | `r3/README.md`                                                |\n| Quickstart             | `docs/tutorial/quickstart.md`                                 |\n| Architecture Deep Dive | `docs/tutorial/gsm8k_grpo.md`                                 |\n| CLI Reference          | `docs/cli_reference.md`                                       |\n\n## Git Workflow\n\n- **Commits**: Conventional Commits (e.g., `feat:`, `fix:`, `docs:`, `gov:`), ~72 chars\n  subject, imperative voice, reasoning in body\n- **Squash**: Squash WIP commits before opening PR\n- **PR requirements**: Run pre-commit, document test coverage, note hardware limitations\n\n## Extended Configuration\n\nSee `.claude/agents/`, `.claude/skills/`, `.claude/commands/`, and `.claude/rules/` for\nspecialized instructions.\n\n### Agents\n\n| Agent                       | Purpose                                   | Activation Trigger                                                  |\n| --------------------------- | ----------------------------------------- | ------------------------------------------------------------------- |\n| `planner`                   | Implementation planning                   | Before multi-file changes, new features, or architectural decisions |\n| `simple-code-reviewer`      | Quick code quality checks                 | After code changes, before committing                               |\n| `code-verifier`             | Formatting/linting/tests                  | After code changes, before committing                               |\n| `fsdp-engine-expert`        | FSDPEngine implementation                 | FSDPEngine code changes or questions                                |\n| `archon-engine-expert`      | ArchonEngine implementation               | ArchonEngine code changes or questions                              |\n| `megatron-engine-expert`    | MegatronEngine implementation             | MegatronEngine code changes or questions                            |\n| `algorithm-expert`          | RL algorithms                             | GRPO/PPO/DAPO questions                                             |\n| `launcher-scheduler-expert` | Cluster launching and resource scheduling | Launcher/scheduler code changes or configuration questions          |\n\n**Stage-by-Stage Agent Guidance**:\n\n1. **Planning Stage** (Before coding): Use `planner` for architecture design and\n   implementation planning\n1. **Code Formatting & Linting** (After coding): Use `code-verifier` to automatically\n   run formatting, linting, and tests, catching syntax errors and style issues quickly\n1. **Code Quality Check** (After formatting): Use `simple-code-reviewer` for quick code\n   quality checks, focusing on logic issues and code smells\n\n### Skills (Guided Development Workflows)\n\nSkills provide step-by-step guides for common development tasks:\n\n- `/add-dataset` - Dataset loader creation guide\n- `/add-workflow` - Workflow implementation guide\n- `/add-reward` - Reward function guide\n- `/add-archon-model` - Archon engine model architecture guide\n- `/debug-distributed` - Distributed debugging guide\n- `/add-unit-tests` - Test development guide (NEW)\n\n### Commands (User-invoked Actions)\n\nCommands perform specific actions when invoked:\n\n- `/create-pr` - Rebase, squash commits, and create/update PR with intelligent messages\n- `/gen-commit-msg` - Generate commit messages from staged changes\n- `/review-pr` - Intelligent PR code review with dynamic agent allocation\n- `/translate-doc-zh` - Translate English documentation to Chinese\n\n### Rules (Code Quality Standards)\n\nProject-wide standards enforced across all code changes:\n\n- `api-config.md` - Configuration dataclass design patterns\n- `code-style.md` - Coding conventions beyond pre-commit hooks\n- `distributed.md` - Distributed training patterns and constraints\n- `testing.md` - Testing strategy and coverage requirements\n"},"files":{".agents/skills/add-archon-model/SKILL.md":"---\nname: add-archon-model\ndescription: Guide for adding a new model to the Archon engine. Use when user wants to add support for a new HuggingFace model architecture in ArchonEngine.\n---\n\n# Add Archon Model\n\nAdd support for a new HuggingFace model architecture in the Archon training engine.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a model to Archon?\"\n- User wants to support a new model family (e.g., Llama, Mistral, DeepSeek) in\n  ArchonEngine\n- User mentions adding a new `ModelSpec` or model type for Archon\n\n## Prerequisites\n\nBefore starting, ensure:\n\n- The target model is available on HuggingFace (has `config.json` with `model_type`)\n- You know the HuggingFace model ID (e.g., `meta-llama/Llama-3-8B`)\n- The model uses a standard transformer architecture (decoder-only)\n\n## Step-by-Step Guide\n\n### Step 1: Analyze the Target Model Architecture\n\nRead the HuggingFace model's source code to extract key architecture information.\n\n**Action**: Fetch and analyze the model's HuggingFace configuration and modeling files.\n\n1. Read the model's `config.json` (via `AutoConfig.from_pretrained`) to identify:\n\n   - `model_type` string (this is the key used for registry lookup)\n   - All architecture hyperparameters (hidden_size, num_layers, etc.)\n   - Any model-specific fields (e.g., `qk_norm`, `attention_bias`, MoE fields)\n\n1. Read the HuggingFace `modeling_*.py` source to identify:\n\n   - **Attention variant**: Does it have Q/K norm? Attention bias? Sliding window?\n     Multi-latent attention?\n   - **FFN variant**: SwiGLU (gate_proj + up_proj + down_proj)? GeGLU? Standard MLP?\n   - **MoE support**: Does it have MoE layers? What router type? Shared experts?\n   - **RoPE variant**: Standard RoPE? YaRN? NTK-aware scaling? What is the inv_freq\n     formula?\n   - **Normalization**: RMSNorm or LayerNorm? Pre-norm or post-norm? Elementwise affine?\n   - **Weight tying**: Does `tie_word_embeddings` appear in config?\n   - **State dict key names**: What are the HF weight key naming conventions?\n\n1. Summarize findings in a checklist like:\n\n```\nTarget model: <name>\nHF model_type: \"<model_type>\" (and variants like \"<model_type>_moe\" if applicable)\nAttention: [standard GQA / with QK norm / with bias / sliding window / ...]\nFFN: [SwiGLU / GeGLU / standard MLP / ...]\nMoE: [no / yes - num_experts, top_k, shared_experts]\nRoPE: [standard / YaRN / NTK-aware / ...]\nNorm: [RMSNorm / LayerNorm] with [pre-norm / post-norm]\nWeight tying: [yes / no]\n```\n\n### Step 2: Select the Reference Model\n\nChoose the closest existing implementation as a starting point:\n\n| Target characteristics               | Reference | Why                                     |\n| ------------------------------------ | --------- | --------------------------------------- |\n| Dense-only, standard GQA, no QK norm | `qwen2`   | Simplest baseline, pure dense           |\n| Has QK norm, or has MoE support      | `qwen3`   | Supports QK norm + MoE + shared experts |\n\n**Action**: Copy the reference model directory as the starting point:\n\n```\nareal/experimental/models/archon/<model>/\n  __init__.py\n  spec.py\n  model/\n    args.py\n    model.py\n    rope.py\n    state_dict_adapter.py\n  infra/\n    parallelize.py\n```\n\n### Step 3: Implement `args.py`\n\nAdapt `<Model>ModelArgs` to match the target model's HuggingFace config fields.\n\n**Key changes from reference**:\n\n1. Update the `@dataclass` fields to match the target model's hyperparameters:\n\n   - Field names should use Archon conventions (`dim`, `n_layers`, `n_heads`,\n     `n_kv_heads`, `vocab_size`, `head_dim`, `hidden_dim`, `norm_eps`, `rope_theta`,\n     etc.)\n   - Default values should match the smallest variant of the target model\n   - Add model-specific fields (e.g., `attention_bias`, `qk_norm`, `sliding_window`)\n\n1. Update `from_hf_config()` to correctly map HuggingFace config attributes:\n\n   - Use `getattr(hf_config, \"field_name\", default)` for optional fields\n   - Handle variant-specific fields (e.g., MoE fields only present in MoE variants)\n   - The method must return an instance of the model args class\n\n**Critical**: Verify every field mapping against the HF model's `config.json`. Incorrect\nmappings here cause silent errors downstream.\n\n**Base class contract** (`BaseModelArgs`):\n\n```python\n@dataclass\nclass <Model>ModelArgs(BaseModelArgs):\n    # ... model-specific fields ...\n\n    @classmethod\n    def from_hf_config(\n        cls,\n        hf_config: PretrainedConfig,\n        is_critic: bool = False,\n        **kwargs,\n    ) -> <Model>ModelArgs:\n        # Map HF config fields to Archon model args\n        ...\n```\n\n### Step 4: Implement `model.py`\n\nAdapt the model architecture to match the target model.\n\n**Key components to adapt**:\n\n1. **Normalization** (`RMSNorm` or similar):\n\n   - Check if `elementwise_affine` is configurable\n   - Check the epsilon default value\n   - If the model uses `LayerNorm`, implement accordingly\n\n1. **Attention** module:\n\n   - Q/K/V projection: Check bias presence (`nn.Linear(..., bias=True/False)`)\n   - QK norm: Add `q_norm`/`k_norm` if the model has them, remove if it doesn't\n   - GQA: `n_kv_heads` \\< `n_heads` for grouped-query attention\n   - Ulysses SP: Keep the `set_cp_group` / `_sp_enabled` pattern from the reference\n   - Output projection: Check bias presence\n\n1. **FeedForward** module:\n\n   - SwiGLU: `w2(silu(w1(x)) * w3(x))` -- most common for modern LLMs\n   - Check bias in linear layers\n   - For MoE models: `MoE` module replaces `FeedForward` on designated layers\n\n1. **TransformerBlock**: Pre-norm (most modern LLMs) vs post-norm\n\n   - MoE layer detection via `_is_moe_layer()` if applicable\n\n1. **Top-level Model** (`<Model>Model(BaseArchonModel)`):\n\n   - `tok_embeddings`, `layers` (as `ModuleDict`), `norm`, `output`/`score`\n   - `init_weights()`: Match initialization scheme from HF\n   - `init_buffers()`: RoPE cache + MoE buffers\n   - `forward()`: Must follow `BaseArchonModel` signature:\n     `(tokens, positions, cu_seqlens, max_seqlen, tree_attn_meta=None) -> Tensor`\n\n**Base class contract** (`BaseArchonModel`):\n\n```python\nclass <Model>Model(BaseArchonModel):\n    def forward(self, tokens, positions, cu_seqlens, max_seqlen, tree_attn_meta=None) -> torch.Tensor: ...\n    def init_weights(self) -> None: ...\n    def init_buffers(self, buffer_device) -> None: ...\n```\n\n### Step 5: Implement `rope.py`\n\nHandle the rotary position embedding variant.\n\n**Options**:\n\n1. **Standard RoPE** (same as qwen2/qwen3): Re-export from qwen2:\n\n   ```python\n   from areal.experimental.models.archon.qwen2.model.rope import (\n       apply_rotary_emb,\n       precompute_rope_cache,\n       repeat_kv,\n       reshape_for_broadcast,\n       rotate_half,\n   )\n   ```\n\n1. **Custom RoPE** (YaRN, NTK-aware, etc.): Implement custom `precompute_rope_cache()`\n   and `apply_rotary_emb()` functions. The key difference is usually in how `inv_freq`\n   is computed (scaling factors, interpolation, etc.).\n\n### Step 6: Implement `state_dict_adapter.py`\n\nMap between HuggingFace and Archon weight key names.\n\n**This is the most error-prone step.** The adapter must correctly handle:\n\n1. **Key name mapping** (`from_hf_map` dict):\n\n   - Embedding: `model.embed_tokens.weight` -> `tok_embeddings.weight`\n   - Attention: `model.layers.{}.self_attn.q_proj.weight` ->\n     `layers.{}.attention.wq.weight`\n   - FFN: `model.layers.{}.mlp.gate_proj.weight` -> `layers.{}.feed_forward.w1.weight`\n   - Norms: `model.layers.{}.input_layernorm.weight` ->\n     `layers.{}.attention_norm.weight`\n   - Output: `lm_head.weight` -> `output.weight`\n   - Skip keys (set to `None`): `rotary_emb.inv_freq` (computed at runtime)\n   - Model-specific keys: bias terms, QK norm weights, etc.\n\n1. **Reverse mapping** (`to_hf_map`): Auto-generated from `from_hf_map`\n\n1. **MoE expert weights** (if applicable): 3D\\<->2D conversion for expert weights. Copy\n   the MoE handling from qwen3 if the model has MoE.\n\n1. **Weight tying**: Skip `output.weight` during `to_hf()` if `tie_word_embeddings=True`\n\n**Verification approach**: After implementation, the adapter should satisfy:\n\n```python\n# Roundtrip: archon -> hf -> archon preserves all keys\nhf_sd = adapter.to_hf(archon_sd)\nroundtrip_sd = adapter.from_hf(hf_sd)\nassert set(roundtrip_sd.keys()) == set(archon_sd.keys())\n```\n\n**Base class contract** (`BaseStateDictAdapter`):\n\n```python\nclass <Model>StateDictAdapter(BaseStateDictAdapter):\n    def from_hf(self, hf_state_dict) -> dict[str, Any]: ...\n    def to_hf(self, archon_state_dict) -> dict[str, Any]: ...\n    def convert_single_to_hf(self, name, tensor) -> list[tuple[str, torch.Tensor]]: ...\n```\n\n### Step 7: Implement `parallelize.py`\n\nDefine the parallelization strategy for the model.\n\n**The parallelize function** applies parallelism in this order:\n\n1. TP (Tensor Parallelism) -- shard attention/FFN across devices\n1. EP (Expert Parallelism) -- for MoE models only\n1. CP (Context Parallelism / Ulysses SP) -- sequence parallelism\n1. AC (Activation Checkpointing) -- memory optimization\n1. torch.compile -- compilation optimization\n1. FSDP (Fully Sharded Data Parallelism) -- data parallelism\n\n**Key adaptations by model architecture**:\n\n- **Attention with QK norm**: wq/wk use `use_local_output=False` (DTensor output for\n  norm), add `SequenceParallel(sequence_dim=2)` for q_norm/k_norm\n- **Attention without QK norm**: wq/wk/wv all use `use_local_output=True`\n- **Attention with bias**: Bias terms follow the same parallel plan as their weights\n- **MoE layers**: Separate TP plan for MoE input/output, router gate, and expert\n  weights. Copy from qwen3's `apply_moe_ep_tp()` and `apply_non_moe_tp()`\n- **Dense-only models**: Simpler plan without MoE handling. Copy from qwen2\n\n**Function signature** (must match `ParallelizeFn` protocol):\n\n```python\ndef parallelize_<model>(\n    model: nn.Module,\n    parallel_dims: ArchonParallelDims,\n    param_dtype: torch.dtype = torch.bfloat16,\n    reduce_dtype: torch.dtype = torch.float32,\n    loss_parallel: bool = True,\n    cpu_offload: bool = False,\n    reshard_after_forward_policy: str = \"default\",\n    ac_config: ActivationCheckpointConfig | None = None,\n    enable_compile: bool = True,\n) -> nn.Module:\n```\n\n### Step 8: Create `spec.py` and Register\n\nAssemble the `ModelSpec` and register it.\n\n```python\nfrom areal.experimental.models.archon.model_spec import ModelSpec, register_model_spec\nfrom areal.experimental.models.archon.pipeline_parallel import pipeline_llm\nfrom areal.experimental.models.archon.<model>.infra.parallelize import parallelize_<model>\nfrom areal.experimental.models.archon.<model>.model.args import <Model>ModelArgs\nfrom areal.experimental.models.archon.<model>.model.model import <Model>Model\nfrom areal.experimental.models.archon.<model>.model.state_dict_adapter import (\n    <Model>StateDictAdapter,\n)\n\n<MODEL>_SPEC = ModelSpec(\n    name=\"<Model>\",\n    model_class=<Model>Model,\n    model_args_class=<Model>ModelArgs,\n    state_dict_adapter_class=<Model>StateDictAdapter,\n    parallelize_fn=parallelize_<model>,\n    supported_model_types=frozenset({\"<model_type>\"}),  # From HF config.json\n    pipelining_fn=pipeline_llm,\n)\n\n# Auto-register when module is imported\nregister_model_spec(<MODEL>_SPEC)\n\n__all__ = [\"<MODEL>_SPEC\"]\n```\n\n**Note**: `supported_model_types` should include all HF `model_type` strings that this\nimplementation handles (e.g., `{\"qwen3\", \"qwen3_moe\"}` for Qwen3).\n\n### Step 9: Register in `__init__.py`\n\nAdd the import to `areal/experimental/models/archon/__init__.py`:\n\n```python\nfrom areal.experimental.models.archon.<model> import spec as <model>_spec  # noqa: F401\n```\n\nThis triggers auto-registration when the module is imported.\n\n### Step 10: Verify and Test\n\nVerification should be done in stages, adapting based on available hardware and the test\npatterns in `tests/experimental/archon/`.\n\n**Before writing tests**, examine the existing test files to understand current\npatterns:\n\n```\ntests/experimental/archon/\n  conftest.py             -- Pytest configuration (version checks)\n  utils.py                -- Shared utilities (model loading, comparison)\n  test_qwen3_args.py      -- Args unit tests (CPU-only)\n  test_state_dict_adapter.py  -- State dict roundtrip tests\n  test_weight_sync.py     -- Weight completeness tests (meta device)\n  test_forward.py         -- Forward precision comparison (single GPU)\n  ...\n```\n\n**Test stages** (write tests appropriate for the model's complexity):\n\n#### Stage 1: Args Tests (CPU-only, always write these)\n\nTest `from_hf_config()` with mock HuggingFace configs:\n\n```python\n# Pattern: Create mock PretrainedConfig, verify args mapping\nfrom unittest.mock import MagicMock\n\ndef test_args_from_hf_config():\n    hf_config = MagicMock()\n    hf_config.hidden_size = 4096\n    hf_config.num_hidden_layers = 32\n    # ... set all required fields\n    args = <Model>ModelArgs.from_hf_config(hf_config)\n    assert args.dim == 4096\n    assert args.n_layers == 32\n```\n\n#### Stage 2: State Dict Adapter Tests (CPU-only)\n\nTest key mapping roundtrip:\n\n```python\ndef test_state_dict_roundtrip():\n    # Create adapter with mock config\n    adapter = <Model>StateDictAdapter(mock_config)\n    # Create fake archon state dict with expected keys\n    archon_sd = {\"tok_embeddings.weight\": torch.randn(vocab, dim), ...}\n    # Roundtrip\n    hf_sd = adapter.to_hf(archon_sd)\n    roundtrip = adapter.from_hf(hf_sd)\n    assert set(roundtrip.keys()) == set(archon_sd.keys())\n```\n\n#### Stage 3: Weight Completeness (meta device, CPU-only)\n\nVerify all model parameters have HF mappings:\n\n```python\ndef test_weight_completeness():\n    # Create model on meta device\n    with torch.device(\"meta\"):\n        model = <Model>Model(args)\n    adapter = <Model>StateDictAdapter(hf_config)\n    # Check every archon param has a HF mapping\n    for name, _ in model.named_parameters():\n        hf_pairs = adapter.convert_single_to_hf(name, torch.empty(0))\n        assert len(hf_pairs) > 0, f\"No HF mapping for {name}\"\n```\n\n#### Stage 4: Forward Precision (single GPU, if available)\n\nCompare Archon model output against HuggingFace reference:\n\n```python\n@pytest.mark.skipif(not torch.cuda.is_available(), reason=\"Requires CUDA\")\ndef test_forward_matches_hf():\n    # Load both HF and Archon models\n    # Run forward on same input\n    # Compare logits within tolerance\n```\n\n**Important**: Do NOT hardcode the test categories. Inspect the existing test files in\n`tests/experimental/archon/` and follow the same patterns, fixtures, and markers. Adapt\ntest scope to the model's specific features (e.g., add MoE-specific tests only if the\nmodel has MoE).\n\n## Reference Implementations\n\n| Model | Directory                                 | Features                                                |\n| ----- | ----------------------------------------- | ------------------------------------------------------- |\n| Qwen2 | `areal/experimental/models/archon/qwen2/` | Dense, attention bias, no QK norm                       |\n| Qwen3 | `areal/experimental/models/archon/qwen3/` | Dense + MoE, QK norm, no attention bias, shared experts |\n\n## Architecture Decision Map\n\n| Feature             | qwen2    | qwen3                      | What to check in target model                            |\n| ------------------- | -------- | -------------------------- | -------------------------------------------------------- |\n| Attention bias      | Yes      | No                         | `attention_bias` in HF config                            |\n| QK norm             | No       | Yes                        | `qk_norm` in HF config or QKNorm module in modeling file |\n| MoE                 | No       | Yes                        | `num_experts`/`num_local_experts` in HF config           |\n| Shared experts      | No       | Yes                        | `num_shared_experts` in HF config                        |\n| Decoder sparse step | No       | Yes                        | `decoder_sparse_step` in HF config                       |\n| Weight tying        | Both     | Both                       | `tie_word_embeddings` in HF config                       |\n| RoPE                | Standard | Standard (re-export qwen2) | Check inv_freq formula in HF modeling code               |\n\n## Common Mistakes\n\n- Not mapping all HF keys in `state_dict_adapter.py` (causes silent weight drops)\n- Wrong `from_hf_config()` field mapping (uses wrong HF config attribute name)\n- Forgetting to handle `None` keys in `from_hf_map` (keys to skip like\n  `rotary_emb.inv_freq`)\n- Missing MoE expert weight 3D\\<->2D conversion when model has MoE\n- Wrong TP plan for attention with/without QK norm (`use_local_output` must match)\n- Forgetting to add import line in `areal/experimental/models/archon/__init__.py`\n- Not including all `model_type` variants in `supported_model_types` frozenset\n- Using `print` instead of `areal.utils.logging.getLogger()`\n\n## File Checklist\n\nAfter completion, verify all files exist and are consistent:\n\n- [ ] `areal/experimental/models/archon/<model>/__init__.py`\n- [ ] `areal/experimental/models/archon/<model>/spec.py` -- ModelSpec + register\n- [ ] `areal/experimental/models/archon/<model>/model/args.py` -- ModelArgs +\n  from_hf_config\n- [ ] `areal/experimental/models/archon/<model>/model/model.py` -- Model + Attention +\n  FFN\n- [ ] `areal/experimental/models/archon/<model>/model/rope.py` -- RoPE (or re-export)\n- [ ] `areal/experimental/models/archon/<model>/model/state_dict_adapter.py` -- Key\n  mapping\n- [ ] `areal/experimental/models/archon/<model>/infra/parallelize.py` -- Parallel\n  strategy\n- [ ] `areal/experimental/models/archon/__init__.py` -- Import line added\n- [ ] `tests/experimental/archon/test_<model>_*.py` -- Tests\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nCanonical location: .agents/skills/add-archon-model/SKILL.md\nMirrors: .opencode/skills/add-archon-model/SKILL.md, .claude/skills/add-archon-model/SKILL.md\nInvocation: $add-archon-model (Codex) / /add-archon-model (OpenCode, Claude Code)\n\n## Purpose\n\nSemi-automated guide for adding new model architectures to the Archon training engine.\nUnlike simpler skills (add-reward, add-dataset), this skill actively guides the agent to:\n1. Analyze HuggingFace source code to extract architecture details\n2. Select the closest reference implementation (qwen2 or qwen3)\n3. Generate code skeletons adapted to the target architecture\n4. Create appropriate tests based on existing test patterns\n\n## How to Update\n\n### When New Reference Models Are Added\n1. Add to \"Reference Implementations\" table\n2. Update \"Architecture Decision Map\" with new feature columns\n3. Update Step 2 (reference selection) with new options\n\n### When Base Classes Change\n1. Update contract signatures in Steps 3, 4, 6, 7\n2. Update file checklist if new files are required\n\n### When ModelSpec Changes\n1. Update Step 8 with new ModelSpec fields\n2. Update spec.py template\n\n### When Test Patterns Change\n1. Update Step 10 with new test patterns\n2. Do NOT hardcode test categories -- keep it flexible\n\n### Important Design Decisions\n- This skill is SEMI-AUTOMATED: Claude should read HF source and generate code,\n  not just provide templates for the user to fill in manually\n- The skill references existing test files rather than hardcoding test categories,\n  ensuring it stays current as the test suite evolves\n- Reference model selection (qwen2 vs qwen3) is based on MoE and QK norm presence\n\n================================================================================\n-->\n",".agents/skills/add-dataset/SKILL.md":"---\nname: add-dataset\ndescription: Guide for adding a new dataset loader to AReaL. Use when user wants to add a new dataset.\n---\n\n# Add Dataset\n\nAdd a new dataset loader to AReaL.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a dataset?\"\n- User wants to integrate a new dataset\n- User mentions creating a dataset loader\n\n## Step-by-Step Guide\n\n### Step 1: Create Dataset File\n\nCreate `areal/dataset/<name>.py`:\n\n```python\nfrom datasets import Dataset, load_dataset\n\n\ndef get_<name>_sft_dataset(\n    path: str,\n    split: str,\n    tokenizer,\n    max_length: int | None = None,\n) -> Dataset:\n    \"\"\"Load dataset for SFT training.\n\n    Args:\n        path: Path to dataset (HuggingFace hub or local path)\n        split: Dataset split (train/validation/test)\n        tokenizer: Tokenizer for processing\n        max_length: Maximum sequence length (optional)\n\n    Returns:\n        HuggingFace Dataset with processed samples\n    \"\"\"\n    dataset = load_dataset(path=path, split=split)\n\n    def process(sample):\n        # Tokenize the full sequence (prompt + response)\n        seq_token = tokenizer.encode(\n            sample[\"question\"] + sample[\"answer\"] + tokenizer.eos_token\n        )\n        prompt_token = tokenizer.encode(sample[\"question\"])\n        # Loss mask: 0 for prompt, 1 for response\n        loss_mask = [0] * len(prompt_token) + [1] * (len(seq_token) - len(prompt_token))\n        return {\"input_ids\": seq_token, \"loss_mask\": loss_mask}\n\n    dataset = dataset.map(process).remove_columns([\"question\", \"answer\"])\n\n    if max_length is not None:\n        dataset = dataset.filter(lambda x: len(x[\"input_ids\"]) <= max_length)\n\n    return dataset\n\n\ndef get_<name>_rl_dataset(\n    path: str,\n    split: str,\n    tokenizer,\n    max_length: int | None = None,\n) -> Dataset:\n    \"\"\"Load dataset for RL training.\n\n    Args:\n        path: Path to dataset\n        split: Dataset split\n        tokenizer: Tokenizer for length filtering\n        max_length: Maximum sequence length\n\n    Returns:\n        HuggingFace Dataset with prompts and answers for reward computation\n    \"\"\"\n    dataset = load_dataset(path=path, split=split)\n\n    def process(sample):\n        messages = [\n            {\n                \"role\": \"user\",\n                \"content\": sample[\"question\"],\n            }\n        ]\n        return {\"messages\": messages, \"answer\": sample[\"answer\"]}\n\n    dataset = dataset.map(process).remove_columns([\"question\"])\n\n    if max_length is not None:\n\n        def filter_length(sample):\n            content = sample[\"messages\"][0][\"content\"]\n            tokens = tokenizer.encode(content)\n            return len(tokens) <= max_length\n\n        dataset = dataset.filter(filter_length)\n\n    return dataset\n```\n\n### Step 2: Register in __init__.py\n\nUpdate `areal/dataset/__init__.py`:\n\n```python\n# Add to VALID_DATASETS\nVALID_DATASETS = [\n    # ... existing datasets\n    \"<name>\",\n]\n\n# Add to _get_custom_dataset function\ndef _get_custom_dataset(name: str, ...):\n    # ... existing code\n    elif name == \"<name>\":\n        from areal.dataset.<name> import get_<name>_sft_dataset, get_<name>_rl_dataset\n        if dataset_type == \"sft\":\n            return get_<name>_sft_dataset(path, split, max_length, tokenizer)\n        else:\n            return get_<name>_rl_dataset(path, split, max_length, tokenizer)\n```\n\n### Step 3: Add Config (Optional)\n\nIf the dataset needs special configuration, add to `areal/api/cli_args.py`:\n\n```python\n@dataclass\nclass TrainDatasetConfig:\n    # ... existing fields\n    <name>_specific_field: Optional[str] = None\n```\n\n### Step 4: Add Tests\n\nCreate `tests/test_<name>_dataset.py`:\n\n```python\nimport pytest\nfrom areal.dataset.<name> import get_<name>_sft_dataset, get_<name>_rl_dataset\n\ndef test_sft_dataset_loads(tokenizer):\n    dataset = get_<name>_sft_dataset(\"path/to/data\", split=\"train\", tokenizer=tokenizer)\n    assert len(dataset) > 0\n    assert \"input_ids\" in dataset.column_names\n    assert \"loss_mask\" in dataset.column_names\n\ndef test_rl_dataset_loads(tokenizer):\n    dataset = get_<name>_rl_dataset(\"path/to/data\", split=\"train\", tokenizer=tokenizer)\n    assert len(dataset) > 0\n    assert \"messages\" in dataset.column_names\n    assert \"answer\" in dataset.column_names\n```\n\n## Reference Implementations\n\n| Dataset    | File                               | Description              |\n| ---------- | ---------------------------------- | ------------------------ |\n| GSM8K      | `areal/dataset/gsm8k.py`           | Math word problems       |\n| Geometry3K | `areal/dataset/geometry3k.py`      | Geometry problems        |\n| CLEVR      | `areal/dataset/clevr_count_70k.py` | Visual counting          |\n| HH-RLHF    | `areal/dataset/hhrlhf.py`          | Helpfulness/Harmlessness |\n| TORL       | `areal/dataset/torl_data.py`       | Tool-use RL              |\n\n## Required Fields\n\n### SFT Dataset\n\n```python\n{\n    \"messages\": [\n        {\"role\": \"user\", \"content\": \"...\"},\n        {\"role\": \"assistant\", \"content\": \"...\"},\n    ]\n}\n```\n\n### RL Dataset\n\n```python\n{\n    \"messages\": [\n        {\"role\": \"user\", \"content\": \"...\"},\n    ],\n    \"answer\": \"ground_truth_for_reward\",\n    # Optional metadata for reward function\n}\n```\n\n## Common Mistakes\n\n- Returning `List[Dict]` instead of HuggingFace `Dataset`\n- Using Python loops instead of `dataset.map()`/`filter()`\n- Missing `\"messages\"` field for RL datasets\n- Wrong message format (should be list of dicts with `role` and `content`)\n- Not registering in `__init__.py`\n",".agents/skills/add-reward/SKILL.md":"---\nname: add-reward\ndescription: Guide for adding a new reward function to AReaL. Use when user wants to create a reward function.\n---\n\n# Add Reward\n\nAdd a new reward function to AReaL.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a reward function?\"\n- User wants to implement custom rewards\n- User mentions reward computation\n\n## Step-by-Step Guide\n\n### Step 1: Create Reward File\n\nCreate `areal/reward/<name>.py`:\n\n```python\nfrom typing import Any\n\nfrom areal.utils import logging\n\nlogger = logging.getLogger(\"MyReward\")\n\n\ndef <name>_reward_fn(\n    prompt: str,\n    completions: str,\n    prompt_ids,\n    completion_ids,\n    answer: str | None = None,\n    **kwargs: Any,\n) -> float:\n    \"\"\"Compute reward for a single completion.\n\n    Args:\n        prompt: Prompt string\n        completions: Completion string (model output)\n        prompt_ids: Tokenized prompt IDs\n        completion_ids: Tokenized completion IDs\n        answer: Ground truth answer from dataset (optional)\n        **kwargs: Additional data from dataset\n\n    Returns:\n        Reward value (float), typically 0.0 or 1.0\n    \"\"\"\n    try:\n        # Extract answer from completion\n        extracted = _extract_answer(completions)\n\n        # Compare with ground truth\n        if answer is not None and extracted == str(answer):\n            return 1.0\n        return 0.0\n    except Exception:\n        logger.warning(\"Exception in reward computation\", exc_info=True)\n        return 0.0\n\n\ndef _extract_answer(completion: str) -> str:\n    \"\"\"Extract the answer from a completion string.\n\n    Implement your extraction logic here.\n    \"\"\"\n    # Example: Extract content from \\boxed{}\n    import re\n\n    match = re.search(r\"\\\\boxed\\{([^}]+)\\}\", completion)\n    if match:\n        return match.group(1).strip()\n    return completion.strip()\n```\n\n### Step 2: Register in __init__.py\n\nUpdate `areal/reward/__init__.py`:\n\n```python\n# Add to VALID_REWARD_FN\nVALID_REWARD_FN = [\n    # ... existing reward functions\n    \"<name>\",\n]\n\n# Add to get_reward_fn function\ndef get_reward_fn(name: str, **kwargs):\n    # ... existing code\n    elif name == \"<name>\":\n        from areal.reward.<name> import <name>_reward_fn\n        return <name>_reward_fn\n```\n\n### Step 3: Handle Blocking Operations\n\nIf your reward function uses blocking operations (e.g., API calls, model inference), the\nworkflow will wrap it with `AsyncRewardWrapper`:\n\n```python\n# In your workflow\nfrom areal.reward import AsyncRewardWrapper\n\nself.reward_fn = AsyncRewardWrapper(reward_fn)\n\n# Then call it asynchronously\nrewards = await self.reward_fn(prompt, completions, **data)\n```\n\n### Step 4: Add Tests\n\nCreate `tests/test_<name>_reward.py`:\n\n```python\nimport pytest\nfrom areal.reward.<name> import <name>_reward_fn\n\ndef test_reward_correct_answer():\n    reward = <name>_reward_fn(\n        prompt=\"What is 2+2?\",\n        completions=\"The answer is \\\\boxed{4}\",\n        prompt_ids=None,\n        completion_ids=None,\n        answer=\"4\",\n    )\n    assert reward == 1.0\n\ndef test_reward_wrong_answer():\n    reward = <name>_reward_fn(\n        prompt=\"What is 2+2?\",\n        completions=\"The answer is \\\\boxed{5}\",\n        prompt_ids=None,\n        completion_ids=None,\n        answer=\"4\",\n    )\n    assert reward == 0.0\n```\n\n## Reference Implementations\n\n| Reward     | File                              | Description                  |\n| ---------- | --------------------------------- | ---------------------------- |\n| GSM8K      | `areal/reward/gsm8k.py`           | Math answer verification     |\n| Geometry3K | `areal/reward/geometry3k.py`      | Geometry answer verification |\n| CLEVR      | `areal/reward/clevr_count_70k.py` | Counting verification        |\n| MathVerify | `areal/reward/math_verify.py`     | General math verification    |\n\n## Function Signature\n\nAll reward functions must follow this signature:\n\n```python\ndef reward_fn(\n    prompt: str,               # Input prompt string\n    completions: str,          # Model completion string\n    prompt_ids,                # Tokenized prompt\n    completion_ids,            # Tokenized completion\n    **kwargs: Any,             # Additional data from dataset (e.g., answer)\n) -> float:                    # Reward value (typically 0.0 or 1.0)\n```\n\n**Note**: The reward function is called once per sample. Batching is handled by\n`AsyncRewardWrapper` in the workflow.\n\n## Key Requirements\n\n1. **Deterministic**: Same inputs should produce same outputs\n1. **Return float**: Output is a single float value per sample\n1. **No blocking in async context**: Use `AsyncRewardWrapper` if needed\n1. **Logging**: Use `areal.utils.logging`, not `print`\n1. **Handle exceptions**: Return 0.0 on error, don't raise\n\n## Common Mistakes\n\n- Returning a tensor instead of a float\n- Expecting batched inputs (reward is called per sample)\n- Non-deterministic behavior\n- Blocking operations without `AsyncRewardWrapper`\n- Raising exceptions instead of returning 0.0\n",".agents/skills/add-unit-tests/SKILL.md":"---\nname: add-unit-tests\ndescription: Guide for adding unit tests to AReaL. Use when user wants to add tests for new functionality or increase test coverage.\n---\n\n# Add Unit Tests\n\nAdd unit tests to AReaL following the project's testing conventions.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add tests?\"\n- User wants to increase test coverage\n- User needs to write tests for new functionality\n- User wants to understand AReaL testing patterns\n\n## Step-by-Step Guide\n\n### Step 1: Understand Test Types\n\nAReaL has two main test categories:\n\n| Test Type             | Purpose                            | Location Pattern                   | How It Runs                                |\n| --------------------- | ---------------------------------- | ---------------------------------- | ------------------------------------------ |\n| **Unit Tests**        | Test individual functions/modules  | `tests/test_<module>_<feature>.py` | Directly via pytest                        |\n| **Distributed Tests** | Test distributed/parallel behavior | `tests/torchrun/run_*.py`          | Via torchrun (called by pytest subprocess) |\n\n**Note**: All tests are invoked via pytest. Distributed tests use `torchrun` but are\nstill called from pytest test files.\n\n### Step 2: Create Test File Structure\n\nCreate test file with naming convention: `test_<module>_<feature>.py`\n\n```python\nimport pytest\nimport torch\n\n# Import the module to test\nfrom areal.dataset.gsm8k import get_gsm8k_sft_dataset\nfrom tests.utils import get_dataset_path  # Optional test utilities\n# For mocking tokenizer: from unittest.mock import MagicMock\n```\n\n### Step 3: Write Test Functions\n\nFollow Arrange-Act-Assert pattern:\n\n```python\ndef test_function_under_condition_returns_expected():\n    \"\"\"Test that function returns expected value under condition.\"\"\"\n    # Arrange\n    input_data = 5\n    expected_output = 10\n\n    # Act\n    result = function_under_test(input_data)\n\n    # Assert\n    assert result == expected_output\n```\n\n### Step 4: Add Pytest Markers and CI Strategy\n\nUse appropriate pytest markers:\n\n| Marker                                  | When to Use                                                  |\n| --------------------------------------- | ------------------------------------------------------------ |\n| `@pytest.mark.slow`                     | Test takes > 10 seconds (excluded from CI by default)        |\n| `@pytest.mark.ci`                       | Slow test that must run in CI (use with `@pytest.mark.slow`) |\n| `@pytest.mark.asyncio`                  | Async test functions                                         |\n| `@pytest.mark.skipif(cond, reason=...)` | Conditional skip                                             |\n| `@pytest.mark.parametrize(...)`         | Parameterized tests                                          |\n\n**CI Test Strategy**:\n\n- `@pytest.mark.slow`: Excluded from CI by default (CI runs `pytest -m \"not slow\"`)\n- `@pytest.mark.slow` + `@pytest.mark.ci`: Slow but must run in CI\n- No marker: Runs in CI (fast unit tests)\n\n```python\n@pytest.mark.asyncio\nasync def test_async_function():\n    result = await async_function()\n    assert result == expected\n\n@pytest.mark.skipif(not torch.cuda.is_available(), reason=\"CUDA not available\")\ndef test_gpu_feature():\n    tensor = torch.tensor([1, 2, 3], device=\"cuda\")\n    # ... assertions\n\n@pytest.mark.parametrize(\"batch_size\", [1, 4, 16])\ndef test_with_parameters(batch_size):\n    # Parameterized test\n\n@pytest.mark.slow\ndef test_slow_function():\n    # Excluded from CI by default\n\n@pytest.mark.slow\n@pytest.mark.ci\ndef test_slow_but_required_in_ci():\n    # Slow but must run in CI\n```\n\n### Step 5: Mock Distributed Environment\n\nFor unit tests that need distributed mocks:\n\n```python\nimport torch.distributed as dist\n\ndef test_distributed_function(monkeypatch):\n    monkeypatch.setattr(dist, \"get_rank\", lambda: 0)\n    monkeypatch.setattr(dist, \"get_world_size\", lambda: 2)\n    result = distributed_function()\n    assert result == expected\n```\n\n### Step 6: Handle GPU Dependencies\n\nAlways skip gracefully when GPU unavailable:\n\n```python\nCUDA_AVAILABLE = torch.cuda.is_available()\n\n@pytest.mark.skipif(not CUDA_AVAILABLE, reason=\"CUDA not available\")\ndef test_gpu_function():\n    tensor = torch.tensor([1, 2, 3], device=\"cuda\")\n    # ... assertions\n```\n\n## Key Requirements (Based on testing.md)\n\n### Mocking Distributed\n\n- Use `torch.distributed.fake_pg` for unit tests\n- Mock `dist.get_rank()` and `dist.get_world_size()` explicitly\n- Don't mock internals of FSDP/DTensor\n\n### GPU Test Constraints\n\n- **Always skip gracefully** when GPU unavailable\n- Clean up GPU memory: `torch.cuda.empty_cache()` in fixtures\n- Use smallest possible model/batch for unit tests\n\n### Assertions\n\n- Use `torch.testing.assert_close()` for tensor comparison\n- Specify `rtol`/`atol` explicitly for numerical tests\n- Avoid bare `assert tensor.equal()` - no useful error message\n\n## Reference Implementations\n\n| Test File                        | Description                            | Key Patterns                                      |\n| -------------------------------- | -------------------------------------- | ------------------------------------------------- |\n| `tests/test_utils.py`            | Utility function tests                 | Fixtures, parametrized tests                      |\n| `tests/test_examples.py`         | Integration tests with dataset loading | Dataset path resolution, success pattern matching |\n| `tests/test_fsdp_engine_nccl.py` | Distributed tests                      | Torchrun integration                              |\n\n## Common Mistakes\n\n- **Missing test file registration**: Ensure file follows `test_*.py` naming\n- **GPU dependency without skip**: Always use `@pytest.mark.skipif` for GPU tests\n- **Incorrect tensor comparisons**: Use `torch.testing.assert_close()` not\n  `assert tensor.equal()`\n- **Memory leaks in GPU tests**: Clean up with `torch.cuda.empty_cache()`\n- **Mocking too much**: Don't mock FSDP/DTensor internals\n- **Unclear test names**: Follow `test_<what>_<condition>_<expected>` pattern\n- **No docstrings**: Add descriptive docstrings to test functions\n\n## Integration with Other Skills\n\nThis skill complements other AReaL development skills:\n\n- **After `/add-dataset`**: Add tests for new dataset loaders\n- **After `/add-workflow`**: Add tests for new workflows\n- **After `/add-reward`**: Add tests for new reward functions\n- **With expert agents**: Reference this skill when planning test implementation\n\n## Running Tests\n\n```bash\n# First check GPU availability (many tests require GPU)\npython -c \"import torch; print('GPU available:', torch.cuda.is_available())\"\n\n# Run specific test file\nuv run pytest tests/test_<name>.py\n\n# Skip slow tests (CI default)\nuv run pytest -m \"not slow\"\n\n# Run with verbose output\nuv run pytest -v\n\n# Run distributed tests (requires torchrun and multi-GPU)\n# Note: Usually invoked via pytest test files\ntorchrun --nproc_per_node=2 tests/torchrun/run_<test>.py\n```\n",".agents/skills/add-workflow/SKILL.md":"---\nname: add-workflow\ndescription: Guide for adding a new RolloutWorkflow to AReaL. Use when user wants to create a new workflow.\n---\n\n# Add Workflow\n\nAdd a new RolloutWorkflow implementation to AReaL.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a workflow?\"\n- User wants to create a new RolloutWorkflow\n- User mentions implementing a custom rollout\n\n## Prerequisites\n\nBefore starting, ensure you understand:\n\n- The workflow's purpose and requirements\n- Input/output data format\n- Reward function to use\n\n## Step-by-Step Guide\n\n### Step 1: Create Workflow File\n\nCreate `areal/workflow/<name>.py`:\n\n```python\nimport uuid\nfrom typing import Any, Callable\n\nimport torch\n\nfrom areal.api.cli_args import GenerationHyperparameters\nfrom areal.api.engine_api import InferenceEngine\nfrom areal.api.io_struct import ModelRequest, ModelResponse\nfrom areal.api.reward_api import AsyncRewardWrapper\nfrom areal.api.workflow_api import RolloutWorkflow\nfrom areal.utils import logging\n\nlogger = logging.getLogger(\"MyWorkflow\")\n\n\nclass MyWorkflow(RolloutWorkflow):\n    \"\"\"Description of your workflow.\"\"\"\n\n    def __init__(\n        self,\n        gconfig: GenerationHyperparameters,\n        tokenizer,\n        reward_fn: Callable,\n    ):\n        self.gconfig = gconfig.new_with_stop_and_pad_token_ids(tokenizer)\n        self.tokenizer = tokenizer\n        self.async_reward_fn = AsyncRewardWrapper(reward_fn)\n\n    async def arun_episode(\n        self,\n        engine: InferenceEngine,\n        data: dict[str, Any],\n    ) -> dict[str, Any] | None | dict[str, InteractionWithTokenLogpReward]:\n        \"\"\"Run a single episode. MUST be async and non-blocking.\"\"\"\n\n        # 1. Prepare input_ids from data\n        input_ids = self.tokenizer.apply_chat_template(\n            data[\"messages\"],\n            tokenize=True,\n            add_generation_prompt=True,\n        )\n\n        # 2. Build ModelRequest\n        req = ModelRequest(\n            rid=uuid.uuid4().hex,\n            input_ids=list(input_ids),\n            gconfig=self.gconfig.new(n_samples=1),\n            tokenizer=self.tokenizer,\n        )\n\n        # 3. Generate completion (async)\n        resp: ModelResponse = await engine.agenerate(req)\n\n        # 4. Compute reward (async)\n        prompt_str = self.tokenizer.decode(input_ids)\n        completion_str = self.tokenizer.decode(resp.output_tokens)\n        reward = await self.async_reward_fn(\n            prompt_str,\n            completion_str,\n            resp.input_tokens,\n            resp.output_tokens,\n            **data,\n        )\n\n        # 5. Return results in expected format\n        return {\n            \"input_ids\": torch.tensor(resp.input_tokens),\n            \"output_ids\": torch.tensor(resp.output_tokens),\n            \"reward\": torch.tensor(reward),\n        }\n```\n\n### Step 2: Register in __init__.py\n\nAdd to `areal/workflow/__init__.py`:\n\n```python\nfrom areal.workflow.<name> import MyWorkflow\n\n__all__ = [\n    # ... existing exports\n    \"MyWorkflow\",\n]\n```\n\n### Step 3: Update Entry Script\n\nUpdate your training script to use the new workflow:\n\n```python\ntrainer.train(\n    workflow=\"areal.workflow.<name>.MyWorkflow\",\n    # ... other args\n)\n```\n\n### Step 4: Add Tests\n\nCreate `tests/test_<name>_workflow.py`:\n\n```python\nimport pytest\nfrom areal.workflow.<name> import MyWorkflow\n\n@pytest.mark.asyncio\nasync def test_workflow_basic():\n    # Test basic functionality\n    pass\n```\n\n## Reference Implementations\n\n| Workflow           | File                            | Description                |\n| ------------------ | ------------------------------- | -------------------------- |\n| MultiTurnWorkflow  | `areal/workflow/multi_turn.py`  | Multi-turn conversation    |\n| RLVRWorkflow       | `areal/workflow/rlvr.py`        | RL with verifiable rewards |\n| VisionRLVRWorkflow | `areal/workflow/vision_rlvr.py` | Vision + RLVR              |\n\n## Key Requirements\n\n1. **Async**: `arun_episode` must be `async def` and non-blocking\n1. **No sync I/O**: Use `aiofiles` for file operations\n1. **Wrap rewards**: Use `AsyncRewardWrapper` for reward functions\n1. **Tensor format**: Output tensors should be `[batch, seq_len, ...]`\n1. **Use helpers**: `concat_padded_tensors` for combining outputs\n\n## Common Mistakes\n\n- Using `open()` instead of `aiofiles.open()`\n- Forgetting to `await` async calls\n- Not wrapping reward function with `AsyncRewardWrapper`\n- Wrong tensor shape conventions\n",".agents/skills/commit-conventions/SKILL.md":"---\nname: commit-conventions\ndescription: AReaL commit message conventions. MUST load on every git commit -- provides Conventional Commits format with scope inference from file paths.\n---\n\n# Commit Conventions\n\nCommit message conventions and scope inference rules for the AReaL repository.\n\n## When to Use\n\n**ALWAYS load this skill when making any git commit in AReaL.** This includes:\n\n- Direct commits (`git commit`)\n- Commits during PR creation (`$create-pr` / `/create-pr`)\n- Commits delegated to sub-agents with this skill loaded\n- Any agent workflow that produces a commit\n\n## Commit Message Format\n\n```\n<type>(<scope>): <subject>\n\n<body>\n\n[Optional sections:]\nKey changes:\n- change 1\n- change 2\n\nRefs: #123, #456\n```\n\n## Type Selection\n\n| Type       | When to Use                      |\n| ---------- | -------------------------------- |\n| `feat`     | New feature or capability        |\n| `fix`      | Bug fix                          |\n| `docs`     | Documentation only               |\n| `gov`      | Governance or maintainer changes |\n| `style`    | Formatting/style-only changes    |\n| `refactor` | Code change without feature/fix  |\n| `perf`     | Performance improvement          |\n| `test`     | Adding or fixing tests           |\n| `build`    | Build system or dependencies     |\n| `ci`       | CI pipeline or workflow changes  |\n| `chore`    | Build, deps, config changes      |\n| `revert`   | Revert a previous commit         |\n\n## Scope Inference\n\nInfer scope from the **primary** changed file paths:\n\n| File Path Pattern                                            | Scope                          |\n| ------------------------------------------------------------ | ------------------------------ |\n| `areal/workflow/`                                            | `workflow`                     |\n| `areal/engine/`                                              | `engine`                       |\n| `areal/reward/`                                              | `reward`                       |\n| `areal/dataset/`                                             | `dataset`                      |\n| `areal/api/`                                                 | `api`                          |\n| `areal/utils/`                                               | `utils`                        |\n| `areal/infra/`                                               | `infra`                        |\n| `areal/trainer/`                                             | `trainer`                      |\n| `areal/models/`                                              | `models`                       |\n| `areal/experimental/`                                        | `experimental`                 |\n| `docs/`                                                      | `docs`                         |\n| `examples/`                                                  | `examples`                     |\n| `AGENTS.md`, `.agents/`, `.claude/`, `.codex/`, `.opencode/` | `agents`                       |\n| Multiple areas                                               | Omit scope or use broader term |\n\n## Rules\n\n- **Subject**: imperative mood, ~50-72 chars, no trailing period\n- **Body**: explain \"why\" not \"what\", wrap at 72 chars\n- **Key changes**: bullet list of main modifications (for complex commits with 3+ files)\n- **Refs**: reference issues/PRs if applicable\n\n## Examples\n\n**Single file fix:**\n\n```\nfix(reward): handle empty completion in gsm8k\n\nReturn 0 reward instead of raising exception when\ncompletion string is empty after extraction.\n```\n\n**Multi-file feature:**\n\n```\nfeat(engine): add CPU offload support to ArchonEngine\n\nEnable torch_memory_saver for model offloading during\nrollout phase to reduce GPU memory pressure.\n\nKey changes:\n- Add offload/onload methods to ArchonEngine\n- Integrate with weight update flow\n- Handle ROCm compatibility\n```\n\n**Docs only:**\n\n```\ndocs: update algorithm comparison table\n\nAdd SAPO and GSPO to the algorithm family documentation\nwith configuration examples.\n```\n\n**Agent/tooling changes:**\n\n```\nchore(agents): port review-pr command to OpenCode\n\nAdd OpenCode-native commands with task() category\ndelegation instead of hardcoded model names.\n\nKey changes:\n- Create .opencode/command/ with review-pr, create-pr\n- Replace hardcoded model routing with platform-native review routing\n- Add expert subagent consultation patterns\n```\n\n**Governance/maintainer changes:**\n\n```\ngov(agents): add maintainer ownership for service modules\n\nUpdate CODEOWNERS and maintainer references to reflect\ncurrent governance responsibilities.\n\nKey changes:\n- Add maintainers for agent_service and infra ownership\n- Align governance docs with updated reviewer responsibilities\n```\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nCanonical location: .agents/skills/commit-conventions/SKILL.md\nMirrors: .opencode/skills/commit-conventions/SKILL.md, .claude/skills/commit-conventions/SKILL.md\nInvocation: Automatically loaded on every git commit (all platforms)\n\n## Purpose\n\nProvides Conventional Commits format with AReaL-specific scope inference\nfrom file paths. Unlike other skills, this one is NOT user-triggered --\nit is loaded by the system/agent on every commit operation.\n\n## How to Update\n\n### When New Modules Are Added\n1. Add the file path pattern and scope to the \"Scope Inference\" table\n2. Keep table sorted by areal/ subpackages first, then top-level dirs\n\n### When Commit Types Change\n1. Update the \"Type Selection\" table\n2. Add/update examples to illustrate the new type\n\n### When Adding Examples\n1. Each example should demonstrate a distinct commit pattern\n2. Keep examples realistic -- use actual AReaL module names\n3. Show both subject-only and subject+body+key-changes variants\n\n### Important Design Decisions\n- This skill is ALWAYS loaded (not optional) -- keep it concise to\n  minimize token overhead on every commit\n- Scope inference is path-based, not content-based -- simpler and\n  more deterministic\n- \"Multiple areas\" -> omit scope rather than invent a new one\n\n================================================================================\n-->\n",".agents/skills/create-pr/SKILL.md":"---\nname: create-pr\ndescription: Rebase the current branch onto the latest base branch, squash local commits, generate a Conventional Commit message, and create or update the GitHub pull request.\n---\n\n# Create Pull Request\n\nUse this skill when the user asks to create or update a PR for the current branch.\n\n## Inputs\n\n- Optional `--draft`\n- Optional `--base <branch>` (default: `main`)\n\n## Preconditions\n\n1. Verify the current branch is not `main` or `master`.\n1. Check for uncommitted changes with `git status --short`.\n1. Ensure `gh` is available.\n1. If there are uncommitted changes, stop and ask the user whether to commit or stash\n   them first.\n\n## Workflow\n\n### Step 1: Check for an existing PR\n\n- Run `gh pr view --json number,title,url,state,isDraft`.\n- If a PR already exists, tell the user before rewriting history or force-pushing.\n\n### Step 2: Fetch and rebase\n\n```bash\ngit fetch origin <base>\ngit rebase origin/<base>\n```\n\n- If rebase conflicts occur, abort the rebase and stop.\n- Tell the user which files conflicted and ask them to resolve manually.\n\n### Step 3: Squash into one commit\n\n```bash\ngit reset --soft origin/<base>\n```\n\n- Load the `commit-conventions` skill before generating the commit message.\n- Infer `type` and `scope` from the staged diff.\n- Keep the commit subject imperative and under about 72 characters.\n\n### Step 4: Generate PR title and body\n\n- Use the squashed commit message style for the PR title.\n- Follow the repository PR template at `.github/PULL_REQUEST_TEMPLATE.md`.\n- Summarize user-facing changes, risk areas, test commands run, and skipped suites with\n  reasons.\n\n### Step 5: Push and create or update the PR\n\n- Push the branch.\n- If history was rewritten, confirm before force-pushing.\n- Create or update the PR with `gh pr create` or `gh pr edit`.\n- Respect `--draft` when requested.\n\n## Guardrails\n\n- Never create a PR from `main` or `master`.\n- Never silently force-push over an existing PR branch.\n- Never bypass `commit-conventions` for the squashed commit.\n- If `gh` authentication or remote permissions fail, stop and report the exact blocker.\n\n## Output\n\nReport:\n\n- Base branch used\n- Final commit message\n- PR title\n- PR URL, if creation or update succeeded\n- Any steps that were skipped or require user follow-up\n",".agents/skills/debug-distributed/SKILL.md":"---\nname: debug-distributed\ndescription: Guide for debugging distributed training issues in AReaL. Use when user encounters hangs, wrong results, OOM, or communication errors.\n---\n\n# Debug Distributed Training\n\nDebugging guide for distributed training issues in AReaL (FSDP2, TP, CP, EP).\n\n## When to Use\n\nThis skill is triggered when:\n\n- Training hangs or deadlocks\n- Results differ across ranks or are numerically wrong\n- OOM errors in distributed settings\n- NCCL/communication errors or device mesh issues\n\n## Debugging Principles\n\n### Minimal Reproduction\n\n**Always follow the minimal demo principle**: Reproduce with the least amount of code to\nnarrow down the issue faster.\n\n```python\n# Bad: Debug in full training loop\n# Good: Create minimal script\nimport torch\nimport torch.distributed as dist\n\ndist.init_process_group(\"nccl\")\nrank = dist.get_rank()\n\n# Reproduce the exact operation that fails\ntensor = torch.ones(10).cuda()\ndist.all_reduce(tensor)  # <-- Isolate the failing op\nprint(f\"Rank {rank}: {tensor}\")\n```\n\n**Reduction strategy:**\n\n1. Remove unrelated model components\n1. Use small tensor sizes\n1. Reduce world_size to minimum (e.g., 2 GPUs)\n1. Remove torch.compile if possible\n1. Disable activation checkpointing\n\n## Step-by-Step Debugging Guide\n\n### 1. Hang Debugging (Deadlocks, Synchronization)\n\n**Environment Variables for Debugging**:\n\n```bash\n# Full debug logging\nexport TORCH_DISTRIBUTED_DEBUG=DETAIL\nexport NCCL_DEBUG=INFO\nexport NCCL_DEBUG_SUBSYS=ALL\n\n# torch.compile debugging\nexport TORCH_LOGS=\"+dynamo,recompiles\"\nexport TORCHDYNAMO_VERBOSE=1\n```\n\n**Dump Call Stack with py-spy** (for hung processes):\n\n```bash\n# Find process IDs\nps aux | grep python\n\n# Dump call stack of specific rank\npy-spy dump --pid <PID>\n\n# Record flame graph for performance analysis\npy-spy record -o profile.svg --pid <PID> --duration 30\n```\n\n**Common Causes**:\n\n1. **Mismatched Collectives**: One rank calls `all_reduce`, another doesn't.\n1. **Wrong Process Group**: Using wrong group for collective.\n1. **Tensor Shape Mismatch**: Different shapes across ranks.\n\n**Debug Steps**:\n\n```python\n# Verify group membership\nmesh = parallel_dims.get_mesh(\"dp_shard_cp\")\ngroup = mesh.get_group()\nprint(f\"Rank {dist.get_rank()}: group size = {dist.get_world_size(group)}\")\n\n# Print shapes on all ranks\nprint(f\"Rank {dist.get_rank()}: tensor.shape = {tensor.shape}\")\ndist.barrier()\n```\n\n**Timeout Adjustment** (for debugging only):\n\n```python\nfrom areal.engine.core.distributed import patch_dist_group_timeout\nfrom datetime import timedelta\npatch_dist_group_timeout(timedelta(minutes=30))\n```\n\n### 2. Wrong Results (Gradient, Reduction Issues)\n\n**Check DTensor Placements**:\n\n```python\nfrom torch.distributed.tensor import DTensor\nif isinstance(param, DTensor):\n    print(f\"Param {name}: placements={param.placements}, mesh={param.device_mesh}\")\n```\n\n**Verify Gradient Reduction**:\n\n```python\nfor name, param in model.named_parameters():\n    if param.grad is not None:\n        print(f\"Rank {dist.get_rank()}: {name} grad_sum = {param.grad.sum().item()}\")\n```\n\n### 3. OOM Issues (Memory, Sharding)\n\n**Check Memory Usage**:\n\n```python\nprint(f\"Rank {dist.get_rank()}: \"\n      f\"allocated={torch.cuda.memory_allocated()/1e9:.2f}GB, \"\n      f\"reserved={torch.cuda.memory_reserved()/1e9:.2f}GB\")\n```\n\n**Check FSDP Coverage**:\n\n```python\nfor name, param in model.named_parameters():\n    is_dtensor = isinstance(param, DTensor)\n    print(f\"{name}: is_dtensor={is_dtensor}, shape={param.shape}\")\n```\n\n### 4. Communication Errors\n\n| Error                     | Cause                | Solution                           |\n| ------------------------- | -------------------- | ---------------------------------- |\n| `NCCL WARN Cuda failure`  | GPU communication    | Check NCCL version, GPU topology   |\n| `RuntimeError: Timed out` | Rank synchronization | Increase timeout, check code paths |\n| `Invalid device mesh`     | Mesh configuration   | Verify world_size = dp * tp * cp   |\n\n## Debugging Tools\n\n### Environment Variables Reference\n\n| Variable                          | Purpose                                |\n| --------------------------------- | -------------------------------------- |\n| `TORCH_DISTRIBUTED_DEBUG=DETAIL`  | Detailed distributed logging           |\n| `NCCL_DEBUG=INFO`                 | NCCL communication logging             |\n| `NCCL_DEBUG_SUBSYS=ALL`           | All NCCL subsystems                    |\n| `TORCH_LOGS=\"+dynamo,recompiles\"` | torch.compile logging                  |\n| `TORCHDYNAMO_VERBOSE=1`           | Dynamo verbose output                  |\n| `CUDA_LAUNCH_BLOCKING=1`          | Synchronous CUDA (slow, for debugging) |\n\n### py-spy for Call Stack Analysis\n\n```bash\n# Install\npip install py-spy\n\n# Dump call stack of hung process\npy-spy dump --pid <PID>\n\n# Dump all Python processes\npgrep -f python | xargs -I {} py-spy dump --pid {}\n\n# Record flame graph\npy-spy record -o profile.svg --pid <PID> --duration 30\n```\n\n### Rank-Conditional Printing\n\n```python\ndef print_all_ranks(msg):\n    for r in range(dist.get_world_size()):\n        if dist.get_rank() == r:\n            print(f\"[Rank {r}] {msg}\")\n        dist.barrier()\n```\n\n### Check Device Mesh\n\n```python\ndef debug_mesh(parallel_dims):\n    mesh = parallel_dims.world_mesh\n    for dim_name in mesh.mesh_dim_names:\n        submesh = parallel_dims.get_mesh(dim_name)\n        if submesh:\n            print(f\"Rank {dist.get_rank()}: {dim_name} size={submesh.size()}\")\n```\n\n### Validate Tensor Consistency\n\n```python\ndef check_tensor_consistency(tensor, name, group=None):\n    local_sum = tensor.sum().item()\n    tensor_sums = [None] * dist.get_world_size(group)\n    dist.all_gather_object(tensor_sums, local_sum, group=group)\n    if dist.get_rank() == 0 and len(set(tensor_sums)) > 1:\n        print(f\"WARNING: {name} inconsistent: {tensor_sums}\")\n```\n\n## Key Files Reference\n\n| Component       | File                                                          |\n| --------------- | ------------------------------------------------------------- |\n| Parallel Dims   | `areal/experimental/models/archon/parallel_dims.py`           |\n| Expert Parallel | `areal/experimental/models/archon/expert_parallel.py`         |\n| Ulysses (CP)    | `areal/experimental/models/archon/ulysses.py`                 |\n| FSDP/TP Apply   | `areal/experimental/models/archon/qwen2/infra/parallelize.py` |\n",".agents/skills/review-pr/MIGRATION.md":"# `/review-pr` Refactor & Update Plan (Codex / OpenCode / Claude)\n\nLast updated: 2026-03-31 Owner: AI workflow maintainers Scope:\n`.agents/skills/review-pr/*`, `.opencode/{command,data}/review-pr*`,\n`.claude/{commands,data}/review-pr*`\n\n______________________________________________________________________\n\n## 1. Goals and Non-Goals\n\n### Goals\n\n1. Rebuild `/review-pr` taxonomy and templates for better coverage of new AReaL modules.\n1. Keep template count manageable while preserving depth for high-risk PRs.\n1. Eliminate cross-platform drift through a single semantic source of truth.\n1. Reduce false positives by prioritizing path-based triggers.\n\n### Non-Goals\n\n1. Changing `/review-pr` into a file-mutating workflow (must remain read-only).\n1. Adding one-off niche templates for every micro-feature.\n1. Rewriting all platform command orchestration logic in one iteration.\n\n______________________________________________________________________\n\n## 2. Current State Summary (Problem Statement)\n\n### What works\n\n1. Existing workflow phases are clear (context -> analysis -> planning -> delegated\n   review -> summary).\n1. Platform-specific command wrappers already exist and are functional.\n1. Risk-first review behavior (CRITICAL/HIGH/MEDIUM/LOW) is already enforced.\n\n### What is broken or stale\n\n1. Domain/signal matrix lags behind repository evolution (`tree_attn`, `vllm_ext`,\n   service stack, `archon_weight_sync`).\n1. Taxonomy is overly fragmented in some areas and missing in others.\n1. Three-platform data files are near-duplicates and easy to drift.\n1. Trigger rules rely heavily on generic keywords in places where path triggers are\n   safer.\n\n______________________________________________________________________\n\n## 3. Target Information Architecture\n\nUse a **two-layer taxonomy**:\n\n- **L1 Domain (12 domains)**: template-level planning unit.\n- **L2 Signals (2-9 per domain)**: detection and checklist specialization unit.\n\nThis avoids both extremes: too many tiny templates or too coarse single-pass review.\n\n### 3.1 L1 Domains and L2 Signals\n\n1. **Distributed Runtime**\n   - L2: `archon_core`, `archon_parallel`, `process_group`, `fsdp_core`,\n     `megatron_core`, `collectives`, `mesh_dtensor`, `activation_checkpointing`,\n     `weight_sync`\n1. **Model Compute & Attention**\n   - L2: `tree_attn`, `sdpa_varlen`, `sp_cp_attention_mask`, `triton_kernel`,\n     `archon_model_family`, `archon_attention_stack`, `archon_moe_modeling`\n1. **Inference Backend & Serving**\n   - L2: `vllm_ext`, `remote_inference_backend`, `request_lifecycle`\n1. **Service Orchestration**\n   - L2: `service_routing_dataflow`, `session_consistency`\n1. **Workflow & Trainer Contract**\n   - L2: `workflow_engine_boundary`, `dataset_surface`, `async_contract`,\n     `weight_version_contract`\n1. **API & Config Compatibility**\n   - L2: `dataclass_schema`, `cli_compat`, `backward_compat`,\n     `project_dependency_config`\n1. **Numerics & Tensor Semantics**\n   - L2: `shape_dtype`, `numerical_stability`, `reward_surface`, `compile_dynamo`,\n     `mixed_precision_fp8`\n1. **Checkpoint & Recovery**\n   - L2: `dcp_consistency`, `optimizer_state`, `resume_compat`\n1. **Launcher & Infrastructure**\n   - L2: `launcher_resource_match`, `scheduler_contract`, `rpc_transport`,\n     `runtime_image_config`\n1. **Low-Risk Hygiene**\n   - L2: `tests_docs_config`, `logging_import_security`, `project_docs_metadata`\n1. **Harness & Agent Infrastructure**\n   - L2: `skill_definition`, `platform_command_data`, `agent_registry_config`\n1. **CI/CD & Release Automation**\n   - L2: `github_workflow_jobs`, `runner_provisioning`, `release_delivery`\n\n______________________________________________________________________\n\n## 4. Detection Strategy (Path-first)\n\n### 4.1 Trigger priority\n\n1. **Path trigger (primary)**\n1. **Keyword trigger (secondary, only to refine existing path hits)**\n1. **Linkage trigger (auto-add dependent checks)**\n\n### 4.2 Canonical path mapping (v1)\n\n1. `areal/models/tree_attn/**` -> Model Compute & Attention (HIGH/MEDIUM by touched\n   files)\n1. `areal/engine/vllm_ext/**` -> Inference Backend & Serving (HIGH)\n1. `areal/experimental/models/archon/**` + `areal/experimental/engine/archon_engine.py`\n   \\+ `areal/experimental/engine/archon_checkpoint.py` -> Distributed Runtime + Model\n   Compute & Attention (CRITICAL/HIGH)\n1. `areal/v2/agent_service/**` -> Service Orchestration (HIGH)\n1. `areal/v2/inference_service/**` -> Service Orchestration (HIGH)\n1. `areal/experimental/engine/archon_weight_sync.py` -> Distributed Runtime (CRITICAL)\n1. `areal/infra/rpc/**` -> Launcher & Infrastructure + Distributed Runtime (HIGH)\n1. `areal/workflow/**`, `areal/trainer/**` -> Workflow & Trainer Contract (HIGH/MEDIUM)\n1. `areal/api/**` -> API & Config Compatibility (MEDIUM)\n1. `areal/utils/{saver.py,recover.py,async_checkpoint.py}` + `*checkpoint*.py` ->\n   Checkpoint & Recovery (CRITICAL/HIGH)\n1. `areal/experimental/models/archon/activation_checkpoint.py` -> Distributed Runtime\n   (MEDIUM/HIGH)\n1. `areal/experimental/models/archon/compile.py` -> Numerics & Tensor Semantics +\n   Distributed Runtime (MEDIUM)\n1. `pyproject.toml`, `uv.lock` -> API & Config Compatibility (MEDIUM)\n1. `Dockerfile`, `.dockerignore` -> Launcher & Infrastructure (HIGH)\n1. `.agents/**`, `.claude/**`, `.opencode/**`, `.codex/**`, `AGENTS.md`, `CLAUDE.md` ->\n   Harness & Agent Infrastructure (MEDIUM/HIGH)\n1. `.github/workflows/**` -> CI/CD & Release Automation (HIGH/CRITICAL)\n1. `docs/build_all.sh`, `docs/generate_cli_docs.py`, `.github/PULL_REQUEST_TEMPLATE.md`,\n   `README.md`, `CONTRIBUTING.md` -> Low-Risk Hygiene (LOW/MEDIUM)\n\n### 4.3 Must-not-regress coverage (from current review-pr)\n\nThe migration must preserve the existing high-risk framework coverage already present\ntoday:\n\n1. `areal/experimental/models/archon/**` + `areal/experimental/engine/archon_engine.py`\n   - `areal/experimental/engine/archon_checkpoint.py` -> Distributed Runtime + Model\n     Compute & Attention\n1. `areal/engine/fsdp_utils/**` + `areal/engine/fsdp_engine.py` -> Distributed Runtime\n1. `areal/engine/megatron_engine.py` + `areal/engine/megatron_utils/**` -> Distributed\n   Runtime\n1. `areal/trainer/**` -> Workflow & Trainer Contract\n1. `areal/reward/**` -> Numerics & Tensor Semantics + Workflow & Trainer Contract\n1. `areal/dataset/**` -> Workflow & Trainer Contract + API & Config Compatibility\n\n### 4.4 Noise control rules\n\n1. No repo-wide standalone triggering for `current_platform`, `RTensor`, or `fp8`.\n1. These keywords only refine severity/checklists after domain has been path-selected.\n1. Cap task fan-out: max one primary template per domain, plus one general logic pass.\n\n______________________________________________________________________\n\n## 5. Template Strategy\n\nMaintain **12 domain templates** (one per L1), each with signal-specific checklists,\nplus **1 universal logic pass**.\n\n### Template inventory (v1)\n\n1. Distributed Runtime Review\n1. Model Compute & Attention Review\n1. Inference Backend & Serving Review\n1. Service Orchestration Review\n1. Workflow & Trainer Contract Review\n1. API & Config Compatibility Review\n1. Numerics & Tensor Semantics Review\n1. Checkpoint & Recovery Review\n1. Launcher & Infrastructure Review\n1. Low-Risk Hygiene Review\n1. Harness & Agent Infrastructure Review\n1. CI/CD & Release Automation Review\n\n### Mandatory universal pass\n\n- Always run one lightweight **General Logic & Boundary** pass for non-doc PRs.\n\n______________________________________________________________________\n\n## 6. Severity Mapping\n\n1. **CRITICAL**: distributed invariants, checkpoint correctness, core weight sync\n   safety.\n1. **HIGH**: service orchestration, inference backend runtime, trainer/workflow\n   contracts.\n1. **MEDIUM**: API compatibility, numerics/tensor semantics in bounded scope.\n1. **LOW**: docs/tests/config-only hygiene.\n\nRule: domain default severity can be escalated by L2 signal combinations (e.g.,\n`mesh_dtensor` + `weight_sync`).\n\n______________________________________________________________________\n\n## 7. Cross-Domain Linkage Rules (Auto-appended checks)\n\n1. `tree_attn` -> also append Numerics & Tensor Semantics checks.\n1. `archon_core` or `archon_parallel` -> also append Model Compute & Attention checks.\n1. `archon_model_family` or `archon_moe_modeling` -> also append Numerics & Tensor\n   Semantics checks.\n1. `reward_surface` -> also append Workflow & Trainer Contract checks.\n1. `compile_dynamo` -> also append Distributed Runtime checks.\n1. `vllm_ext` -> also append Launcher & Infrastructure checks.\n1. Service Orchestration changes -> also append Workflow & Trainer async-contract\n   checks.\n1. `archon_weight_sync` -> also append DTensor + process-group + checkpoint interaction\n   checks.\n1. RPC transport changes -> also append Distributed Runtime synchronization checks.\n1. `mixed_precision_fp8` + Distributed Runtime -> also append mesh + weight-sync\n   compatibility checks.\n1. `runtime_image_config` -> also append Inference Backend & Serving checks.\n1. `project_dependency_config` -> also append API & Config Compatibility checks.\n1. `github_workflow_jobs` or `release_delivery` -> also append Launcher & Infrastructure\n   checks.\n1. `skill_definition` or `platform_command_data` -> also append Low-Risk Hygiene checks.\n\n______________________________________________________________________\n\n## 8. Three-Platform Synchronization Model\n\n## 8.1 Source-of-truth\n\nSemantic content is authored only in:\n\n1. `.agents/skills/review-pr/references/review-pr-domains-and-signals.md`\n1. `.agents/skills/review-pr/references/review-pr-templates.md`\n\nCanonical semantic scope includes:\n\n1. taxonomy (L1/L2), path/linkage rules, and severity rules\n1. checklist bodies and change-analysis vocabulary\n\nWrapper-specific scope (non-canonical) includes:\n\n1. command syntax and frontmatter fields\n1. shell snippets, import/include syntax, and runtime-specific routing policies\n1. OpenCode-only and Claude-only execution options\n\n## 8.2 Derived outputs\n\nGenerated/derived files:\n\n1. `.opencode/data/review-pr-domains-and-signals.md`\n1. `.opencode/data/review-pr-templates.md`\n1. `.claude/data/review-pr-domains-and-signals.md`\n1. `.claude/data/review-pr-templates.md`\n\n## 8.3 Mechanical sync (definition)\n\n\"Mechanical sync\" means deterministic conversion, not manual copy/paste:\n\n1. Read canonical `.agents` files.\n1. Emit OpenCode and Claude data copies with the same generic review-depth vocabulary\n   (`comprehensive`, `targeted`, `basic`).\n1. Keep all runtime routing and platform execution choices in the wrapper command files\n   only.\n1. Preserve section order and checklist content exactly.\n\n______________________________________________________________________\n\n## 9. Implementation Plan\n\n### Phase A (Foundation)\n\n1. Replace the old review-pr taxonomy with L1/L2 domain/signal references in canonical\n   `.agents` references.\n1. Rebuild template file into 12 domain templates + universal logic pass.\n1. Add linkage rules and fan-out caps.\n\n### Phase B (Platform sync)\n\n1. Add sync script at `.agents/skills/review-pr/sync_review_pr_refs.py`.\n1. Regenerate `.opencode/data/*` and `.claude/data/*`.\n1. Add CI check: fail if derived files differ from regeneration output.\n\n### Phase C (Command layer alignment)\n\n1. Update `.opencode/command/review-pr.md` wording to reflect new domains/signals.\n1. Update `.claude/commands/review-pr.md` wording similarly.\n1. Keep orchestration differences platform-specific (task categories vs model routing).\n\n### Phase D (Validation)\n\n1. Run **classification lane** with `/review-pr --quick` against representative PRs\n   from:\n   - `tree_attn`\n   - `vllm_ext`\n   - `agent_service/inference_service`\n   - `archon_weight_sync`\n1. Measure in classification lane:\n   - expected detected domains/signals\n   - expected severity\n   - false positive rate\n   - missing high-risk findings\n1. Run **delegation lane** with full `/review-pr` (non-quick) on the same fixtures.\n1. Measure in delegation lane:\n   - false positive rate\n   - missing high-risk findings\n   - number of spawned review tasks per PR\n\n______________________________________________________________________\n\n## 10. Plan Review (Critical self-review)\n\n### Strengths\n\n1. Improves coverage for newly introduced high-risk modules.\n1. Shrinks maintenance overhead by moving to domain templates.\n1. Prevents cross-platform drift via deterministic derivation.\n\n### Risks\n\n1. Over-broad domains can dilute checklist quality.\n1. First migration may temporarily shift severity distributions.\n1. Command docs can lag behind taxonomy if sync discipline is not enforced.\n\n### Mitigations\n\n1. Keep L2 signals explicit and path-anchored.\n1. Use golden PR cases for pre/post comparison.\n1. Add CI consistency gate for derived platform data files.\n1. Require wrapper wording updates in the same migration PR as taxonomy changes.\n\n______________________________________________________________________\n\n## 11. Acceptance Criteria\n\n1. Canonical references express all 12 domains + L2 signals and linkage rules.\n1. Derived OpenCode/Claude data files regenerate with zero manual edits.\n1. No mixed old/new label vocabulary remains after regeneration.\n1. Representative PRs trigger expected domains:\n   - `tree_attn` PR -> Model Compute & Attention (+ Numerics linkage)\n   - `vllm_ext` PR -> Inference Backend & Serving (+ Launcher linkage)\n   - service-stack PR -> Service Orchestration (+ Workflow linkage)\n   - `archon_weight_sync` PR -> Distributed Runtime (CRITICAL)\n1. Legacy high-risk coverage remains intact (Archon/FSDP/Megatron/Reward/Dataset).\n1. Task fan-out remains bounded (no uncontrolled template explosion).\n\n______________________________________________________________________\n\n## 12. Out of Scope for v1 (defer)\n\n1. Fully unifying command orchestration syntax across OpenCode and Claude.\n1. Creating standalone domain types for every backend keyword.\n1. Automatically posting review comments to GitHub.\n\n______________________________________________________________________\n\n## 13. Recommended Next Action\n\nLand **Phase A + B + C** in one migration PR (taxonomy + sync tooling + wrapper\nalignment), then run **Phase D** validation immediately using fixed fixtures.\n",".agents/skills/review-pr/SKILL.md":"---\nname: review-pr\ndescription: Read-only pull request review workflow with risk analysis, targeted checklists, and Codex subagent consultation.\n---\n\n# Review Pull Request\n\nUse this skill when the user asks for a PR review of the current branch or a specific\nPR.\n\n## Inputs\n\n- Optional PR number\n- Optional `--quick` to stop after the change analysis phase\n\n## Hard Rules\n\n- Stay read-only.\n- Do not edit files, commit, push, rebase, or change GitHub state.\n- Do not run build, install, or test commands that mutate the environment.\n- Use `gh` for PR metadata and git diff retrieval.\n\n## Reference Files\n\n- `references/review-pr-domains-and-signals.md`\n- `references/review-pr-templates.md`\n\n## Workflow\n\n### Phase 1: Resolve PR context\n\n1. Use `gh pr view` to fetch PR title, body, state, draft status, and changed files.\n1. If no PR exists, stop and report that clearly.\n1. If the PR is closed, stop.\n1. Record the branch name and changed file list.\n\n### Phase 2: Change analysis\n\n1. Classify changed files using `references/review-pr-domains-and-signals.md`.\n1. Determine the highest overall risk level: `CRITICAL`, `HIGH`, `MEDIUM`, or `LOW`.\n1. Build a `CHANGE_ANALYSIS_REPORT` that lists:\n   - detected domains/signals\n   - risk level\n   - affected files\n   - related frameworks\n   - likely failure modes\n\nIf `--quick` is set, return the change analysis report and stop here.\n\n### Phase 3: Review planning\n\n1. Select the smallest useful set of review passes from\n   `references/review-pr-templates.md`.\n1. Split by risk area, not by file count.\n1. Always include at least one general logic pass.\n\n### Phase 4: Expert consultation\n\nConsult the matching Codex subagents registered in `.codex/config.toml` when relevant:\n\n- `archon-expert`\n- `fsdp-expert`\n- `megatron-expert`\n- `algorithm-expert`\n- `launcher-expert`\n\nIf the Codex runtime supports parallel subagent execution, run independent review passes\nin parallel. Otherwise, execute them serially.\n\n### Phase 5: Final review\n\nProduce findings first, ordered by severity:\n\n1. `CRITICAL`\n1. `HIGH`\n1. `MEDIUM`\n1. `LOW`\n\nFor every finding, include:\n\n- file path\n- line number when available\n- why it is a bug, regression, or risk\n- concrete fix direction\n\n## What to Ignore\n\n- Pure style nits with no correctness impact\n- Issues outside the changed scope unless the PR makes them worse\n- Failures that standard linters or formatters would already catch\n- Speculative concerns with no concrete trigger in the diff\n\n## Output Shape\n\nUse this structure:\n\n```markdown\nCHANGE_ANALYSIS_REPORT:\n- detected_domains: [...]\n- detected_signals: [...]\n- risk_level: ...\n- affected_files: [...]\n- related_frameworks: [...]\n- identified_risks: [...]\n\nFindings\n1. [severity] Title — path:line\n   - Problem: ...\n   - Fix: ...\n\nOpen Questions\n- ...\n\nResidual Risk\n- ...\n```\n",".agents/skills/review-pr/references/review-pr-domains-and-signals.md":"# PR Review: Domain & Signal Detection Reference\n\nThis file contains the canonical change-domain and signal detection tables for PR\nreview. Referenced by: `.agents/skills/review-pr/SKILL.md`\n\n______________________________________________________________________\n\n## Severity-to-Review-Depth Mapping\n\n- **CRITICAL**: use `comprehensive` review depth\n- **HIGH**: use `comprehensive` review depth\n- **MEDIUM**: use `targeted` review depth\n- **LOW**: use `basic` review depth\n\n______________________________________________________________________\n\n## L1 Domains and L2 Signals\n\n## Domain 1: Distributed Runtime (CRITICAL/HIGH)\n\n| L2 Signal                  | File Path Pattern                                                                              | Code Pattern                                                                                                                                                              |\n| -------------------------- | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `archon_core`              | `areal/experimental/engine/archon_engine.py`, `areal/experimental/engine/archon_checkpoint.py` | `ArchonEngine`, `ArchonCheckpointManager`, `archon`                                                                                                                       |\n| `archon_parallel`          | `areal/experimental/models/archon/`, `parallel_dims.py`, `parallelize.py`                      | `ArchonParallelDims`, `_build_mesh`, `apply_moe_ep_tp`, `apply_tp`, `apply_cp`, `ExpertTensorParallel`, `etp`, `parallelize_module`, `ColwiseParallel`, `RowwiseParallel` |\n| `process_group`            | `areal/engine/fsdp_utils/`, `areal/engine/megatron_utils/`, `areal/experimental/engine/`       | `new_group`, `ProcessGroup`, `dist.get_rank(`                                                                                                                             |\n| `fsdp_core`                | `areal/engine/fsdp_engine.py`, `areal/engine/fsdp_utils/`                                      | `FSDP`, `fully_shard`, `FullyShardedDataParallel`                                                                                                                         |\n| `megatron_core`            | `areal/engine/megatron_engine.py`, `areal/engine/megatron_utils/`                              | `MegatronEngine`, `pipeline`, `micro-batch`                                                                                                                               |\n| `collectives`              | `areal/engine/`, `areal/infra/rpc/`                                                            | `all_reduce`, `all_gather`, `reduce_scatter`, `all_to_all`, `broadcast`, `barrier`                                                                                        |\n| `mesh_dtensor`             | `areal/experimental/models/archon/`, `areal/engine/fsdp_utils/`                                | `DeviceMesh`, `DTensor`, `Shard(`, `Replicate(`, `distribute_tensor`                                                                                                      |\n| `activation_checkpointing` | `areal/experimental/models/archon/activation_checkpoint.py`, `areal/models/`, `areal/engine/`  | `activation_checkpoint`, `checkpoint_wrapper`, `selective_checkpoint`                                                                                                     |\n| `weight_sync`              | `areal/experimental/engine/archon_weight_sync.py`, `areal/api/engine_api.py`, `areal/engine/`  | `WeightUpdateMeta`, `set_version`, `update_weights`                                                                                                                       |\n\n## Domain 2: Model Compute & Attention (HIGH/MEDIUM)\n\n| L2 Signal                | File Path Pattern                                                                                                                                          | Code Pattern                                                                                           |\n| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |\n| `tree_attn`              | `areal/models/tree_attn/`                                                                                                                                  | `TreeAttention`, `tree_attn`, `TreeNode`, `tree`                                                       |\n| `sdpa_varlen`            | `attention/sdpa.py`, `attention/varlen.py`, `areal/models/tree_attn/`                                                                                      | `sdpa`, `flash_attn`, `varlen`, `causal_mask`                                                          |\n| `sp_cp_attention_mask`   | `areal/models/tree_attn/`, `areal/experimental/models/archon/attention/`                                                                                   | `SequenceParallel`, `context_parallel`, `mask`                                                         |\n| `triton_kernel`          | `areal/models/tree_attn/triton_kernel.py`                                                                                                                  | `triton`, `kernel`, `autotune`                                                                         |\n| `archon_model_family`    | `areal/experimental/models/archon/model_spec.py`, `areal/experimental/models/archon/qwen*/`                                                                | `ModelSpec`, `register_model_spec`, `supported_model_types`, `state_dict_adapter`, `rope`              |\n| `archon_attention_stack` | `areal/experimental/models/archon/attention/`, `areal/experimental/models/archon/ulysses.py`                                                               | `ulysses_slice_inputs`, `ulysses_gather_output`, `gather_seq_scatter_heads`, `sdpa`, `varlen`          |\n| `archon_moe_modeling`    | `areal/experimental/models/archon/moe/`, `areal/experimental/models/archon/expert_parallel.py`, `areal/experimental/models/archon/moe_weight_converter.py` | `TokenChoiceTopKRouter`, `RouterGateLinear`, `GroupedExperts`, `MoEWeightConverter`, `expert_parallel` |\n\n## Domain 3: Inference Backend & Serving (HIGH)\n\n| L2 Signal                  | File Path Pattern                                              | Code Pattern                                                     |\n| -------------------------- | -------------------------------------------------------------- | ---------------------------------------------------------------- |\n| `vllm_ext`                 | `areal/engine/vllm_ext/`                                       | `areal_vllm_server`, `vllm_worker_extension`, `pause_generation` |\n| `remote_inference_backend` | `areal/engine/vllm_remote.py`, `areal/engine/sglang_remote.py` | `vllm`, `sglang`, `OpenAI`, `request`, `response`                |\n| `request_lifecycle`        | `areal/engine/`, `areal/infra/launcher/`                       | `enqueue`, `dequeue`, `cancel`, `timeout`                        |\n\n## Domain 4: Service Orchestration (HIGH)\n\n| L2 Signal                  | File Path Pattern                                                                                                                                       | Code Pattern                                                     |\n| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |\n| `service_routing_dataflow` | `areal/v2/agent_service/gateway/`, `areal/v2/agent_service/router/`, `areal/v2/inference_service/data_proxy/`, `areal/v2/inference_service/controller/` | `route`, `gateway`, `router`, `DataProxy`, `controller`, `batch` |\n| `session_consistency`      | `areal/v2/agent_service/`, `areal/v2/inference_service/`                                                                                                | `session`, `affinity`, `history`, `state`                        |\n\n## Domain 5: Workflow & Trainer Contract (HIGH/MEDIUM)\n\n| L2 Signal                  | File Path Pattern                                                           | Code Pattern                                        |\n| -------------------------- | --------------------------------------------------------------------------- | --------------------------------------------------- |\n| `workflow_engine_boundary` | `areal/workflow/`, `areal/trainer/`, `areal/engine/`                        | `RolloutWorkflow`, `arun_episode`, `agenerate`      |\n| `dataset_surface`          | `areal/dataset/`                                                            | `DataLoader`, `IterableDataset`, `get_*_dataset`    |\n| `async_contract`           | `areal/workflow/`, `areal/v2/agent_service/`, `areal/v2/inference_service/` | `async def`, `await`, `aiofiles`, `asyncio`         |\n| `weight_version_contract`  | `areal/api/engine_api.py`, `areal/workflow/`, `areal/trainer/`              | `WeightUpdateMeta`, `set_version`, `weight version` |\n\n## Domain 6: API & Config Compatibility (MEDIUM)\n\n| L2 Signal                   | File Path Pattern                     | Code Pattern                                                                     |\n| --------------------------- | ------------------------------------- | -------------------------------------------------------------------------------- |\n| `dataclass_schema`          | `areal/api/`                          | `@dataclass`, `field(`, `__post_init__`                                          |\n| `cli_compat`                | `areal/api/cli_args.py`               | `Literal`, `help`, `default`                                                     |\n| `backward_compat`           | `areal/api/`, `areal/infra/launcher/` | `deprecated`, `compat`, `version`                                                |\n| `project_dependency_config` | `pyproject.toml`, `uv.lock`           | `requires-python`, `dependencies`, `optional-dependencies`, `build-system`, `uv` |\n\n## Domain 7: Numerics & Tensor Semantics (MEDIUM)\n\n| L2 Signal             | File Path Pattern                                                               | Code Pattern                                            |\n| --------------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------- |\n| `shape_dtype`         | `areal/engine/`, `areal/models/`, `areal/trainer/`                              | `.view(`, `.reshape(`, `dtype=`, `.contiguous(`         |\n| `numerical_stability` | `areal/engine/`, `areal/reward/`, `areal/utils/functional/`                     | `log(`, `softmax`, `eps=`, `.clamp(`, `nan`, `inf`      |\n| `reward_surface`      | `areal/reward/`                                                                 | `reward_fn`, `AsyncRewardWrapper`, `MathVerifyWorker`   |\n| `compile_dynamo`      | `areal/experimental/models/archon/compile.py`, `areal/models/`, `areal/engine/` | `torch.compile`, `_dynamo`, `mark_dynamic`, `fullgraph` |\n| `mixed_precision_fp8` | `areal/engine/megatron_utils/fp8/`, `areal/experimental/models/archon/`         | `fp8`, `bf16`, `fp16`, `mixed precision`                |\n\n## Domain 8: Checkpoint & Recovery (CRITICAL/HIGH)\n\n| L2 Signal         | File Path Pattern                                                   | Code Pattern                                    |\n| ----------------- | ------------------------------------------------------------------- | ----------------------------------------------- |\n| `dcp_consistency` | `areal/utils/async_checkpoint.py`, `areal/engine/**/checkpoint*.py` | `dcp.save`, `dcp.load`, `DistributedCheckpoint` |\n| `optimizer_state` | `areal/engine/fsdp_utils/checkpoint.py`, `areal/utils/saver.py`     | `optimizer state`, `state_dict`                 |\n| `resume_compat`   | `areal/utils/recover.py`, `areal/utils/saver.py`                    | `resume`, `load_state_dict`, `migration`        |\n\n## Domain 9: Launcher & Infrastructure (HIGH/MEDIUM)\n\n| L2 Signal                 | File Path Pattern                                            | Code Pattern                                              |\n| ------------------------- | ------------------------------------------------------------ | --------------------------------------------------------- |\n| `launcher_resource_match` | `areal/infra/launcher/`                                      | `LaunchConfig`, `RayLauncher`, `SlurmLauncher`            |\n| `scheduler_contract`      | `areal/infra/scheduler/`, `areal/scheduler/`                 | `Scheduler`, `placement`, `resource`                      |\n| `rpc_transport`           | `areal/infra/rpc/`, `areal/v2/inference_service/data_proxy/` | `RTensor`, `serialize`, `rpc`, `fetch`                    |\n| `runtime_image_config`    | `Dockerfile`, `.dockerignore`                                | `FROM`, `ARG`, `RUN`, `ENV`, `COPY`, `uv sync`, `VARIANT` |\n\n## Domain 10: Low-Risk Hygiene (LOW)\n\n| L2 Signal                 | File Path Pattern                                                                                                                                                       | Code Pattern                                                                                                 |\n| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |\n| `tests_docs_config`       | `tests/`, `docs/`, `*.md`, `*.yaml`, `*.json`, `*.toml`                                                                                                                 | -                                                                                                            |\n| `logging_import_security` | `areal/`, `examples/`                                                                                                                                                   | `getLogger`, `print(`, `import *`, `api_key`, `token`, `password`                                            |\n| `project_docs_metadata`   | `docs/build_all.sh`, `docs/generate_cli_docs.py`, `docs/en/`, `docs/zh/`, `README.md`, `CONTRIBUTING.md`, `.github/PULL_REQUEST_TEMPLATE.md`, `.github/ISSUE_TEMPLATE/` | `jupyter-book`, `generate_cli_docs`, `build_all`, `_build`, `checklist`, `template`, `contributing`, `usage` |\n\n## Domain 11: Harness & Agent Infrastructure (MEDIUM/HIGH)\n\n| L2 Signal               | File Path Pattern                                                             | Code Pattern                                                 |\n| ----------------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------ |\n| `skill_definition`      | `.agents/skills/**/SKILL.md`, `.agents/skills/**/references/`                 | `description:`, `## Workflow`, `## Reference Files`, `skill` |\n| `platform_command_data` | `.claude/commands/`, `.claude/data/`, `.opencode/command/`, `.opencode/data/` | `@.`, `/review-pr`, `/create-pr`, `data/`, `task(`           |\n| `agent_registry_config` | `.codex/config.toml`, `.codex/agents/`, `AGENTS.md`, `CLAUDE.md`              | `agents`, `skills`, `registry`, `subagent`, `config.toml`    |\n\n## Domain 12: CI/CD & Release Automation (HIGH/CRITICAL)\n\n| L2 Signal              | File Path Pattern                                                                                                          | Code Pattern                                              |\n| ---------------------- | -------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- |\n| `github_workflow_jobs` | `.github/workflows/*.yml`                                                                                                  | `jobs:`, `runs-on:`, `needs:`, `if:`, `workflow_dispatch` |\n| `runner_provisioning`  | `.github/workflows/bake-gcp-image.yml`, `.github/workflows/runner-heartbeat.yml`                                           | `gcp`, `runner`, `image`, `heartbeat`                     |\n| `release_delivery`     | `.github/workflows/build-docker-image.yml`, `.github/workflows/tag-release-image.yml`, `.github/workflows/deploy-docs.yml` | `docker`, `tag`, `release`, `pages`, `publish`            |\n\n______________________________________________________________________\n\n## Must-Not-Regress Core Coverage\n\nThe refactor must preserve these existing review surfaces:\n\n- Archon core: `areal/experimental/models/archon/`,\n  `areal/experimental/engine/archon_engine.py`\n- FSDP core: `areal/engine/fsdp_utils/`, `areal/engine/fsdp_engine.py`\n- Megatron core: `areal/engine/megatron_engine.py`, `areal/engine/megatron_utils/`\n- Reward: `areal/reward/`\n- Dataset: `areal/dataset/`\n- Trainer: `areal/trainer/`\n- Harness: `.agents/`, `.claude/`, `.opencode/`, `.codex/`\n- CI/CD and release: `.github/workflows/`, `Dockerfile`, `pyproject.toml`\n\n______________________________________________________________________\n\n## Cross-Domain Linkage Rules\n\n| Detected Signal                                | Auto-Linked Review                                  |\n| ---------------------------------------------- | --------------------------------------------------- |\n| `archon_core` or `archon_parallel`             | Model Compute & Attention checks                    |\n| `archon_model_family` or `archon_moe_modeling` | Numerics & Tensor Semantics checks                  |\n| `tree_attn`                                    | Numerics & Tensor Semantics checks                  |\n| `reward_surface`                               | Workflow & Trainer Contract checks                  |\n| `compile_dynamo`                               | Distributed Runtime checks                          |\n| `vllm_ext`                                     | Launcher & Infrastructure checks                    |\n| `service_routing_dataflow`                     | Workflow & Trainer async-contract checks            |\n| `weight_sync`                                  | DTensor/process-group/checkpoint interaction checks |\n| `rpc_transport`                                | Distributed Runtime synchronization checks          |\n| `mixed_precision_fp8` + Distributed Runtime    | mesh + weight-sync compatibility checks             |\n| `runtime_image_config`                         | Inference Backend & Serving checks                  |\n| `project_dependency_config`                    | API & Config Compatibility checks                   |\n| `github_workflow_jobs` or `release_delivery`   | Launcher & Infrastructure checks                    |\n| `skill_definition` or `platform_command_data`  | Low-Risk Hygiene checks                             |\n\n______________________________________________________________________\n\n## Risk Identification Guidance\n\n### Distributed Runtime Risks\n\n- Archon mesh construction or parallel-dims mismatch\n- EP/TP/CP application order errors in Archon parallelization\n- Activation checkpoint placement violating TP/CP/FSDP ordering assumptions\n- Archon engine lifecycle drift around distributed setup and checkpoint boundaries\n- Collective call order mismatch across ranks\n- Wrong process-group scope in rank-sensitive logic\n- Mesh dimension mismatch and invalid DTensor placement\n- Weight version drift between rollout and training workers\n\n### Model Compute & Attention Risks\n\n- Attention mask inconsistency under TP/SP/CP paths\n- Tree attention index/routing mismatch\n- Archon model-family registration or per-family wiring drift\n- Archon MoE router/expert behavior diverging from weight-conversion expectations\n- Archon Ulysses slicing/gather semantics mismatching attention layout assumptions\n- Kernel assumptions violating dtype/shape invariants\n- Sequence packing alignment errors\n\n### Service Orchestration Risks\n\n- Session affinity or history drift across gateway/router/data proxy\n- Async message handling holes and dropped tasks\n- Controller/worker lifecycle desynchronization\n\n### Inference Backend & Serving Risks\n\n- Request lifecycle inconsistencies (enqueue/cancel/timeout)\n- Worker state transitions leaving requests stranded\n- Backend extension hooks drifting from runtime expectations\n\n### Workflow & Trainer Contract Risks\n\n- Workflow-engine contract drift across async boundaries\n- Weight version handshake mismatch between rollout and train\n- Trainer lifecycle transition inconsistencies\n\n### API & Config Compatibility Risks\n\n- Breaking config/schema changes without migration path\n- Dataclass or CLI default changes altering behavior silently\n- Missing validation for newly introduced fields\n- Dependency or build-system pin changes breaking supported environments\n\n### Numerics & Tensor Semantics Risks\n\n- Silent shape/dtype mismatch under distributed paths\n- Unstable numerical operations in loss/reward logic\n- torch.compile or dynamo guard changes breaking graph assumptions\n- Mixed-precision interaction regressions\n\n### Checkpoint & Recovery Risks\n\n- Partial-rank checkpoint participation\n- Incompatible state key evolution\n- Resume path breaking optimizer/model synchronization\n\n### Launcher & Infrastructure Risks\n\n- Resource assignment mismatching parallel strategy assumptions\n- RPC transport metadata loss (shape/dtype/device)\n- Startup/shutdown ordering races across processes\n- Runtime image or build-arg drift from supported inference/training variants\n\n### Low-Risk Hygiene Risks\n\n- Docs/config drift from actual runtime behavior\n- Logging or import hygiene regressions\n- Sensitive data exposure in logs or config\n- Documentation/build scripts or project templates drifting from actual workflow\n\n### Harness & Agent Infrastructure Risks\n\n- Skill and command docs drifting from actual platform behavior\n- Cross-platform data files falling out of sync with canonical references\n- Agent registry/config changes breaking expert routing or command discovery\n\n### CI/CD & Release Automation Risks\n\n- Workflow trigger or job dependency changes skipping required validation\n- Runner provisioning drift causing flaky or non-reproducible CI\n- Release or docs deployment jobs publishing the wrong artifacts\n",".agents/skills/review-pr/references/review-pr-templates.md":"# PR Review: Domain Templates Reference\n\nThis file contains canonical domain templates for PR review. Referenced by:\n`.agents/skills/review-pr/SKILL.md`\n\n______________________________________________________________________\n\n## Template Selection Rules\n\n1. Select templates by detected L1 domains and L2 signals.\n1. Use at most one primary template per domain.\n1. Always include **General Logic & Boundary** for non-doc/config-only PRs.\n1. Apply cross-domain linkage checks from `review-pr-domains-and-signals.md`.\n\n______________________________________________________________________\n\n## Universal Template\n\n### General Logic & Boundary\n\n```\nApplicable: Any non-doc/config-only change\nChecklist:\n- Boundary condition correctness (empty inputs, singleton, max-size)\n- Conditional logic correctness (branch inversion, short-circuit mistakes)\n- Error-path behavior (exceptions propagated with actionable context)\n- Return-value consistency across code paths\n- No newly introduced hidden behavior changes\n```\n\n______________________________________________________________________\n\n## Domain 1 Template: Distributed Runtime Review \\[comprehensive\\]\n\n```\nApplicable signals: archon_core, archon_parallel, process_group, fsdp_core, megatron_core, collectives, mesh_dtensor, activation_checkpointing, weight_sync\nChecklist:\n- Archon engine and checkpoint lifecycle remain aligned with distributed runtime assumptions\n- FSDP and Megatron engine invariants still match process-group, sharding, and pipeline assumptions\n- Archon parallel-dims and mesh construction still match downstream placement logic\n- Process-group creation/usage/cleanup is rank-consistent\n- Collective operations are called by all required ranks in consistent order\n- DeviceMesh dimensions and DTensor placements are correct for each path\n- Activation checkpoint placement remains compatible with parallel and sharding order requirements\n- Local/global tensor conversion boundaries are explicit and correct\n- Weight version propagation and update ordering are deterministic\n- No debug-only barriers left in hot path\n```\n\n## Domain 2 Template: Model Compute & Attention Review \\[comprehensive\\]\n\n```\nApplicable signals: tree_attn, sdpa_varlen, sp_cp_attention_mask, triton_kernel, archon_model_family, archon_attention_stack, archon_moe_modeling\nChecklist:\n- Attention mask semantics preserved under TP/SP/CP\n- Archon model-family registration and per-family wiring remain internally consistent\n- Archon attention/Ulysses slicing and gather paths preserve layout assumptions\n- Archon MoE router, grouped experts, and weight-conversion interfaces remain aligned\n- Tree attention index/order invariants are maintained\n- Kernel assumptions on dtype/shape/contiguity are satisfied\n- No silent behavior change in sequence packing/unpacking\n- Tensor layouts remain compatible with downstream modules\n```\n\n## Domain 3 Template: Inference Backend & Serving Review \\[comprehensive\\]\n\n```\nApplicable signals: vllm_ext, remote_inference_backend, request_lifecycle\nChecklist:\n- Request lifecycle (enqueue, execution, cancellation, timeout) is coherent\n- Worker state transitions are safe under concurrency\n- Backend-specific extension points stay API-compatible\n- Error handling does not strand in-flight requests\n- Versioning/weight-update interactions are explicit and safe\n```\n\n## Domain 4 Template: Service Orchestration Review \\[comprehensive\\]\n\n```\nApplicable signals: service_routing_dataflow, session_consistency\nChecklist:\n- Gateway/router/data-proxy routing rules are deterministic\n- Session affinity and history consistency are preserved\n- Controller/worker coordination has no lost-update window\n- Async boundaries avoid blocking operations in critical paths\n- Failure/retry behavior does not duplicate or drop work\n```\n\n## Domain 5 Template: Workflow & Trainer Contract Review \\[comprehensive\\]\n\n```\nApplicable signals: workflow_engine_boundary, dataset_surface, async_contract, weight_version_contract\nChecklist:\n- RolloutWorkflow and Engine interfaces remain contract-compatible\n- Dataset/output structure still matches workflow and trainer consumption expectations\n- Async flow uses await consistently and avoids sync I/O in async paths\n- Weight update/version handshake is preserved end-to-end\n- Trainer lifecycle transitions are valid for all execution branches\n- Call ordering assumptions across trainer/workflow/engine are unchanged or justified\n```\n\n## Domain 6 Template: API & Config Compatibility Review \\[targeted\\]\n\n```\nApplicable signals: dataclass_schema, cli_compat, backward_compat, project_dependency_config\nChecklist:\n- Public API signature and default value changes are intentional and compatible\n- Dataclass validation remains complete and informative\n- CLI options preserve expected compatibility semantics\n- New fields include safe defaults or explicit migration handling\n- Breaking changes are documented and scoped\n- Dependency and build-system changes remain compatible with supported environments\n```\n\n## Domain 7 Template: Numerics & Tensor Semantics Review \\[targeted\\]\n\n```\nApplicable signals: shape_dtype, numerical_stability, reward_surface, compile_dynamo, mixed_precision_fp8\nChecklist:\n- Tensor shape/dtype transitions are explicit and internally consistent\n- Numerical stability is protected (log/division/softmax/clamp paths)\n- Reward-side numerical behavior remains consistent with workflow consumption expectations\n- torch.compile / dynamo assumptions still hold for dynamic shapes and distributed execution\n- Mixed-precision behavior is correct for forward + backward + reduce paths\n- In-place and view/reshape operations do not corrupt gradient flow\n- Device placement and dtype combinations remain legal across code paths\n```\n\n## Domain 8 Template: Checkpoint & Recovery Review \\[comprehensive\\]\n\n```\nApplicable signals: dcp_consistency, optimizer_state, resume_compat\nChecklist:\n- Save/load requires and enforces all-rank participation where needed\n- State dict naming/structure is stable or migration-safe\n- Optimizer state sharding/gather behavior is consistent\n- Resume path restores model + optimizer + version state coherently\n- Async checkpoint behavior preserves ordering and durability assumptions\n```\n\n## Domain 9 Template: Launcher & Infrastructure Review \\[targeted\\]\n\n```\nApplicable signals: launcher_resource_match, scheduler_contract, rpc_transport, runtime_image_config\nChecklist:\n- Resource assignment matches declared parallel strategy assumptions\n- Scheduler decisions preserve required placement/affinity constraints\n- RPC serialization/deserialization keeps shape/dtype/device semantics\n- Transport retries/timeouts do not violate idempotency expectations\n- Cross-process startup/shutdown ordering is robust\n- Runtime image and build configuration remain aligned with supported variants\n```\n\n## Domain 10 Template: Low-Risk Hygiene Review \\[basic\\]\n\n```\nApplicable signals: tests_docs_config, logging_import_security, project_docs_metadata\nChecklist:\n- Tests/docs/config edits are internally consistent and non-misleading\n- Logging follows project conventions and avoids sensitive leakage\n- No wildcard imports or obvious dependency hygiene regressions\n- No accidental secrets/keys/tokens introduced\n- Docs build scripts and project templates stay aligned with real contributor workflow\n```\n\n## Domain 11 Template: Harness & Agent Infrastructure Review \\[targeted\\]\n\n```\nApplicable signals: skill_definition, platform_command_data, agent_registry_config\nChecklist:\n- Canonical skills and derived platform data remain structurally aligned\n- Command docs still point to the correct data files and execution model\n- Agent registry/config changes preserve command discovery and expert routing\n- Cross-platform mirrors are regenerated after canonical changes\n```\n\n## Domain 12 Template: CI/CD & Release Automation Review \\[comprehensive\\]\n\n```\nApplicable signals: github_workflow_jobs, runner_provisioning, release_delivery\nChecklist:\n- Workflow triggers, job dependencies, and permissions still enforce required validation\n- Runner/image provisioning remains reproducible and compatible with job expectations\n- Release, docker, and docs deployment jobs publish the intended artifacts only\n- CI changes do not silently skip tests, formatting, or release gates\n```\n\n______________________________________________________________________\n\n## Signal-Specific Add-On Checklists\n\nUse these only when corresponding L2 signals are detected.\n\n### `tree_attn` Add-On \\[comprehensive\\]\n\n```\n- Node/edge indexing is deterministic and shape-safe\n- Tree traversal order matches attention mask semantics\n- FSDP/Megatron/Archon variant modules remain behaviorally aligned\n```\n\n### `vllm_ext` Add-On \\[comprehensive\\]\n\n```\n- Server and worker extension hooks still match upstream expectations\n- Request pause/resume/cancel semantics remain coherent\n- Integration-specific monkey-patches are scoped and guarded\n```\n\n### `archon_model_family` Add-On \\[comprehensive\\]\n\n```\n- ModelSpec registration stays unique and complete for supported model types\n- Per-family model/args/spec/state-adapter wiring remains consistent\n- Pipelining hooks and model-part boundaries stay compatible with runtime assumptions\n```\n\n### `archon_moe_modeling` Add-On \\[comprehensive\\]\n\n```\n- Router top-k, gating dtype, and expert grouping semantics remain coherent\n- GroupedExperts layout and token reordering assumptions still match expert execution\n- MoE weight conversion paths stay consistent with runtime sharding expectations\n```\n\n### `service_routing_dataflow` Add-On \\[comprehensive\\]\n\n```\n- Route selection and fallback ordering are deterministic\n- Data proxy transformations preserve payload integrity\n- Session-key partitioning logic is collision-safe\n```\n\n### `remote_inference_backend` Add-On \\[targeted\\]\n\n```\n- Remote backend request/response semantics remain consistent across supported engines\n- Backend-specific transport options do not change lifecycle expectations silently\n- Shared request payload assumptions remain compatible across remote backends\n```\n\n### `weight_sync` Add-On \\[comprehensive\\]\n\n```\n- Versioned updates are monotonic and race-safe\n- Broadcast/all-gather points are aligned with consumer expectations\n- Local caching behavior cannot serve stale weights indefinitely\n```\n\n### `activation_checkpointing` Add-On \\[targeted\\]\n\n```\n- Checkpoint wrappers are applied in a parallelism-safe order\n- Selective checkpoint policies still cover the intended modules only\n- Activation recompute paths do not break sharding or sequence-parallel assumptions\n```\n\n### `reward_surface` Add-On \\[targeted\\]\n\n```\n- AsyncRewardWrapper-facing reward interfaces remain contract-compatible\n- Reward outputs keep expected shape, dtype, and per-sample semantics\n- Workflow assumptions about reward timing and batching remain valid\n```\n\n### `compile_dynamo` Add-On \\[targeted\\]\n\n```\n- torch.compile and dynamo guards still tolerate expected dynamic-shape inputs\n- fullgraph and mark_dynamic choices remain compatible with distributed execution paths\n- Compile-specific changes do not silently alter runtime fallback behavior\n```\n\n### `rpc_transport` Add-On \\[targeted\\]\n\n```\n- RTensor conversion is reversible and metadata-complete\n- Batch fetch/request framing preserves ordering and boundaries\n- Retry logic does not replay non-idempotent actions incorrectly\n```\n\n### `runtime_image_config` Add-On \\[targeted\\]\n\n```\n- Docker base image and build args still match supported backend variants\n- Layer ordering preserves expected cache and dependency behavior\n- Image contents remain aligned with runtime assumptions documented in the repo\n```\n\n### `project_dependency_config` Add-On \\[targeted\\]\n\n```\n- Python/version constraints and extras remain internally consistent\n- Lockfile changes match the intended dependency update scope\n- Build backend/tooling changes do not break install or publish workflows\n```\n\n### `github_workflow_jobs` Add-On \\[comprehensive\\]\n\n```\n- Workflow triggers and job graph still run required validation paths\n- Required secrets/permissions are scoped correctly\n- Matrix or conditional changes do not silently skip critical jobs\n```\n\n### `project_docs_metadata` Add-On \\[basic\\]\n\n```\n- Docs build entrypoints and contributor-facing metadata remain mutually consistent\n- Public templates and contributor instructions still match the actual workflow\n- Build/preview guidance still points to the supported commands\n```\n\n### `skill_definition` / `platform_command_data` Add-On \\[targeted\\]\n\n```\n- Canonical and derived review-pr data files stay in sync after edits\n- Command/import paths remain correct after file moves or renames\n- Wrapper-specific routing stays out of canonical reference files\n```\n",".agents/skills/translate-doc-zh/SKILL.md":"---\nname: translate-doc-zh\ndescription: Translate an English document under `docs/en/` into the matching Chinese document under `docs/zh/`.\n---\n\n# Translate Docs EN to ZH\n\nUse this skill when the user asks to translate a document from `docs/en/` to `docs/zh/`.\n\n## Input\n\n- A markdown file path under `docs/en/`\n\n## Workflow\n\n### Step 1: Validate the source path\n\n1. Confirm the file exists.\n1. Confirm it is under `docs/en/`.\n1. Confirm it ends with `.md`.\n1. If any check fails, stop and ask the user for a valid `docs/en/...md` path.\n\n### Step 2: Resolve the target path\n\n- Source: `docs/en/<path>.md`\n- Target: `docs/zh/<path>.md`\n\n### Step 3: Choose translation mode\n\n- If the Chinese file already exists, update only the changed parts while preserving the\n  rest.\n- If the Chinese file does not exist, translate the full document.\n\n## Translation Rules\n\n- Preserve technical terms such as FSDP, FSDP2, GRPO, PPO, DAPO, MoE, LLM, RL, RLVR,\n  Codex, Claude Code, OpenCode, Megatron, Archon, SGLang, vLLM, PyTorch, HuggingFace,\n  and Transformers.\n- Do not translate file paths, code blocks, CLI flags, or literal configuration keys.\n- Preserve Markdown structure, tables, and fenced code blocks.\n- Use concise, professional Chinese terminology.\n\n## Error Handling\n\n- If the target directory does not exist, create it before writing the translated file.\n- If the source document is partially translated already, preserve sections that do not\n  need updates.\n",".agents/skills/upgrade-deps/CHECKLIST_MAINTENANCE.md":"# Checklist Maintenance Guide\n\nThis document is the single reference for maintaining the per-package API checklists\nunder `checklists/`. It covers two complementary maintenance activities:\n\n- **Structural validation** — ensuring every AReaL import site is documented in the\n  checklist. Performed in SKILL.md → **Step 0.5** before any dependency changes.\n- **Content update** — ensuring documented API signatures and code snippets match the\n  post-upgrade state. Performed in SKILL.md → **Step 6e** after code changes are\n  applied.\n\nBoth activities share the same checklist format. This guide also covers creating new\nchecklists and writing good API catalog entries.\n\n______________________________________________________________________\n\n## 1. Checklist Structure Reference\n\nEach checklist file lives at `checklists/<package-name>.md` and follows the format\ndefined in `checklists/_TEMPLATE.md`. The key sections are:\n\n| Section                  | Purpose                                                                                                                                                                                                            |\n| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| **YAML frontmatter**     | `package`, `github` (org/repo), `branch_template`, `upstream_paths` — metadata used by the API audit (Step 6a) to clone the correct upstream tag and locate source files.                                          |\n| **Affected Files**       | Three-tier table (Primary / Secondary / Tertiary) listing every AReaL file that imports or uses the package, with a summary of what it imports.                                                                    |\n| **API Usage Catalog**    | Numbered entries documenting each distinct API surface (function, class, HTTP endpoint, schema contract). Each entry includes the upstream source path, the AReaL call-site code, and specific Check instructions. |\n| **Version-Guarded Code** | List of AReaL code locations that have version-specific conditionals for this package (e.g., `if version < \"0.4.10\": ...`).                                                                                        |\n\n______________________________________________________________________\n\n## 2. Import Patterns by Package\n\nUse these patterns to discover all AReaL usages of each focused package. Grep across\n`areal/`, `tests/`, and `examples/`.\n\n| Package           | Import path(s)    | Grep patterns                                    | Notes                                                                                 |\n| ----------------- | ----------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------- |\n| `megatron-core`   | `megatron.core`   | `from megatron.core`, `import megatron.core`     |                                                                                       |\n| `megatron-bridge` | `megatron.bridge` | `from megatron.bridge`, `import megatron.bridge` |                                                                                       |\n| `mbridge`         | `mbridge`         | `from mbridge`, `import mbridge`                 | Pinned to git commit; also check `mbridge.core.*` submodule imports                   |\n| `transformers`    | `transformers`    | `from transformers`, `import transformers`       | Exclude `flash_attn.bert_padding` false positives                                     |\n| `sglang`          | `sglang`          | `from sglang`, `import sglang`                   | Also scan `sglang_remote.py` for HTTP endpoint paths and JSON field names             |\n| `vllm`            | `vllm`            | `from vllm`, `import vllm`                       | Also scan `vllm_remote.py` and `vllm_ext/` for HTTP endpoints and JSON fields         |\n| `peft`            | `peft`            | `from peft`, `import peft`                       | Also check for indirect PEFT schema dicts (`\"r\"`, `\"lora_alpha\"`, `\"target_modules\"`) |\n| `torchao`         | `torchao`         | `from torchao`, `import torchao`                 | All usage goes through `prototype.*` path                                             |\n\n### Edge cases to watch for\n\n- **HTTP-only integrations** (sglang, vllm): The primary integration is REST-based, not\n  Python imports. Grep for endpoint path strings (e.g., `\"/generate\"`,\n  `\"/update_weights_from_distributed\"`) and JSON field names in request/response parsing\n  code.\n- **Indirect / schema-only usage**: Some files reference a package's conventions without\n  importing it directly (e.g., PEFT config dicts in `areal/api/io_struct.py`). These\n  should be listed in Affected Files and may warrant an API catalog entry if the schema\n  contract is non-trivial.\n- **Conditional / lazy imports**: Imports inside `if TYPE_CHECKING:` blocks or inside\n  function bodies. These are still real dependencies — include them.\n- **String references**: Config enums or registry keys that name a package (e.g.,\n  `\"sglang\"` in `SGLangConfig`). These rarely need catalog entries but should appear in\n  Affected Files if the file also has substantive usage.\n- **Test stubs**: Files like `tests/experimental/archon/conftest.py` that conditionally\n  stub out a package. List in Tertiary Affected Files.\n\n______________________________________________________________________\n\n## 3. Structural Validation Procedure\n\n> **Referenced by:** SKILL.md → Step 0.5\n\nThis procedure checks whether a checklist documents ALL current AReaL import sites and\ncall patterns. Run it before upgrading a package to ensure the API audit (Step 6) has\ncomplete coverage.\n\n### 3.1 Discover all usages\n\nFor each focused package being validated:\n\n1. Grep the AReaL codebase using the patterns from § 2. Search across `areal/`,\n   `tests/`, and `examples/`.\n1. For HTTP-based packages, also scan the relevant remote engine files for endpoint\n   paths and JSON field names.\n1. Exclude false positives: comments, docstrings, unrelated substrings (e.g.,\n   `flash_attn.bert_padding` is not a `transformers` import).\n1. Collect results as `{file_path, imported_names[]}` for each hit.\n\n### 3.2 Compare against Affected Files tables\n\nParse the checklist's Primary / Secondary / Tertiary tables and compare against the\ndiscovered files. Build three lists:\n\n- **MISSING** — files found in the codebase that are not listed in any tier.\n- **STALE** — files listed in the checklist that no longer import the package (file\n  deleted, import removed, or moved to a different file).\n- **CHANGED** — files listed in the checklist whose actual imports differ from what the\n  \"Imports / Usage\" column documents.\n\n### 3.3 Classify new files into tiers\n\nUse these rules (consistent with existing checklists):\n\n| Tier          | Directories                                                                                                                                                               | Rationale                                                                |\n| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |\n| **Primary**   | `areal/engine/`, `areal/experimental/engine/`, `areal/experimental/models/`                                                                                               | Engine layer — most likely to break on API changes, highest blast radius |\n| **Secondary** | `areal/models/`, `areal/workflow/`, `areal/infra/`, `areal/api/`, `areal/utils/`, `areal/trainer/`, `areal/dataset/`, `areal/reward/`, `areal/experimental/` (non-engine) | Model / infra / workflow layer — important but lower blast radius        |\n| **Tertiary**  | `tests/`, `examples/`, `docs/`, tool scripts                                                                                                                              | Tests and examples — breakage is contained, lower priority               |\n\nWhen a file spans concerns (e.g., a test that exercises engine internals), classify by\nthe file's location, not its content.\n\n### 3.4 Determine new API catalog entries\n\nFor each MISSING or CHANGED file, examine the specific imports and call sites:\n\n- If the usage pattern is **already covered** by an existing catalog entry (same\n  function/class, same call pattern) → just add the file to the Affected Files table. No\n  new catalog entry needed.\n- If the usage involves a function/class **NOT in the catalog**, or uses it with\n  **significantly different parameters** → create a new API catalog entry (see § 6 for\n  format).\n\n\"Significantly different\" means: different keyword arguments, different return value\nconsumption, or a different code path that could break independently.\n\n### 3.5 Update the checklist\n\n1. Add missing files to the appropriate tier table with their imports summary.\n1. Remove stale file entries. If uncertain whether a removal is correct, add a\n   `<!-- TODO: verify removal -->` comment instead of deleting.\n1. Update the \"Imports / Usage\" column for CHANGED files.\n1. Add new API catalog entries following the format in § 6.\n1. Renumber catalog entries sequentially (1, 2, 3, ...).\n1. Update the entry count in SKILL.md's **Checklist File Status** table at the bottom.\n\n### 3.6 Report format\n\nAfter completing validation, report changes before proceeding:\n\n```text\nChecklist validation for <package>:\n- Added N files to Affected Files (P primary, S secondary, T tertiary)\n- Added M new API catalog entries: [brief list]\n- Removed K stale file entries: [brief list]\n- Updated J existing entries with changed imports\n- No changes needed (if clean)\n```\n\n______________________________________________________________________\n\n## 4. Content Update Procedure\n\n> **Referenced by:** SKILL.md → Step 6e\n\nThis procedure updates a checklist's API signatures and code snippets AFTER an upgrade\nhas been applied and code changes (Step 6d) are complete. The goal is to keep the\nchecklist accurate for the next upgrade cycle.\n\n### 4.1 Update API signatures\n\nFor each catalog entry where the upstream API signature changed at the new version:\n\n1. Update the **Source** path if the upstream file was moved or renamed.\n1. Note any new parameters, removed parameters, or type changes in the **Check**\n   instructions — these become the baseline for the next upgrade.\n1. If a function was removed and replaced, update the entry to reference the\n   replacement.\n\n### 4.2 Update call-site code snippets\n\nIf Step 6d modified an AReaL call site to accommodate the new version:\n\n1. Replace the code snippet in the corresponding catalog entry with the updated code.\n1. Update the line number references if they shifted.\n1. Ensure the snippet includes enough context to understand the parameters used and the\n   return value consumed.\n\n### 4.3 Update version-guarded code\n\n- **Remove** entries for version guards that were cleaned up (the guarded version is now\n  below the minimum supported version).\n- **Add** entries for any new version guards introduced during the upgrade.\n- **Update** threshold versions in existing entries.\n\n### 4.4 Update frontmatter\n\n- Add or remove entries in `upstream_paths` if upstream source files were moved,\n  renamed, or deleted.\n- Update `branch_template` if the upstream project changed its tagging convention (e.g.,\n  from `v${VERSION}` to `release/${VERSION}`).\n\n### 4.5 Update SKILL.md status table\n\nUpdate the entry count and description in the **Checklist File Status** table at the\nbottom of SKILL.md to reflect the current state of the checklist.\n\n______________________________________________________________________\n\n## 5. Creating a New Checklist\n\nWhen a new package becomes \"focused\" (added to SKILL.md's Focused Packages table):\n\n1. **Copy the template.** Copy `checklists/_TEMPLATE.md` to\n   `checklists/<package-name>.md`.\n\n1. **Fill in YAML frontmatter:**\n\n   - `package`: the pip package name (e.g., `sglang`)\n   - `github`: the GitHub org/repo (e.g., `sgl-project/sglang`)\n   - `branch_template`: how to construct the git tag from a version string (e.g.,\n     `v${VERSION}`)\n   - `upstream_paths`: list of source paths in the upstream repo that are most relevant\n     to AReaL's usage\n\n1. **Run structural validation.** Follow the full procedure in § 3 to populate the\n   Affected Files tables and API Usage Catalog.\n\n1. **Update SKILL.md.** Add the package to:\n\n   - The **Focused Packages** table (Architecture section)\n   - The **Package Impact Matrix** (with correct scope and pyproject locations)\n   - The **Checklist File Status** table (bottom of file)\n\n1. **Commit** the new checklist alongside the SKILL.md update.\n\n______________________________________________________________________\n\n## 6. API Catalog Entry Format\n\nEach entry documents one distinct API surface — a function, class, HTTP endpoint, or\nschema contract. Use this format:\n\n````markdown\n### N. `fully.qualified.FunctionOrClass`\n\n**Source:** `upstream-repo/path/to/file.py`\n\nCalled in `areal/path/to/file.py` (description, lines N–M):\n\n```python\n# Paste the ACTUAL call site code — enough context to understand\n# the parameters used and the return value consumed.\nactual_call(param1=..., param2=...)\n```\n\n**Check:** [What to verify — be specific.]\n````\n\n### Guidelines\n\n- **One entry per distinct API surface.** A function, class, HTTP endpoint, or schema\n  contract gets one entry. If the same function is called in multiple files with the\n  same pattern, list all call sites in a single entry.\n- **Multiple call sites.** When the same API is used in several files, list the primary\n  call site with a full code snippet, then note additional call sites with a brief\n  reference: \"Also called in `areal/path/to/other.py`.\"\n- **HTTP endpoints.** Document the endpoint path, HTTP method, request payload fields,\n  and response fields that AReaL parses. These are API surfaces even though they are not\n  Python imports.\n- **Indirect / schema usage.** If AReaL constructs a dict that must conform to a\n  package's schema (e.g., PEFT config dicts), document the schema contract and note that\n  it is indirect.\n- **Check instructions.** Be specific and actionable:\n  - Good: \"Confirm `bias` still accepts the string `\"none\"` (not renamed to an enum).\"\n  - Bad: \"Check if anything changed.\"\n- **Code snippets.** Include enough surrounding code to show parameter names, types, and\n  how the return value is consumed. Strip unrelated code. Include line numbers for\n  reference.\n\n______________________________________________________________________\n\n## 7. When NOT to Add a Catalog Entry\n\nNot every import needs its own API catalog entry. Skip a dedicated entry when:\n\n- **Type-only imports.** A class imported solely for type annotations or `isinstance`\n  checks (e.g., `from transformers import PretrainedConfig` used only as\n  `config: PretrainedConfig`). List the file in Affected Files but skip the catalog\n  unless the type annotation relies on specific attributes that could change.\n- **Re-exports / pass-throughs.** A file that re-exports a symbol without calling it.\n- **Standard patterns unlikely to break.** Accessing `__version__`, basic enum values,\n  or other stable public API that has never changed across versions.\n- **Test files duplicating Primary/Secondary coverage.** If a test file uses the exact\n  same API pattern already documented in a Primary or Secondary entry, list it in\n  Tertiary Affected Files but skip a separate catalog entry. Exception: if the test\n  exercises a unique code path or parameter combination, add an entry.\n\nWhen in doubt, **add the entry**. A slightly verbose checklist is better than a gap that\ncauses a missed breaking change.\n",".agents/skills/upgrade-deps/SKILL.md":"---\nname: upgrade-deps\ndescription: Upgrade focused runtime dependencies in AReaL. First validates and updates per-package API checklists for structural completeness, then updates pyproject files, resolves conflicts, locks, updates the Dockerfile, and audits API compatibility against the checklists.\n---\n\n## Usage\n\n```text\n/upgrade-deps <package==version> [<package==version> ...]\n```\n\n**Arguments:** One or more pinned package versions, e.g.,\n`/upgrade-deps megatron-core==0.17.0 sglang==0.5.10 vllm==0.18.0 transformers==4.58.0`.\n\nIf a package is omitted, its current version can be preserved or upgraded, depending on\nthe resolution of `uv lock`.\n\n______________________________________________________________________\n\n## Architecture\n\n### Dual-Manifest Model\n\nAReaL maintains **two pyproject files** because SGLang and vLLM pin\nmutually-incompatible `torch` / `torchao` versions:\n\n| File                  | Inference backend | Lock file      |\n| --------------------- | ----------------- | -------------- |\n| `pyproject.toml`      | SGLang (default)  | `uv.lock`      |\n| `pyproject.vllm.toml` | vLLM              | `uv.vllm.lock` |\n\nBoth share the same core dependencies, megatron extras, and dev group. They diverge only\nin inference backend extras and `torch`/`torchao` version constraints.\n\nThe `Dockerfile` builds both variants from a single file using `ARG VARIANT` (`sglang`\nor `vllm`). The base image, torch install, and flash-attn wheels are all\nvariant-specific.\n\n### Focused Packages\n\nThe following packages are **focused** — their API usage in AReaL is cataloged, and any\nversion change triggers the API compatibility audit (Step 6):\n\n| Package           | Import path       |\n| ----------------- | ----------------- |\n| `megatron-core`   | `megatron.core`   |\n| `megatron-bridge` | `megatron.bridge` |\n| `mbridge`         | `mbridge`         |\n| `transformers`    | `transformers`    |\n| `sglang`          | `sglang`          |\n| `vllm`            | `vllm`            |\n| `peft`            | `peft`            |\n| `torchao`         | `torchao`         |\n\n`torch` is tracked in the Package Impact Matrix below for scope and Docker awareness,\nbut is **not** a focused package — it does not receive API auditing.\n\n### Package Impact Matrix\n\nEvery entry below has a **variant scope** that determines which files to edit, which\nlock files to regenerate, and whether the Dockerfile needs review. All focused packages\nplus `torch` (tracked for Docker impact only, not API-audited) are listed.\n\n| Package           | Scope            | pyproject.toml locations                                       | pyproject.vllm.toml locations                                  | Docker impact |\n| ----------------- | ---------------- | -------------------------------------------------------------- | -------------------------------------------------------------- | ------------- |\n| `sglang`          | sglang-only      | `[optional-deps].sglang`                                       | —                                                              | Base image    |\n| `vllm`            | vllm-only        | —                                                              | `[optional-deps].vllm`                                         | No            |\n| `megatron-core`   | shared           | `[optional-deps].megatron` + `[tool.uv].override-dependencies` | `[optional-deps].megatron` + `[tool.uv].override-dependencies` | No            |\n| `megatron-bridge` | shared           | `[optional-deps].megatron`                                     | `[optional-deps].megatron`                                     | No            |\n| `mbridge`         | shared           | `[optional-deps].megatron` (git pin)                           | `[optional-deps].megatron` (git pin)                           | No            |\n| `transformers`    | shared           | `[project].dependencies`                                       | `[project].dependencies`                                       | No            |\n| `peft`            | shared           | `[project].dependencies`                                       | `[project].dependencies`                                       | No            |\n| `torch`           | shared-divergent | `[project].dependencies` (≥2.9.1)                              | `[project].dependencies` (≥2.10.0)                             | Stage 1 torch |\n| `torchao`         | shared-divergent | `[tool.uv].override-dependencies` (==0.15.0)                   | `[project].dependencies` (==0.16.0)                            | No            |\n\n**Scope rules:**\n\n- **sglang-only / vllm-only**: Edit and lock only the affected variant.\n- **shared**: Edit both files; lock both variants.\n- **shared-divergent**: The two variants intentionally use different versions of the\n  same package. If the user supplies a single target version, **ask which variant(s) to\n  apply it to** rather than assuming convergence. If the user supplies two targets\n  (e.g., `torch==2.9.1@sglang torch==2.10.0@vllm`), apply each accordingly.\n\n### Upgrade Families\n\nSome packages are tightly coupled and should be upgraded together:\n\n| Family    | Members                                  | Reason                                                    |\n| --------- | ---------------------------------------- | --------------------------------------------------------- |\n| megatron  | `megatron-core`, `megatron-bridge`       | megatron-bridge wraps megatron-core; tightly coupled APIs |\n| inference | `sglang` or `vllm` + `torch` + `torchao` | Inference backends pin specific torch/torchao versions    |\n\nWhen the user upgrades one member, **check the checklist of all family members** for\nrequired co-upgrades and warn if a co-upgrade is needed but not requested.\n\n### Per-Package API Checklists\n\nEach focused package has a dedicated markdown file under `checklists/` that documents\nhow AReaL uses that package's APIs:\n\n```\n.agents/skills/upgrade-deps/\n├── SKILL.md\n└── checklists/\n    ├── megatron-core.md\n    ├── megatron-bridge.md\n    ├── vllm.md\n    ├── sglang.md\n    ├── transformers.md\n    ├── peft.md\n    └── torchao.md\n```\n\nEach checklist file MUST contain the following sections:\n\n````markdown\n---\npackage: <pip-package-name>\ngithub: <org/repo>                    # e.g., NVIDIA/Megatron-LM\nbranch_template: \"v${VERSION}\"        # how to construct the git branch/tag from version\nupstream_paths:                        # source paths to cross-reference\n  - path/to/relevant/file.py\n  - path/to/relevant/module/\n---\n\n## Affected Files\n\n### Primary (most likely to break)\n| File | Imports / Usage |\n| ---- | --------------- |\n| ...  | ...             |\n\n### Secondary\n...\n\n### Tertiary (tests, infra)\n...\n\n## API Usage Catalog\n\nFor each function/class, verify the call signature against the upstream source.\nFocus on: removed params, renamed params, new required params, changed return types,\nmoved/renamed modules.\n\n### 1. `module.submodule.function_or_class`\n\n**Source:** `upstream_path/file.py`\n\nCalled in `areal/path/to/file.py:LINE`:\n\\```python\nactual_call_site_code()\n\\```\n\n**Check:** [what to verify]\n\n### 2. ...\n\n## Version-Guarded Code (if any)\n- [file:line] description of version guard and when it can be removed\n````\n\nTo populate a checklist, catalog AReaL's usage of the package by grepping for imports\nand call sites, then fill in the template sections. Each checklist should be\nself-contained — all API signatures, file paths, and upstream references must be\nrecorded directly in the checklist file.\n\n______________________________________________________________________\n\n## Workflow\n\n### Step 0: Parse Input and Record Baseline\n\n1. Parse arguments. Each argument is `<package==version>` or, for shared-divergent\n   packages, `<package==version@variant>` (e.g., `torchao==0.16.0@vllm`).\n1. Validate that each package name is a recognized focused package (see Focused Packages\n   table). If unrecognized, warn but proceed — it may be a transitive dependency the\n   user wants to pin.\n1. For **shared-divergent** packages (`torch`, `torchao`): if the user supplies a single\n   version without a `@variant` qualifier, **ask which variant(s) to apply it to**\n   before proceeding.\n1. Record the current **resolved** versions of ALL focused packages from the applicable\n   lock file(s) as the baseline snapshot. Use `uv.lock` for sglang-scoped and shared\n   packages; `uv.vllm.lock` for vllm-scoped and shared packages. Packages scoped to a\n   single variant appear only in that variant's lock file. This baseline will be used\n   later to detect which packages actually changed — including transitive bumps caused\n   by `uv lock` resolution, not just explicit pin changes.\n\n### Step 0.5: Validate and Update Checklists\n\nBefore modifying any dependencies, verify that the API checklists for the packages being\nupgraded are structurally complete — i.e., they document all current AReaL import sites\nand call patterns. Stale checklists lead to missed breaking changes in Step 6.\n\nFor each **focused package explicitly requested** in the command that has a checklist\nfile under `checklists/`:\n\n1. **Discover all usages.** Grep the AReaL codebase (`areal/`, `tests/`, `examples/`)\n   for all imports matching the package's import path(s). See the Import Patterns table\n   in `CHECKLIST_MAINTENANCE.md` § 2 for package-specific grep patterns. For HTTP-based\n   integrations (sglang, vllm), also scan request-building code for endpoint paths and\n   JSON field names.\n\n1. **Compare against the checklist.** Diff the discovered files against the Affected\n   Files tables (Primary / Secondary / Tertiary) in the checklist. Identify:\n\n   - **Missing** — files that import the package but are not listed in any tier.\n   - **Stale** — files listed in the checklist that no longer import the package.\n   - **Changed** — files whose actual imports differ from what's documented.\n\n1. **Classify and update.** For each discrepancy, follow the Structural Validation\n   Procedure in `CHECKLIST_MAINTENANCE.md` § 3:\n\n   - Add missing files to the appropriate tier (Primary / Secondary / Tertiary).\n   - Add new API Usage Catalog entries for genuinely new call patterns.\n   - Remove stale entries.\n   - Renumber catalog entries if needed.\n\n1. **Update the Checklist File Status table** at the bottom of this file if entry counts\n   changed.\n\n1. **Report** changes before proceeding:\n\n   ```text\n   Checklist validation for <package>:\n   - Added N files to Affected Files (P primary, S secondary, T tertiary)\n   - Added M new API catalog entries: [brief list]\n   - Removed K stale entries: [brief list]\n   - No changes needed (if clean)\n   ```\n\nIf a requested focused package does **not** have a checklist file, create one from\n`checklists/_TEMPLATE.md` using the full procedure in `CHECKLIST_MAINTENANCE.md` § 5.\n\n**Scope note:** This step validates only the packages explicitly named in the command.\nPackages that are transitively bumped are identified later in Step 5; their checklists\nare validated at that point using the same procedure before the API audit in Step 6.\n\n### Step 1: Update Pyproject Files\n\nFor each requested package, using the Package Impact Matrix:\n\n1. Determine the **variant scope** (sglang-only, vllm-only, shared, shared-divergent).\n1. Edit the version pin in **all declaration locations** for each affected pyproject\n   file. A single package may appear in multiple locations:\n   - `[project].dependencies`\n   - `[project.optional-dependencies].<extra>`\n   - `[tool.uv].override-dependencies`\n1. For **upgrade families**: if `megatron-core` is being upgraded, also check whether\n   `megatron-bridge` needs a corresponding version bump. Warn if it does but was not\n   included in the command.\n\n### Step 2: Lock Dependencies (Variant-Aware)\n\nRegenerate lock files for each **affected variant**. A variant is affected if any of its\npyproject file's dependencies were modified. If conflicts arise during locking, do NOT\nattempt to resolve them in this step — just report them and defer resolution to Step 3.\n\n**SGLang variant** (if `pyproject.toml` was modified):\n\n```bash\nbash scripts/uv_lock.sh\n```\n\n**vLLM variant** (if `pyproject.vllm.toml` was modified):\n\n```bash\nbash scripts/uv_lock.sh\n```\n\nIf `uv lock` fails for either variant:\n\n1. Read the error message carefully.\n1. If it's a version conflict, proceed to Step 3 to resolve, then **return here to\n   re-lock**.\n1. Do NOT modify dependencies without user approval to make the lock succeed.\n\n**Validation:** After locking, verify both lock files exist and are non-empty.\n\n### Step 3: Resolve Dependency Conflicts\n\nResolve any conflicts from Step 2. After all conflicts are resolved, **return to Step 2\nand re-lock** the affected variant(s). Classify each conflict:\n\n**Auto-resolvable** — only AReaL's pin conflicts with an upstream package, and the\nupstream's required version is acceptable. Update AReaL's pin automatically.\n\n**Needs user input** — two upstream packages have mutual conflicts (e.g., sglang\nrequires `torch==2.9.1` but vllm requires `torch==2.10.0`). Summarize and ask the user.\n\nOutput format:\n\n```\nSummary\n\n---\nAuto-resolved (no action required):\n- <name>: <packageA> requires <versionA>, <packageB> requires <versionB>,\n  AReaL specified <oldVersion>, updated to <newVersion>\n- ...\n\n---\nConflicts (need user resolution):\n- <name>: <packageA> requires <versionA>, <packageB> requires <versionB>\n- ...\n```\n\nYou may use `override-dependencies` in `[tool.uv]` to force-pin versions where needed.\nRemember that `sglang` and `vllm` are **separate variants** maintained in different\npyproject files — they are never installed together in the same environment.\n\n### Step 4: Update Dockerfile (If Required)\n\nReview the Package Impact Matrix \"Docker impact\" column. Only proceed with Dockerfile\nchanges if an upgraded package has Docker impact. The most common triggers are:\n\n**Base image change** (triggered by `sglang` upgrade):\n\nThe Dockerfile base image is `lmsysorg/sglang:v{SGLANG_VERSION}-cu129-amd64-runtime`. If\n`sglang` was upgraded, update line 9 of the `Dockerfile`:\n\n```dockerfile\nFROM lmsysorg/sglang:v{NEW_SGLANG_VERSION}-cu129-amd64-runtime\n```\n\nVerify that the new base image tag exists on Docker Hub / GHCR before committing.\n\n**Torch version change** (triggered by `torch` upgrade):\n\nThe Dockerfile Stage 1 installs torch with variant-specific versions. Update the version\nmapping in the `RUN uv venv` command:\n\n```dockerfile\n&& if [ \"$VARIANT\" = \"vllm\" ]; then TORCH_VER=\"{VLLM_TORCH}\"; else TORCH_VER=\"{SGLANG_TORCH}\"; fi \\\n```\n\nAlso check flash-attn wheel compatibility — **both** flash-attn-2 and flash-attn-3\ninstalls use a `TORCH_TAG` that must match the torch major.minor version. Update both\noccurrences in the Dockerfile:\n\n```dockerfile\n# flash-attn-2 (first flash-attn RUN block)\n&& if [ \"$VARIANT\" = \"vllm\" ]; then TORCH_TAG=\"torch{VLLM_TORCH_MAJOR_MINOR}\"; else TORCH_TAG=\"torch{SGLANG_TORCH_MAJOR_MINOR}\"; fi \\\n\n# flash-attn-3 (second flash-attn RUN block — same TORCH_TAG pattern)\n&& if [ \"$VARIANT\" = \"vllm\" ]; then TORCH_TAG=\"torch{VLLM_TORCH_MAJOR_MINOR}\"; else TORCH_TAG=\"torch{SGLANG_TORCH_MAJOR_MINOR}\"; fi \\\n```\n\n**No Dockerfile changes needed** for: `megatron-core`, `megatron-bridge`,\n`transformers`, `peft`, `vllm`, `torchao`. These are installed via\n`uv pip install -r pyproject.toml` in Stage 3, which reads the updated pyproject\nautomatically.\n\n### Step 5: Identify Updated Focused Packages\n\nCompare the baseline snapshot (Step 0) against the **resolved** versions in the lock\nfiles (`uv.lock` and/or `uv.vllm.lock`) produced by Step 2 (or re-locked after Step 3\nconflict resolution). This catches not only explicitly requested upgrades but also\n**transitive version bumps** — e.g., upgrading `sglang` may pull in a newer\n`transformers` through dependency resolution.\n\nBuild a list of focused packages whose resolved version actually changed. This list\ndetermines which API checklists to audit in Step 6.\n\nThe focused packages to check are listed in the Focused Packages table (Architecture\nsection):\n\n- `megatron-core` (imports as `megatron.core`)\n- `megatron-bridge` (imports as `megatron.bridge`)\n- `transformers`\n- `sglang`\n- `vllm`\n- `peft`\n- `torchao`\n\nIf a package version did NOT change (even if it was in the user's input but resolved to\nthe same version), skip its API audit.\n\nFor any newly-identified package whose checklist was NOT already validated in Step 0.5,\nrun the same structural validation procedure (see `CHECKLIST_MAINTENANCE.md` § 3) on its\nchecklist before proceeding to Step 6.\n\n### Step 6: API Compatibility Audit\n\nFor each updated focused package that has a checklist file under `checklists/`:\n\n#### 6a. Clone upstream source\n\nRead the checklist frontmatter to get `github` and `branch_template`. Clone or checkout\nthe target version:\n\n```bash\nREPO_ROOT=$(pwd)\nPKG_DIR=\"${REPO_ROOT}/<package>-src\"\nVERSION=\"<target_version>\"\n# Validate VERSION to prevent command injection\nif [[ ! \"$VERSION\" =~ ^[a-zA-Z0-9._/-]+$ ]]; then\n  echo \"Error: Invalid version format: $VERSION\"; exit 1\nfi\nBRANCH=$(echo \"<branch_template>\" | sed \"s/\\${VERSION}/$VERSION/\")\nif [ ! -d \"$PKG_DIR\" ]; then\n  git clone --depth 1 --branch \"$BRANCH\" \"https://github.com/<github>.git\" \"$PKG_DIR\"\nelse\n  (cd \"$PKG_DIR\" && git fetch origin && git checkout \"$BRANCH\")\nfi\n```\n\nIf cloning fails (tag doesn't exist, etc.), report to the user immediately.\n\n#### 6b. Audit API signatures\n\nFor EACH entry in the checklist's API Usage Catalog:\n\n1. Open the upstream source file at the target version (paths listed in the checklist's\n   `upstream_paths` frontmatter).\n1. Compare the function/class signature against the current AReaL invocation.\n1. Flag any of:\n   - **Removed parameters** still passed by AReaL → must remove from call site\n   - **Renamed parameters** → must rename in call site\n   - **New required parameters** (no default) → must add to call site\n   - **New optional parameters** with useful defaults → document but skip\n   - **Changed return types** → must update consumers\n   - **Removed functions/classes** → must find replacement\n   - **Moved modules** → must update import paths\n   - **Changed method signatures** on returned objects → must update call sites\n1. Record findings per-file.\n\n#### 6c. Check version-guarded code\n\nIf the checklist has a \"Version-Guarded Code\" section, check whether any guards\nreference versions at or below the new target. If so, verify the upstream fix is present\nand note the dead code for cleanup.\n\n#### 6d. Apply code changes (if any)\n\nFor each flagged incompatibility:\n\n1. Update the call site in the affected AReaL file.\n1. **Preserve existing behavior** — do NOT refactor beyond what's required.\n1. If a function was removed, check the upstream migration guide or changelog.\n1. Priority order: engine layer → model layer → infra layer → test files.\n\nIf there are unresolvable breaking changes, **STOP and ask the user** before proceeding.\n\n#### 6e. Update checklist file\n\nUpdate the checklist file to reflect the post-upgrade state. Follow the Content Update\nProcedure in `CHECKLIST_MAINTENANCE.md` § 4:\n\n1. Update API signatures in catalog entries where upstream changed.\n1. Update call-site code snippets where Step 6d modified AReaL code.\n1. Update version-guarded code entries (remove cleaned-up guards, add new ones).\n1. Update frontmatter `upstream_paths` if source files moved.\n1. Update the entry count in the Checklist File Status table (bottom of this file).\n\nThis ensures the checklist remains an accurate reference for future upgrades.\n\n#### 6f. Clean up cloned repositories\n\nRemove the cloned upstream source directories to avoid cluttering the workspace:\n\n```bash\nrm -rf \"${REPO_ROOT}/<package>-src\"\n```\n\n### Step 7: Run Pre-Commit\n\n```bash\npre-commit run --all-files\n```\n\n### Step 8: Generate Upgrade Summary\n\nDump a formatted markdown summary to `upgrade-summary.md` in the repository root (this\nfile is gitignored and ephemeral). The summary MUST include:\n\n```markdown\n## Dependency Upgrade Summary\n\n**Date:** YYYY-MM-DD\n**Requested:** <original command>\n\n### Version Changes\n\n| Package | Old Version | New Version | Variant(s) |\n| ------- | ----------- | ----------- | ---------- |\n| ...     | ...         | ...         | ...        |\n\n### Dependency Resolution\n\n- <auto-resolved change description>\n- ...\n\n### Dockerfile Changes\n\n- <change description, or \"No changes required\">\n\n### API Compatibility Audit\n\n#### <package-name> (old → new)\n\n**Breaking changes found:**\n- [file:line] description of change\n\n**Module moves / renames:**\n- [old_path] → [new_path]\n\n**Version-guarded code:**\n- [file:line] status (still needed / can be removed)\n\n**No breaking changes found** _(if clean)_\n\n#### ...\n\n### Unresolved Issues (if any)\n\n- <description of issue and why it could not be auto-resolved>\n```\n\nIf the upgrade failed at any step, the summary should still be generated with the\nfailure reason clearly documented in the \"Unresolved Issues\" section.\n\n### Step 9: Create PR and Trigger CI (Optional)\n\nAsk the user if they want to create a PR.\n\nIf the user agrees:\n\n1. Load the `create-pr` skill to create the PR.\n1. Trigger the CI workflow manually via `.github/workflows/build-docker-image.yml` (only\n   if the Dockerfile was modified or inference backend versions changed).\n1. The Docker build CI builds both sglang and vllm images, then automatically triggers\n   testing on each. Debug until the overall workflow succeeds.\n1. If you encounter issues that cannot be resolved, ask the user for help.\n\n______________________________________________________________________\n\n## Checklist File Status\n\n| Package           | Checklist file                  | Status                                                                                                                                                                              |\n| ----------------- | ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `megatron-core`   | `checklists/megatron-core.md`   | ✅ 23 API entries (parallel_state, DDP, optimizer, pipeline, checkpointing/async internals, transformer config, FP8, GPT/MTP, tensor_parallel, custom attention, layer specs, RoPE) |\n| `megatron-bridge` | `checklists/megatron-bridge.md` | ✅ 9 API entries (AutoBridge, LoRA, save/load HF, Qwen3-VL patch target, AutoMapping, monkey-patch guard)                                                                           |\n| `mbridge`         | `checklists/mbridge.md`         | ✅ 14 API entries (AutoBridge, Bridge properties, weight mappings, LLMBridge subclassing, register_model, monkey-patch target)                                                      |\n| `vllm`            | `checklists/vllm.md`            | ✅ 14 API entries (entrypoints, LoRA manager, worker V0/V1, tool parsers, CLI)                                                                                                      |\n| `sglang`          | `checklists/sglang.md`          | ✅ 14 API entries (HTTP endpoints, tool/reasoning parsers, CLI flags, version guards)                                                                                               |\n| `transformers`    | `checklists/transformers.md`    | ✅ 12 API entries (Auto\\* classes, tokenizer, flash attention monkey-patches, Qwen VL internals, LR schedulers)                                                                     |\n| `peft`            | `checklists/peft.md`            | ✅ 4 API entries (LoraConfig, TaskType, get_peft_model, weight key format)                                                                                                          |\n| `torchao`         | `checklists/torchao.md`         | ✅ 5 API entries (fp8_blockwise_mm, enable_fp8_linear/experts, shard validation, Triton kernels)                                                                                    |\n",".agents/skills/upgrade-deps/checklists/_TEMPLATE.md":"<!-- Template for per-package API checklists. Copy this file and fill in for each package. -->\n\n______________________________________________________________________\n\npackage: <pip-package-name> github: \\<org/repo> branch_template: \"v${VERSION}\"\nupstream_paths:\n\n- path/to/relevant/file.py\n- path/to/relevant/module/\n\n______________________________________________________________________\n\n## Affected Files\n\n### Primary (engine layer — most likely to break)\n\n| File                    | Imports / Usage                   |\n| ----------------------- | --------------------------------- |\n| `areal/path/to/file.py` | `module.function`, `module.Class` |\n\n### Secondary (model / infra layer)\n\n| File                    | Imports / Usage   |\n| ----------------------- | ----------------- |\n| `areal/path/to/file.py` | `module.function` |\n\n### Tertiary (tests, config)\n\n| File                 | Imports / Usage   |\n| -------------------- | ----------------- |\n| `tests/test_file.py` | `module.function` |\n\n______________________________________________________________________\n\n## API Usage Catalog\n\nFor each function/class below, verify the call signature against the upstream source at\nthe target version. Focus on: **missing new required parameters**, **removed old\nparameters**, **renamed parameters**, **changed return types**, **changed method\nsignatures on returned objects**, and **moved/renamed modules**.\n\n### 1. `module.submodule.FunctionOrClass`\n\n**Source:** `upstream-repo/path/to/file.py`\n\nCalled in `areal/path/to/file.py`:\n\n```python\n# Paste the actual call site code here\nFunctionOrClass(param1=..., param2=...)\n```\n\n**Check:** \\[Describe what to verify — new params? renamed? return type change?\\]\n\n### 2. ...\n\n______________________________________________________________________\n\n## Version-Guarded Code\n\n<!-- List any AReaL code that has version-specific behavior for this package -->\n\n- `areal/path/to/file.py:LINE` — description of version guard and threshold\n",".agents/skills/upgrade-deps/checklists/mbridge.md":"---\npackage: mbridge\ngithub: ISEEKYAN/mbridge\nbranch_template: ${VERSION}\nupstream_paths:\n  - mbridge/__init__.py\n  - mbridge/core/__init__.py\n  - mbridge/core/auto_bridge.py\n  - mbridge/core/bridge.py\n  - mbridge/core/llm_bridge.py\n  - mbridge/core/util.py\n---\n\n## Affected Files\n\n### Primary (engine layer — most likely to break)\n\n| File                                        | Imports / Usage                                                                                                                                                                                 |\n| ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `areal/engine/megatron_engine.py`           | `import mbridge` → `mbridge.AutoBridge.from_pretrained`, `.dtype`, `.set_extra_args`                                                                                                            |\n| `areal/models/mcore/hf_load.py`             | `from mbridge.core.bridge import Bridge` → `bridge._get_actual_hf_path`, `._weight_name_mapping_*`, `.hf_config`, `.dtype`, `.config`                                                           |\n| `areal/models/mcore/hf_save.py`             | `from mbridge.core import Bridge`, `from mbridge.core.util import unwrap_model` → `bridge._weight_name_mapping_*`, `._weight_merge_across_tp`, `._weight_to_hf_format`, `.hf_config`, `.config` |\n| `areal/models/mcore/bailing_moe_bridge.py`  | `from mbridge.core import LLMBridge, register_model` → subclasses `LLMBridge`, uses `@register_model` decorator                                                                                 |\n| `areal/models/tree_attn/module_megatron.py` | `from mbridge.core import LLMBridge` → monkey-patches `LLMBridge._get_transformer_layer_spec`                                                                                                   |\n\n### Secondary (model / infra layer)\n\n| File                             | Imports / Usage                                                                          |\n| -------------------------------- | ---------------------------------------------------------------------------------------- |\n| `areal/models/mcore/registry.py` | `from mbridge.core.bridge import Bridge` → `bridge.hf_config`, `.config`, `.get_model()` |\n\n### Tertiary (tests, config)\n\n| File                                          | Imports / Usage                                         |\n| --------------------------------------------- | ------------------------------------------------------- |\n| `tests/test_estimate_num_params.py`           | `import mbridge` → `mbridge.AutoBridge.from_pretrained` |\n| `areal/tools/validation_base.py`              | `\"mbridge\"` in `CRITICAL_PACKAGES` (metadata only)      |\n| `areal/tools/validate_docker_installation.py` | `\"mbridge\"` in package validation list (metadata only)  |\n\n______________________________________________________________________\n\n## API Usage Catalog\n\nFor each function/class below, verify the call signature against the upstream source at\nthe target version. Focus on: **missing new required parameters**, **removed old\nparameters**, **renamed parameters**, **changed return types**, **changed method\nsignatures on returned objects**, and **moved/renamed modules**.\n\n### 1. `mbridge.AutoBridge.from_pretrained`\n\n**Source:** `mbridge/core/auto_bridge.py`\n\nCalled in `areal/engine/megatron_engine.py` (line 507):\n\n```python\nself.bridge = mbridge.AutoBridge.from_pretrained(\n    self.config.path, trust_remote_code=True\n)\n```\n\nAlso called in `tests/test_estimate_num_params.py` (line 43):\n\n```python\nbridge = mbridge.AutoBridge.from_pretrained(model_name_or_path)\n```\n\n**Check:** Confirm `trust_remote_code` is still accepted as a keyword argument. Verify\nthe first positional arg is still the model path (str). Verify the return type is still\na `Bridge` (or subclass like `LLMBridge`) instance with `.hf_config`, `.config`, and\n`.dtype` attributes.\n\n______________________________________________________________________\n\n### 2. `Bridge.dtype` (property/attribute)\n\n**Source:** `mbridge/core/bridge.py`\n\nSet in `areal/engine/megatron_engine.py` (line 510):\n\n```python\nself.bridge.dtype = self.dtype\n```\n\n**Check:** Confirm `dtype` is still a settable attribute (not read-only property).\nVerify it accepts `torch.dtype` values. This controls the precision used for weight\nloading and conversion throughout the bridge.\n\n______________________________________________________________________\n\n### 3. `Bridge.set_extra_args`\n\n**Source:** `mbridge/core/bridge.py` or `mbridge/core/llm_bridge.py`\n\nCalled in `areal/engine/megatron_engine.py` (lines 512–518):\n\n```python\nself.bridge.set_extra_args(\n    recompute_granularity=self.mcore_config.recompute_granularity,\n    recompute_method=self.mcore_config.recompute_method,\n    recompute_num_layers=self.mcore_config.recompute_num_layers,\n    distribute_saved_activations=self.mcore_config.distribute_saved_activations,\n    recompute_modules=self.mcore_config.recompute_modules,\n)\n```\n\n**Check:** Confirm all five keyword arguments are still accepted:\n`recompute_granularity`, `recompute_method`, `recompute_num_layers`,\n`distribute_saved_activations`, `recompute_modules`. Check if new required arguments\nwere added.\n\n______________________________________________________________________\n\n### 4. `Bridge.get_model`\n\n**Source:** `mbridge/core/bridge.py` or `mbridge/core/llm_bridge.py`\n\nCalled in `areal/models/mcore/registry.py` (lines 178–188):\n\n```python\nmodels = bridge.get_model(\n    wrap_with_ddp=mcore_config.wrap_with_ddp,\n    ddp_config=dataclasses.asdict(mcore_config.ddp),\n    use_torch_fsdp2=mcore_config.use_torch_fsdp2,\n    use_custom_fsdp=mcore_config.use_custom_fsdp,\n    fp16=tf_config.fp16,\n    bf16=tf_config.bf16,\n    use_precision_aware_optimizer=mcore_config.use_precision_aware_optimizer,\n    overlap_param_gather_with_optimizer_step=mcore_config.overlap_param_gather_with_optimizer_step,\n)\n```\n\n**Check:** Confirm all keyword arguments are still accepted: `wrap_with_ddp`,\n`ddp_config`, `use_torch_fsdp2`, `use_custom_fsdp`, `fp16`, `bf16`,\n`use_precision_aware_optimizer`, `overlap_param_gather_with_optimizer_step`. Verify the\nreturn type is still an iterable of model instances (GPTModel or DDP-wrapped). Check for\nnewly added required parameters.\n\n______________________________________________________________________\n\n### 5. `Bridge._get_actual_hf_path`\n\n**Source:** `mbridge/core/bridge.py`\n\nCalled in `areal/models/mcore/hf_load.py` (line 539):\n\n```python\nweights_path = bridge._get_actual_hf_path(weights_path)\n```\n\n**Check:** Confirm this private method still exists and accepts a single string argument\n(path). Verify it returns a string (resolved path to the actual HF weights directory).\nSince this is a private API, check if it was renamed or replaced.\n\n______________________________________________________________________\n\n### 6. `Bridge._weight_name_mapping_mcore_local_to_global`\n\n**Source:** `mbridge/core/bridge.py` or `mbridge/core/llm_bridge.py`\n\nCalled in `areal/models/mcore/hf_load.py` (line 570):\n\n```python\nlocal_to_global_map = bridge._weight_name_mapping_mcore_local_to_global(model)\n```\n\nAlso called in `areal/models/mcore/hf_save.py` (line 243):\n\n```python\nbridge._weight_name_mapping_mcore_local_to_global(model, consider_ep=False)\n```\n\n**Check:** Confirm the method still accepts a model as the first positional argument.\nVerify `consider_ep` keyword argument is still accepted (used in hf_save.py). Verify the\nreturn type is still `dict[str, str]` mapping local parameter names to global names.\nCheck if the method was renamed or its signature changed.\n\n______________________________________________________________________\n\n### 7. `Bridge._weight_name_mapping_mcore_to_hf`\n\n**Source:** `mbridge/core/bridge.py` or `mbridge/core/llm_bridge.py`\n\nCalled in `areal/models/mcore/hf_load.py` (line 573):\n\n```python\nlocal_to_hf_map = {\n    k: bridge._weight_name_mapping_mcore_to_hf(v)\n    for k, v in local_to_global_map.items()\n    if \"_extra_state\" not in k\n}\n```\n\n**Check:** Confirm the method accepts a single string argument (mcore global weight\nname). Verify the return type is still `list[str]` (list of HF weight names). This is\nalso overridden in `BailingMoeBridge` — check that the base class signature is stable.\n\n______________________________________________________________________\n\n### 8. `Bridge._weight_merge_across_tp`\n\n**Source:** `mbridge/core/bridge.py` or `mbridge/core/llm_bridge.py`\n\nCalled in `areal/models/mcore/hf_save.py` (lines 414–415):\n\n```python\ninfer_params = bridge._weight_merge_across_tp(\n    s.global_name, infer_params, param\n)\n```\n\n**Check:** Confirm the method accepts three positional arguments: `mcore_weights_name`\n(str), `tp_shards` (list of tensors), `param` (tensor). Verify the return type is still\na `torch.Tensor`. This is overridden in `BailingMoeBridge` — check base class stability.\n\n______________________________________________________________________\n\n### 9. `Bridge._weight_to_hf_format`\n\n**Source:** `mbridge/core/bridge.py` or `mbridge/core/llm_bridge.py`\n\nCalled in `areal/models/mcore/hf_save.py` (lines 422–423):\n\n```python\nconverted_names, converted_params = bridge._weight_to_hf_format(\n    s.global_name, infer_params\n)\n```\n\n**Check:** Confirm the method accepts two positional arguments: `mcore_weights_name`\n(str) and `mcore_weights` (tensor). Verify the return type is still\n`tuple[list[str], list[torch.Tensor]]`. This is overridden in `BailingMoeBridge` — check\nbase class stability.\n\n______________________________________________________________________\n\n### 10. `mbridge.core.util.unwrap_model`\n\n**Source:** `mbridge/core/util.py`\n\nCalled in `areal/models/mcore/hf_save.py` (line 237):\n\n```python\nmodels = [unwrap_model(model) for model in models]\n```\n\n**Check:** Confirm the function still accepts a single model argument. Verify it returns\nthe unwrapped model (strips DDP/FSDP wrappers). Check if the function was moved to a\ndifferent module.\n\n______________________________________________________________________\n\n### 11. `LLMBridge` (subclassing contract)\n\n**Source:** `mbridge/core/llm_bridge.py`\n\nSubclassed in `areal/models/mcore/bailing_moe_bridge.py` (line 87):\n\n```python\nclass BailingMoeBridge(LLMBridge):\n    TransformerConfigClass = MLATransformerConfig\n\n    def _build_config(self): ...\n    def _get_gptmodel_args(self) -> dict: ...\n    def _get_transformer_layer_spec(self, vp_stage: int | None = None): ...\n    def _weight_name_mapping_mcore_to_hf(self, mcore_weights_name: str) -> list[str]: ...\n    def _weight_merge_across_tp(self, mcore_weights_name, tp_shards, param): ...\n    def _weight_to_mcore_format(self, mcore_weights_name, hf_weights): ...\n    def _weight_to_hf_format(self, mcore_weights_name, mcore_weights): ...\n```\n\n**Check:** Confirm `LLMBridge` still exposes these overridable methods with compatible\nsignatures. Verify `TransformerConfigClass` class attribute is still supported. Check if\nany abstract methods were added that subclasses must now implement. Verify\n`_build_base_config(...)` still accepts the keyword arguments used in\n`BailingMoeBridge._build_config()` (especially MoE and MLA parameters). Check if\n`has_vp_stage` attribute is still read by the base class.\n\n______________________________________________________________________\n\n### 12. `register_model` decorator\n\n**Source:** `mbridge/core/__init__.py` (re-exported from `mbridge/core/llm_bridge.py` or\nsimilar)\n\nUsed in `areal/models/mcore/bailing_moe_bridge.py` (lines 84–86):\n\n```python\n@register_model(\"bailing_moe_v2\")\n@register_model(\"bailing_moe_linear\")\n@register_model(\"bailing_hybrid\")\nclass BailingMoeBridge(LLMBridge):\n    ...\n```\n\n**Check:** Confirm `register_model` is still importable from `mbridge.core`. Verify it\nstill accepts a single string argument (model type name). Verify it still works as a\nclass decorator that registers the bridge class for `AutoBridge.from_pretrained`\ndispatch. Check if the registration mechanism changed (e.g., from decorator to explicit\nregistry call).\n\n______________________________________________________________________\n\n### 13. `LLMBridge._get_transformer_layer_spec` (monkey-patch target)\n\n**Source:** `mbridge/core/llm_bridge.py`\n\nMonkey-patched in `areal/models/tree_attn/module_megatron.py` (lines 195–213):\n\n```python\noriginal_layer_spec_getter = LLMBridge._get_transformer_layer_spec\n\ndef _patched_getter(self, vp_stage: int | None = None):\n    spec: TransformerBlockSubmodules = original_layer_spec_getter(self, vp_stage)\n    for layer_spec in spec.layer_specs:\n        ...\n    return spec\n\nLLMBridge._get_transformer_layer_spec = _patched_getter\n```\n\n**Check:** Confirm `_get_transformer_layer_spec` still exists on `LLMBridge` with\nsignature `(self, vp_stage: int | None = None)`. Verify the return type is still\n`TransformerBlockSubmodules` with a `.layer_specs` attribute (list of layer specs). Each\nlayer spec must still have `.module`, `.submodules`, and\n`.submodules.self_attention.submodules.core_attention` that can be reassigned. If the\nmethod was renamed or its return structure changed, the monkey-patch will silently fail.\n\n______________________________________________________________________\n\n### 14. `Bridge.hf_config` and `Bridge.config` (properties)\n\n**Source:** `mbridge/core/bridge.py`\n\nAccessed throughout:\n\n- `bridge.hf_config` → HuggingFace `PretrainedConfig` instance (used in hf_load.py,\n  hf_save.py, registry.py, bailing_moe_bridge.py)\n- `bridge.config` → Megatron `TransformerConfig` instance (used in hf_save.py,\n  registry.py)\n\n```python\n# hf_load.py\nbridge.hf_config  # .num_key_value_heads, .hidden_size, .num_attention_heads, .quantization_config\nbridge.config     # .fp8, .fp8_param\nbridge.dtype      # torch.dtype\n\n# registry.py\nbridge.hf_config  # ._name_or_path\nbridge.config     # returned as TransformerConfig\n```\n\n**Check:** Confirm `hf_config` still returns a HuggingFace `PretrainedConfig` (or\nsubclass). Confirm `config` still returns a Megatron `TransformerConfig` with at least\n`.fp8`, `.fp8_param`, `.num_moe_experts` attributes. These are the most widely accessed\nproperties — any type change will cascade across all Primary files.\n\n______________________________________________________________________\n\n## Version-Guarded Code\n\n_None currently._ The `mbridge` package is pinned to a git commit (`310e8fb`) rather\nthan a versioned release, so no version-conditional code exists.\n",".agents/skills/upgrade-deps/checklists/megatron-bridge.md":"---\npackage: megatron-bridge\ngithub: NVIDIA-NeMo/Megatron-Bridge\nbranch_template: v${VERSION}\nupstream_paths:\n  - megatron/bridge/__init__.py\n  - megatron/bridge/models/conversion/auto_bridge.py\n  - megatron/bridge/models/conversion/param_mapping.py\n  - megatron/bridge/models/qwen_vl/modelling_qwen3_vl/text_model.py\n  - megatron/bridge/peft/lora.py\n---\n\n## Affected Files\n\n### Primary (engine layer — most likely to break)\n\n| File                                           | Imports / Usage                                                |\n| ---------------------------------------------- | -------------------------------------------------------------- |\n| `areal/engine/megatron_engine.py`              | `megatron.bridge.AutoBridge`, `megatron.bridge.peft.lora.LoRA` |\n| `areal/engine/megatron_utils/megatron_lora.py` | `megatron.bridge.AutoBridge` (inside function, monkey-patched) |\n\n### Secondary (model / infra layer)\n\n| File                                        | Imports / Usage                                                   |\n| ------------------------------------------- | ----------------------------------------------------------------- |\n| `areal/models/mcore/vocab_parallel_head.py` | `AutoMapping.register_module_type` for dynamic LM-head subclasses |\n\n### Tertiary (tests, config)\n\n| File                                          | Imports / Usage                                                                   |\n| --------------------------------------------- | --------------------------------------------------------------------------------- |\n| `areal/tools/validation_base.py`              | `\"megatron-bridge\"` → `\"megatron.bridge\"` in `PACKAGE_IMPORT_MAP` (metadata only) |\n| `areal/tools/validate_docker_installation.py` | Megatron Bridge package validation metadata                                       |\n| `tests/test_bailing_v3_nccl.py`               | Conditional Bridge availability and real conversion integration                   |\n| `tests/test_megatron_bridge_deterministic.py` | Bridge provider determinism integration                                           |\n| `tests/test_megatron_engine.py`               | Megatron engine integration through Bridge-backed paths                           |\n| `tests/test_megatron_transport.py`            | Conditional import of Bridge-backed Megatron engine                               |\n| `tests/test_megatron_lm_head.py`              | `AutoMapping` registry assertion for promoted LM-head subclasses                  |\n\n______________________________________________________________________\n\n## API Usage Catalog\n\nFor each function/class below, verify the call signature against the upstream source at\nthe target version. Focus on: **missing new required parameters**, **removed old\nparameters**, **renamed parameters**, **changed return types**, **changed method\nsignatures on returned objects**, and **moved/renamed modules**.\n\n### 1. `megatron.bridge.AutoBridge.from_hf_pretrained`\n\n**Source:** `megatron/bridge/models/conversion/auto_bridge.py`\n\nCalled in `areal/engine/megatron_engine.py` (line 430):\n\n```python\nself.bridge = MegatronBridgeAutoBridge.from_hf_pretrained(\n    self.config.path,\n    trust_remote_code=True,\n    dtype=self.config.dtype,\n)\n```\n\n**Check:** Confirm `trust_remote_code` and `dtype` are still accepted keyword arguments.\nVerify the first positional arg is still the model path. Verify the method still returns\na bridge object that exposes `save_hf_pretrained`, `load_hf_weights`, and (depending on\nversion) `save_hf_adapter`. Check for any new required parameters.\n\n______________________________________________________________________\n\n### 2. `megatron.bridge.AutoBridge.save_hf_pretrained`\n\n**Source:** `megatron/bridge/models/conversion/auto_bridge.py`\n\nCalled in `areal/engine/megatron_engine.py` (line 1561):\n\n```python\nbridge.save_hf_pretrained(model, path, source_path=base_model_path)\n```\n\n**Check:** Confirm `source_path` is still a valid keyword argument. Verify positional\norder of `model` and `path` hasn't changed. Check return type (currently void/`None`).\n\n______________________________________________________________________\n\n### 3. `megatron.bridge.AutoBridge.load_hf_weights`\n\n**Source:** `megatron/bridge/models/conversion/auto_bridge.py`\n\nCalled in `areal/engine/megatron_engine.py` (line 1595):\n\n```python\nbridge.load_hf_weights(model, hf_path=path)\n```\n\n**Check:** Confirm `hf_path` is still the correct keyword name. Verify `model` is still\nthe first positional argument. Check for newly added required arguments.\n\n______________________________________________________________________\n\n### 4. `megatron.bridge.AutoBridge.save_hf_adapter`\n\n**Source:** `megatron/bridge/models/conversion/auto_bridge.py`\n\nCalled in `areal/engine/megatron_engine.py` (lines 1554-1559) via the monkey-patched\nmethod on the bridge instance:\n\n```python\nself.bridge.save_hf_adapter(\n    self.model,\n    path=path,\n    peft_config=self.bridge_lora,\n    base_model_name_or_path=base_model_path or self.config.path,\n)\n```\n\n**Check:** If the new version adds this method natively, confirm its signature matches\nthe monkey-patch in `areal/engine/megatron_utils/megatron_lora.py` (line 189). The\nmonkey-patched signature is:\n\n```python\ndef save_hf_adapter(\n    self, model, path, peft_config, base_model_name_or_path=None, show_progress=True\n)\n```\n\nAny mismatch in parameter names or order will silently break adapter saving. Note that\n`peft_config` receives a `megatron.bridge.peft.lora.LoRA` instance (not a dict). See\nalso the [Version-Guarded Code](#version-guarded-code) section.\n\n______________________________________________________________________\n\n### 5. `megatron.bridge.AutoBridge.export_adapter_weights`\n\n**Source:** `megatron/bridge/models/conversion/auto_bridge.py`\n\nCalled inside the monkey-patched `save_hf_adapter` in\n`areal/engine/megatron_utils/megatron_lora.py` (lines 237-240):\n\n```python\nfor name, tensor in self.export_adapter_weights(\n    model, cpu=True, show_progress=show_progress\n):\n    adapter_state[f\"base_model.model.{name}\"] = tensor.clone().float()\n```\n\n**Check:** Confirm `cpu` and `show_progress` are still accepted. Verify the method\nyields `(name, tensor)` tuples (iterable of pairs). The names are module FQNs without\nthe `base_model.model.` prefix — that prefix is added by the caller. This is a native\nbridge method used inside the monkey-patch — if its return type or signature changes,\nthe patch breaks.\n\n______________________________________________________________________\n\n### 6. `megatron.bridge.peft.lora.LoRA`\n\n**Source:** `megatron/bridge/peft/lora.py`\n\nCalled in `areal/engine/megatron_engine.py` (line 233):\n\n```python\nbridge_lora = MegatronBridgeLoRA(\n    target_modules=target_modules,\n    dim=lora_rank,\n    alpha=lora_alpha,\n    dropout=0.0,\n)\n```\n\n**Check:** Confirm `dim` is still the rank parameter (not renamed to `r` or `rank`).\nVerify `alpha` and `dropout` are still accepted. Check the `target_modules` accepted\ntype (list of strings vs. regex).\n\n______________________________________________________________________\n\n### 7. `LoRA.__call__` (apply to model)\n\n**Source:** `megatron/bridge/peft/lora.py`\n\nCalled in `areal/engine/megatron_engine.py` (lines 239-240):\n\n```python\nself.model = _MegatronModelList(self.bridge_lora(self.model, training=True))\nself.bridge_lora.set_params_to_save(self.model)\n```\n\n**Check:** Confirm `LoRA` instances are still callable with `(model, training=...)`.\nVerify the return type — it returns a modified model (or list of model chunks), which is\nthen wrapped in `_MegatronModelList`. Confirm `set_params_to_save(model)` still exists\nand marks LoRA parameters for checkpoint saving. Check if `training=True` is still the\ncorrect keyword to enable grad on LoRA parameters.\n\n______________________________________________________________________\n\n### 8. Qwen3-VL model forward contract\n\n**Source:** `megatron/bridge/models/qwen_vl/modelling_qwen3_vl/text_model.py`\n\nCalled through `packed_context_parallel_forward` in\n`areal/engine/megatron_utils/packed_context_parallel.py`:\n\n```python\noutput = model(\n    input_ids=input_ids,\n    attention_mask=attention_mask,\n    position_ids=position_ids,\n    packed_seq_params=packed_seq_params,\n    loss_mask=mtp_loss_mask,\n)\n```\n\n**Check:** Verify `Qwen3VLModel.forward` and its `Qwen3VLGPTModel` decoder accept\n`loss_mask`, preserve `labels=None` logits output, and pass layout-aligned `input_ids`\nto MCore's native MTP path. Confirm the upstream shadow embedding exposes\n`word_embeddings`; AReaL no longer patches either class.\n\n______________________________________________________________________\n\n### 9. `AutoMapping.register_module_type`\n\n**Source:** `megatron/bridge/models/conversion/param_mapping.py`\n\nCalled after dynamically promoting an MCore output layer in\n`areal/models/mcore/vocab_parallel_head.py`:\n\n```python\nAutoMapping.register_module_type(output_layer.__class__.__name__, \"column\")\n```\n\n**Check:** Confirm the import path, classmethod signature, accepted mapping type\n(`\"column\"`), and registry lifetime. This mapping must allow Bridge to export dynamic\nAReAL LM-head subclasses without replacing their parameters or hooks.\n\n______________________________________________________________________\n\n## Version-Guarded Code\n\n- `areal/engine/megatron_utils/megatron_lora.py:185` —\n  `hasattr(AutoBridge, \"save_hf_adapter\")` guard. The monkey-patch at line 291 is only\n  applied when `save_hf_adapter` does not exist on `AutoBridge`. The module-level call\n  at line 298 (`_monkey_patch_save_hf_adapter()`) runs at import time, so the guard is\n  evaluated once on first import. If upgrading to a version that ships `save_hf_adapter`\n  natively, the guard will skip patching, but the native signature must then match what\n  AReaL's call site expects (see entry 4 above). Once confirmed compatible, the entire\n  function `_monkey_patch_save_hf_adapter()` and its line-298 invocation can be removed.\n",".agents/skills/upgrade-deps/checklists/megatron-core.md":"---\npackage: megatron-core\ngithub: NVIDIA/Megatron-LM\nbranch_template: core_v${VERSION}\nupstream_paths:\n  - megatron/core/parallel_state.py\n  - megatron/core/distributed/\n  - megatron/core/optimizer/\n  - megatron/core/optimizer_param_scheduler.py\n  - megatron/core/pipeline_parallel/\n  - megatron/core/tensor_parallel/\n  - megatron/core/transformer/transformer_config.py\n  - megatron/core/transformer/pipeline_parallel_layer_layout.py\n  - megatron/core/transformer/multi_token_prediction.py\n  - megatron/core/transformer/multi_latent_attention.py\n  - megatron/core/transformer/spec_utils.py\n  - megatron/core/transformer/transformer_block.py\n  - megatron/core/transformer/transformer_layer.py\n  - megatron/core/dist_checkpointing/\n  - megatron/core/dist_checkpointing/mapping.py\n  - megatron/core/dist_checkpointing/serialization.py\n  - megatron/core/dist_checkpointing/strategies/async_utils.py\n  - megatron/core/dist_checkpointing/strategies/fully_parallel.py\n  - megatron/core/fp8_utils.py\n  - megatron/core/models/gpt/\n  - megatron/core/packed_seq_params.py\n  - megatron/core/models/common/embeddings/rotary_pos_embedding.py\n  - megatron/core/utils.py\n---\n\n## Affected Files\n\n### Primary (engine layer — most likely to break)\n\n| File                                                     | Imports / Usage                                                                                                                                                                                                  |\n| -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `areal/engine/megatron_engine.py`                        | `parallel_state`, `tensor_parallel`, `DDP`, `finalize_model_grads`, `OptimizerConfig`, `get_megatron_optimizer`, `OptimizerParamScheduler`, `get_forward_backward_func`, `TransformerConfig`, `get_model_config` |\n| `areal/engine/megatron_utils/checkpointer.py`            | `dist_checkpointing.save`, `load`, sharded strategies, `ShardedObject`                                                                                                                                           |\n| `areal/engine/megatron_utils/megatron.py`                | `parallel_state`, FP8 detection, `TransformerConfig`                                                                                                                                                             |\n| `areal/engine/megatron_utils/pipeline_parallel.py`       | `TransformerConfig`, `PipelineParallelLayerLayout`                                                                                                                                                               |\n| `areal/engine/megatron_utils/packed_context_parallel.py` | `PackedSeqParams`, `parallel_state`                                                                                                                                                                              |\n| `areal/engine/megatron_utils/fp8/tensor_helper.py`       | `fp8_utils.is_float8tensor`                                                                                                                                                                                      |\n| `areal/engine/megatron_utils/deterministic.py`           | `AttnBackend` enum for deterministic attention selection                                                                                                                                                         |\n| `areal/engine/megatron_utils/weight_residency.py`        | `DistributedDataParallel` flat-buffer residency (`buffers`, `expert_parallel_buffers`)                                                                                                                           |\n\n### Secondary (model / infra layer)\n\n| File                                              | Imports / Usage                                                                                              |\n| ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |\n| `areal/models/mcore/registry.py`                  | `GPTModel`, `DDP`, `TransformerConfig`, `parallel_state`, tensor-parallel utilities                          |\n| `areal/models/mcore/bailing_moe.py`               | `MLATransformerConfig`, layer specs/builders, transformer blocks, TE/torch/fused norms                       |\n| `areal/models/mcore/bailing_v3.py`                | `MLATransformerConfig`, layer specs/builders, transformer blocks, TE/TP linears, TE/torch/fused norms        |\n| `areal/models/mcore/bailing_v3_bridge.py`         | `MLATransformerConfig`, `AttnBackend`, `parallel_state`                                                      |\n| `areal/models/mcore/bailing_v3_mla.py`            | `MLASelfAttention`, `build_module`, TE/TP column-parallel linear                                             |\n| `areal/models/mcore/lightning_attention.py`       | `parallel_state`, `apply_rotary_pos_emb`, `MegatronModule`, `ModuleSpec`, `build_module`, checkpoint helpers |\n| `areal/models/mcore/kda_attention.py`             | Sharded checkpoint types, `PackedSeqParams`, TP mappings/RNG, transformer module/spec/checkpoint helpers     |\n| `areal/models/mcore/hf_load.py`                   | `parallel_state`, `fp8_utils.is_float8tensor`                                                                |\n| `areal/models/mcore/hf_save.py`                   | `parallel_state`, `fp8_utils.is_float8tensor`                                                                |\n| `areal/models/mcore/vocab_parallel_head.py`       | Tensor-parallel layers and collectives                                                                       |\n| `areal/models/tree_attn/module_megatron.py`       | `PackedSeqParams`, `TransformerConfig`, `SelfAttention`, `AttnMaskType`, transformer layer/block specs       |\n| `areal/models/mcore/bailing_moe_bridge.py`        | `MLATransformerConfig`, `AttnBackend`                                                                        |\n| `areal/models/mcore/common.py`                    | `TransformerConfig`                                                                                          |\n| `areal/models/mcore/qwen3.py`                     | `gpt_layer_specs`, `TransformerConfig`                                                                       |\n| `areal/v2/weight_update/awex/delta_detect.py`     | Lazy `parallel_state` access for Megatron delta ownership                                                    |\n| `areal/v2/weight_update/awex/megatron_adapter.py` | Lazy `parallel_state` access for AWEX Megatron rank/group handling                                           |\n\n### Tertiary (tests, infra)\n\n| File                                                | Imports / Usage                                                   |\n| --------------------------------------------------- | ----------------------------------------------------------------- |\n| `areal/infra/workflow_executor.py`                  | conditional `parallel_state` for DP world size                    |\n| `tests/test_estimate_num_params.py`                 | `parallel_state`, `tensor_parallel`                               |\n| `tests/fp8/engine_utils.py`                         | `parallel_state`                                                  |\n| `tests/fp8/model_hooks.py`                          | `parallel_state`                                                  |\n| `tests/fp8/test_fp8_rmsnorm.py`                     | `fp8_utils`, `get_model_config`                                   |\n| `tests/torchrun/run_megatron_engine_distributed.py` | `parallel_state`                                                  |\n| `tests/test_megatron_engine_vlm.py`                 | `GPTModel` unwrapping and VLM engine contracts                    |\n| `tests/test_megatron_lm_head.py`                    | TP layers, `TransformerConfig`, `Float16Module`, PP helpers       |\n| `tests/test_reassemble_cp_logprobs.py`              | Conditional `megatron.core` availability                          |\n| `tests/torchrun/run_vocab_parallel.py`              | `parallel_state`, `ColumnParallelLinear`, `TransformerConfig`     |\n| `areal/tools/validation_base.py`                    | Megatron package/import metadata validation                       |\n| `areal/tools/validate_docker_installation.py`       | Megatron package and submodule smoke imports                      |\n| `tests/test_bailing_v3_hf_load.py`                  | Stubbed `parallel_state` and FP8 import contracts                 |\n| `tests/test_bailing_v3_kda_cp_helpers.py`           | Stubbed KDA checkpoint, packed-sequence, TP, and transformer APIs |\n| `tests/test_bailing_v3_nccl.py`                     | Conditional real MCore integration                                |\n| `tests/test_deterministic_prebuild.py`              | Conditional `AttnBackend` integration                             |\n| `tests/test_megatron_async_save.py`                 | Stubbed async distributed-checkpoint contracts                    |\n| `tests/test_megatron_bridge_deterministic.py`       | Conditional MCore bridge-provider integration                     |\n| `tests/test_megatron_engine.py`                     | Megatron engine integration and package version reporting         |\n| `tests/test_megatron_transport.py`                  | Conditional Megatron engine transport integration                 |\n| `tests/torchrun/run_mopd_teacher_residency.py`      | Patched MCore DDP flat-buffer residency                           |\n\n______________________________________________________________________\n\n## API Usage Catalog\n\nFor each function/class below, verify the call signature against the upstream source at\nthe target version. Focus on: **missing new required parameters**, **removed old\nparameters**, **renamed parameters**, **changed return types**, **changed method\nsignatures on returned objects**, and **moved/renamed modules**.\n\n### 1. `megatron.core.parallel_state.initialize_model_parallel`\n\n**Source:** `megatron/core/parallel_state.py`\n\nCalled in `areal/engine/megatron_engine.py` (line 195):\n\n```python\nmpu.initialize_model_parallel(\n    tensor_model_parallel_size=self.parallel_strategy.tensor_parallel_size,\n    pipeline_model_parallel_size=self.parallel_strategy.pipeline_parallel_size,\n    virtual_pipeline_model_parallel_size=vpp_size if vpp_size > 1 else None,\n    use_sharp=False,\n    order=\"tp-cp-ep-dp-pp\",\n    context_parallel_size=self.parallel_strategy.context_parallel_size,\n    expert_model_parallel_size=self.parallel_strategy.expert_parallel_size,\n    expert_tensor_parallel_size=self.parallel_strategy.expert_tensor_parallel_size,\n    distributed_timeout_minutes=int(DIST_GROUP_DEFAULT_TIMEOUT.seconds / 60),\n)\n```\n\n**Check:** Verify all keyword arguments still accepted — especially\n`expert_tensor_parallel_size` (recently added) and `distributed_timeout_minutes`. This\nis the most critical call — any signature change blocks ALL Megatron training. Confirm\n`order` string format `\"tp-cp-ep-dp-pp\"`.\n\n______________________________________________________________________\n\n### 2. `megatron.core.parallel_state` getter functions\n\n**Source:** `megatron/core/parallel_state.py`\n\nCalled extensively in `areal/engine/megatron_engine.py`, `areal/models/mcore/*.py`:\n\n```python\nmpu.get_data_parallel_rank()\nmpu.get_data_parallel_world_size()\nmpu.get_data_parallel_group()\nmpu.get_tensor_model_parallel_rank()\nmpu.get_tensor_model_parallel_world_size()\nmpu.get_tensor_model_parallel_group()\nmpu.get_pipeline_model_parallel_rank()\nmpu.get_pipeline_model_parallel_world_size()\nmpu.get_pipeline_model_parallel_group()\nmpu.get_context_parallel_rank()\nmpu.get_context_parallel_world_size()\nmpu.get_context_parallel_group()\nmpu.get_expert_model_parallel_rank()\nmpu.get_expert_model_parallel_world_size()\nmpu.is_pipeline_last_stage()\nmpu.is_pipeline_first_stage()\nmpu.model_parallel_is_initialized()\nmpu.destroy_model_parallel()\n```\n\n**Check:** Confirm all getter functions still exist with same no-arg signatures. These\nare called dozens of times across the codebase — any removal or rename is immediately\nfatal.\n\n______________________________________________________________________\n\n### 3. `megatron.core.parallel_state` utility functions\n\n**Source:** `megatron/core/parallel_state.py`\n\nCalled in `areal/engine/megatron_engine.py` (line 1048):\n\n```python\nrank_generator = mpu.RankGenerator(\n    tp=self.parallel_strategy.tensor_parallel_size,\n    ep=1,\n    dp=self.parallel_strategy.data_parallel_size,\n    pp=self.parallel_strategy.pipeline_parallel_size,\n    cp=self.parallel_strategy.context_parallel_size,\n    order=\"tp-cp-ep-dp-pp\",\n    rank_offset=0,\n)\ncontext_and_model_parallel_ranks = rank_generator.get_ranks(\"tp-cp-pp\")\n\ngroup = mpu.create_group(\n    ranks,\n    timeout=DIST_GROUP_DEFAULT_TIMEOUT,\n    pg_options=mpu.get_nccl_options(\"tp-cp-pp\", {}),\n    group_desc=\"CONTEXT_AND_MODEL_PARALLEL_GROUP\",\n)\n```\n\n**Check:** Verify `RankGenerator.__init__` keyword args — especially `rank_offset` and\n`order`. Confirm `get_ranks(group_str)` method. Verify `create_group` accepts `timeout`,\n`pg_options`, `group_desc` kwargs. Confirm `get_nccl_options(group_name, config_dict)`\nsignature.\n\n______________________________________________________________________\n\n### 4. `megatron.core.distributed.DistributedDataParallel`\n\n**Source:** `megatron/core/distributed/`\n\nCalled in `areal/models/mcore/registry.py` (line 310):\n\n```python\nfrom megatron.core.distributed import DistributedDataParallel as DDP\nfrom megatron.core.distributed import DistributedDataParallelConfig as MCoreDDPConfig\n\nddp_config = MCoreDDPConfig(**dataclasses.asdict(mcore_config.ddp))\nwrapped = DDP(\n    config=tf_config,\n    ddp_config=ddp_config,\n    module=model,\n    disable_bucketing=False,\n)\n```\n\n**Check:** Verify keyword arguments `config`, `ddp_config`, `module`,\n`disable_bucketing` are still accepted. Note AReaL does NOT pass `data_parallel_group`\nor `expert_data_parallel_group` — confirm these remain optional with sane defaults.\nCheck for new required parameters. Also verify `MCoreDDPConfig` dataclass fields haven't\nchanged (constructed from `mcore_config.ddp`).\n\n______________________________________________________________________\n\n### 5. `megatron.core.distributed.finalize_model_grads`\n\n**Source:** `megatron/core/distributed/`\n\nAssigned in `areal/engine/megatron_engine.py` (line 403):\n\n```python\nmodel_config.finalize_model_grads_func = finalize_model_grads\n```\n\nNot called directly by AReaL — instead assigned to\n`TransformerConfig.finalize_model_grads_func` and invoked internally by Megatron's\npipeline parallel forward-backward functions.\n\n**Check:** Confirm `finalize_model_grads` still has a compatible signature for\n`TransformerConfig.finalize_model_grads_func`. Verify it accepts `(model_chunks)` or\n`(model_chunks, num_tokens=...)` as expected by the pipeline executor.\n\n______________________________________________________________________\n\n### 6. `megatron.core.optimizer.OptimizerConfig`\n\n**Source:** `megatron/core/optimizer/`\n\nCalled in `areal/engine/megatron_engine.py` (line 1090):\n\n```python\nmcore_opt_config = MCoreOptimizerConfig(\n    optimizer=self.optimizer_config.type,\n    lr=self.optimizer_config.lr,\n    min_lr=self.optimizer_config.min_lr_ratio * self.optimizer_config.lr,\n    weight_decay=self.optimizer_config.weight_decay,\n    bf16=self.dtype is torch.bfloat16,\n    fp16=self.dtype is torch.float16,\n    adam_beta1=self.optimizer_config.beta1,\n    adam_beta2=self.optimizer_config.beta2,\n    adam_eps=self.optimizer_config.eps,\n    use_distributed_optimizer=use_distributed_optimizer,\n    params_dtype=self.dtype,\n    clip_grad=self.optimizer_config.gradient_clipping,\n    fp8_recipe=(self.fp8_config.recipe if self.enable_fp8 else None),\n)\n# Post-init field assignments:\nmcore_opt_config.overlap_param_gather_with_optimizer_step = ...\nmcore_opt_config.use_precision_aware_optimizer = ...\nmcore_opt_config.main_grads_dtype = getattr(torch, ...)\nmcore_opt_config.main_params_dtype = getattr(torch, ...)\nmcore_opt_config.exp_avg_dtype = getattr(torch, ...)\nmcore_opt_config.exp_avg_sq_dtype = getattr(torch, ...)\n```\n\n**Check:** Verify all ~13 constructor kwargs still accepted — especially `fp8_recipe`\nand `fp16`. Confirm the 6 post-init fields (`overlap_param_gather_with_optimizer_step`,\n`use_precision_aware_optimizer`, `main_grads_dtype`, `main_params_dtype`,\n`exp_avg_dtype`, `exp_avg_sq_dtype`) still exist on the dataclass. Check for renamed or\nremoved fields.\n\n______________________________________________________________________\n\n### 7. `megatron.core.optimizer.get_megatron_optimizer`\n\n**Source:** `megatron/core/optimizer/`\n\nCalled in `areal/engine/megatron_engine.py` (line 1122):\n\n```python\nself.optimizer = get_megatron_optimizer(mcore_opt_config, self.model)\n```\n\n**Check:** Verify the two-arg call `(config, model_chunks)` is still valid. Confirm\n`model_chunks` accepts a list of DDP-wrapped modules. Check if new required arguments\nwere added (e.g., `no_weight_decay_cond`, `scale_lr_cond` — AReaL does not pass these,\nso confirm they remain optional).\n\n______________________________________________________________________\n\n### 8. `megatron.core.optimizer_param_scheduler.OptimizerParamScheduler`\n\n**Source:** `megatron/core/optimizer_param_scheduler.py`\n\nCalled in `areal/engine/megatron_engine.py` (line 1126):\n\n```python\nlr_scheduler = OptimizerParamScheduler(\n    self.optimizer,\n    init_lr=0.0 if warmup_steps_proportion > 0 else self.optimizer_config.lr,\n    max_lr=self.optimizer_config.lr,\n    min_lr=self.optimizer_config.min_lr_ratio * self.optimizer_config.lr,\n    lr_warmup_steps=warmup_steps,\n    lr_decay_steps=ft_spec.total_train_steps - warmup_steps,\n    lr_decay_style=self.optimizer_config.lr_scheduler_type,\n    start_wd=self.optimizer_config.weight_decay,\n    end_wd=self.optimizer_config.weight_decay,\n    wd_incr_steps=ft_spec.total_train_steps,\n    wd_incr_style=\"constant\",\n)\n```\n\n**Check:** Confirm all keyword arguments still accepted — especially `start_wd`,\n`end_wd`, `wd_incr_steps`, `wd_incr_style` (weight decay scheduling). Verify\n`lr_decay_style` valid choices (e.g., `\"cosine\"`, `\"linear\"`, `\"WSD\"`). Check for\nrenamed params.\n\n______________________________________________________________________\n\n### 9. `megatron.core.pipeline_parallel.get_forward_backward_func`\n\n**Source:** `megatron/core/pipeline_parallel/`\n\nCalled in `areal/engine/megatron_engine.py` (line 732, hot training loop):\n\n```python\nforward_backward_func = get_forward_backward_func()\nforward_backward_func(\n    forward_step_func=forward_step,\n    data_iterator=data_iterator,\n    model=self.model if len(self.model) > 1 else self.model[0],\n    num_microbatches=len(mb_list),\n    seq_length=mb_list.max_seqlen,\n    micro_batch_size=1,\n    forward_only=forward_only,\n)\n```\n\n**Check:** Verify `get_forward_backward_func()` still returns a callable with this\nsignature. Confirm `seq_length` and `micro_batch_size` kwargs are still accepted (marked\n\"no use when input_shapes was set\" in code). This is in the HOT training loop — any\nbreakage stops all PP training.\n\n______________________________________________________________________\n\n### 10. `megatron.core.transformer.TransformerConfig`\n\n**Source:** `megatron/core/transformer/transformer_config.py`\n\nUsed in `areal/engine/megatron_engine.py`,\n`areal/engine/megatron_utils/pipeline_parallel.py`, `areal/models/mcore/registry.py`,\n`areal/models/mcore/common.py`, `areal/models/mcore/qwen3.py`:\n\n```python\ntransformer_config = TransformerConfig(\n    num_layers=num_layers,\n    hidden_size=hidden_size,\n    num_attention_heads=num_heads,\n    num_key_value_heads=num_kv_heads,\n    # ... many more fields\n)\n```\n\n**Check:** This is the central config dataclass — verify no fields were removed or\nrenamed. New optional fields with defaults are fine. Check `MLATransformerConfig`\nsubclass compatibility (used in `bailing_moe.py` and `bailing_moe_bridge.py`).\n\n______________________________________________________________________\n\n### 11. `megatron.core.dist_checkpointing` save/load\n\n**Source:** `megatron/core/dist_checkpointing/`\n\nCalled in `areal/engine/megatron_utils/checkpointer.py` (lines 23-32, 53-85):\n\n```python\nfrom megatron.core import dist_checkpointing\nfrom megatron.core.dist_checkpointing.mapping import ShardedObject\nfrom megatron.core.dist_checkpointing.serialization import (\n    get_default_save_sharded_strategy,\n    get_default_load_sharded_strategy,\n)\nfrom megatron.core.dist_checkpointing.strategies.fully_parallel import (\n    FullyParallelLoadStrategyWrapper,\n    FullyParallelSaveStrategyWrapper,\n)\n\n# Save (line 62):\nsave_strategy = get_default_save_sharded_strategy(\"torch_dist\")\nsave_strategy = FullyParallelSaveStrategyWrapper(save_strategy, dp_group)\ndist_checkpointing.save(\n    sharded_state_dict, ckpt_path,\n    sharded_strategy=save_strategy,\n    async_sharded_save=async_save,\n    validate_access_integrity=validate_sharding_integrity,\n)\n\n# Load (line 75):\nload_strategy = get_default_load_sharded_strategy(ckpt_dir)\nload_strategy = FullyParallelLoadStrategyWrapper(load_strategy, dp_group)\nstate_dict = dist_checkpointing.load(\n    sharded_state_dict, ckpt_dir, sharded_strategy=load_strategy\n)\n```\n\n**Check:** Verify `save` kwargs — especially `async_sharded_save` and\n`validate_access_integrity`. Confirm `get_default_save_sharded_strategy` accepts\n`\"torch_dist\"` string. Confirm `get_default_load_sharded_strategy` accepts `ckpt_dir`\npath. Verify import paths: `serialization` for strategy factories,\n`strategies.fully_parallel` for wrappers, `mapping` for `ShardedObject`. Checkpoint\nformat compatibility is critical — verify no format version bumps.\n\n______________________________________________________________________\n\n### 12. `megatron.core.transformer.PipelineParallelLayerLayout`\n\n**Source:** `megatron/core/transformer/pipeline_parallel_layer_layout.py`\n\nCalled in `areal/engine/megatron_utils/pipeline_parallel.py` (line 6):\n\n```python\nfrom megatron.core.transformer.pipeline_parallel_layer_layout import (\n    PipelineParallelLayerLayout,\n)\n```\n\n**Check:** Confirm the import path is still\n`megatron.core.transformer.pipeline_parallel_layer_layout` (not\n`transformer_config.py`). Verify constructor kwargs. Check if\n`get_num_layers_to_build()` and `get_transformer_layer_offset()` are still accessible.\n\n______________________________________________________________________\n\n### 13. `megatron.core.fp8_utils.is_float8tensor`\n\n**Source:** `megatron/core/fp8_utils.py`\n\nCalled in `areal/engine/megatron_utils/fp8/tensor_helper.py`:\n\n```python\nfrom megatron.core.fp8_utils import is_float8tensor\nif is_float8tensor(param):\n    ...\n```\n\n**Check:** Confirm function still exists at this path. Verify it accepts a single tensor\nargument. Check if FP8 support was restructured (e.g., moved to\n`megatron.core.extensions.transformer_engine`).\n\n______________________________________________________________________\n\n### 14. `megatron.core.models.gpt.GPTModel`\n\n**Source:** `megatron/core/models/gpt/`\n\nCalled in `areal/models/mcore/registry.py`:\n\n```python\nmodel = GPTModel(\n    config=transformer_config,\n    transformer_layer_spec=layer_spec,\n    vocab_size=vocab_size,\n    max_sequence_length=max_seq_len,\n    pre_process=pre_process,\n    post_process=post_process,\n)\n```\n\n**Check:** Verify constructor kwargs. Confirm `get_gpt_decoder_block_spec()` still\nreturns a valid layer spec. Check for new required parameters.\n\n______________________________________________________________________\n\n### 15. `megatron.core.tensor_parallel` utilities\n\n**Source:** `megatron/core/tensor_parallel/`\n\nCalled in `areal/engine/megatron_engine.py` and\n`areal/models/mcore/lightning_attention.py`; extended by\n`areal/models/mcore/vocab_parallel_head.py`:\n\n```python\ntensor_parallel.model_parallel_cuda_manual_seed(seed)\ntensor_parallel.gather_from_sequence_parallel_region(output)\ntensor_parallel.get_cuda_rng_tracker()\nlinear_with_areal_output(..., gtp_remat_size=gtp_remat_size)\n```\n\n**Check:** Verify these functions still exist with same signatures.\n`model_parallel_cuda_manual_seed` is called once at init;\n`gather_from_sequence_parallel_region` is in hot path. Keep the custom LM-head autograd\nfunction's forward/backward arity aligned with\n`LinearWithGradAccumulationAndAsyncCommunication`, including `gtp_remat_size`.\n\n______________________________________________________________________\n\n### 16. Transformer layer specs and module builders\n\n**Source:** `megatron/core/transformer/`\n\nCalled in `areal/models/mcore/bailing_moe.py`,\n`areal/models/mcore/lightning_attention.py`,\n`areal/models/mcore/tree_attn/module_megatron.py`:\n\n```python\nfrom megatron.core.transformer import ModuleSpec, build_module\nfrom megatron.core.transformer.enums import AttnMaskType, LayerType\nfrom megatron.core.transformer.transformer_layer import (\n    TransformerLayer, TransformerLayerSubmodules,\n)\nfrom megatron.core.transformer.transformer_block import TransformerBlockSubmodules\nfrom megatron.core.transformer.attention import SelfAttention\nfrom megatron.core.transformer.custom_layers.transformer_engine import (\n    # TE norm modules\n)\n```\n\n**Check:** Verify enum values in `AttnMaskType` and `LayerType`. Confirm `ModuleSpec`\nand `build_module` signatures. Check `SelfAttention.__init__` for new required params.\nVerify TE integration module paths haven't been restructured.\n\n______________________________________________________________________\n\n### 17. `PackedSeqParams`\n\n**Source:** `megatron/core/packed_seq_params.py` (or `megatron/core/transformer/`)\n\nCalled in `areal/engine/megatron_utils/packed_context_parallel.py`:\n\n```python\nfrom megatron.core.packed_seq_params import PackedSeqParams\npacked_seq_params = PackedSeqParams(\n    cu_seqlens_q=cu_seqlens_q,\n    cu_seqlens_kv=cu_seqlens_kv,\n    max_seqlen_q=max_seqlen_q,\n    max_seqlen_kv=max_seqlen_kv,\n    qkv_format=\"thd\",\n)\n```\n\n**Check:** Confirm constructor kwargs and import path. Verify `qkv_format` valid\nchoices.\n\n______________________________________________________________________\n\n### 18. RoPE utilities\n\n**Source:** `megatron/core/models/common/embeddings/`\n\nCalled in `areal/models/mcore/bailing_moe.py`,\n`areal/models/mcore/lightning_attention.py`:\n\n```python\nfrom megatron.core.models.common.embeddings.rotary_pos_embedding import apply_rotary_pos_emb\n# Also various rope_utils for extended RoPE (YaRN, etc.)\n```\n\n**Check:** Verify `apply_rotary_pos_emb` signature (`t`, `freqs`) and the optional MLA\nrotary arguments. MCore 0.19 owns THD+CP frequency selection; AReaL must not reinstall\nthe removed global MLA RoPE workaround.\n\n______________________________________________________________________\n\n### 19. Async distributed-checkpoint request and queue internals\n\n**Source:** `megatron/core/dist_checkpointing/strategies/async_utils.py`\n\nUsed in `areal/engine/megatron_utils/checkpointer.py`:\n\n```python\nfrom megatron.core.dist_checkpointing.strategies.async_utils import (\n    AsyncCallsQueue,\n    AsyncRequest,\n)\n\nsave_kwargs[\"async_strategy\"] = \"mcore\"\nasync_request = dist_checkpointing.save(**save_kwargs)\nqueue = AsyncCallsQueue()\nqueue.schedule_async_request(async_request)\n```\n\nThe checkpoint manager also validates and clears the retained tensor payload through the\nprivate `AsyncRequest.async_fn_args` write-bucket layout after D2H staging.\n\n**Check:** Confirm `AsyncRequest` and `AsyncCallsQueue` import paths and methods. Verify\n`dist_checkpointing.save(..., async_strategy=\"mcore\")` still returns an `AsyncRequest`,\nand audit the exact `async_fn_args` / write-bucket payload structure before retaining\nthe private-reference cleanup optimization.\n\n______________________________________________________________________\n\n### 20. MTP model, loss, and metrics APIs\n\n**Source:** `megatron/core/models/gpt/gpt_model.py` and\n`megatron/core/transformer/multi_token_prediction.py`\n\nConfigured in `areal/models/mcore/registry.py`, forwarded by\n`areal/engine/megatron_utils/packed_context_parallel.py`, and collected in\n`areal/engine/megatron_engine.py`:\n\n```python\nprovider.mtp_detach_heads = True\nmodel(..., labels=None, loss_mask=mtp_loss_mask)\nMTPLossLoggingHelper.reduce_metrics_in_tracker()\nloss = MTPLossLoggingHelper.tracker[\"loss_values\"]\nMTPLossLoggingHelper.clean_metrics_in_tracker()\n```\n\n**Check:** Verify `GPTModel.forward` forwards `input_ids` and `loss_mask` to native\n`process_mtp_loss` when `labels=None`; `mtp_detach_heads` must isolate both hidden state\nand tied/untied output weights. Verify the `MTPLossLoggingHelper` tracker keys and\nreduce/clean method names. These are semi-private contracts and changed in 0.19.\n\n______________________________________________________________________\n\n### 21. MLA configuration, attention backend, and layer-layout helpers\n\n**Source:** `megatron/core/transformer/multi_latent_attention.py`,\n`megatron/core/transformer/enums.py`, `transformer_block.py`, and `transformer_layer.py`\n\nUsed by the Bailing model family and pipeline layout helpers:\n\n```python\nfrom megatron.core.transformer.multi_latent_attention import (\n    MLASelfAttention,\n    MLATransformerConfig,\n)\nfrom megatron.core.transformer.enums import AttnBackend, LayerType\nfrom megatron.core.transformer.transformer_block import get_num_layers_to_build\nfrom megatron.core.transformer.transformer_layer import get_transformer_layer_offset\n```\n\n**Check:** Confirm imports and constructor signatures, enum members used by AReaL, and\nthe helper return types. Verify `MLASelfAttention` still exposes the overridable methods\nconsumed by `bailing_v3_mla.py`.\n\n______________________________________________________________________\n\n### 22. Custom attention module and sharded-state helpers\n\n**Source:** `megatron/core/dist_checkpointing/mapping.py`,\n`megatron/core/tensor_parallel/mappings.py`, and `megatron/core/transformer/`\n\nUsed by `areal/models/mcore/kda_attention.py` and the custom attention modules:\n\n```python\nfrom megatron.core.dist_checkpointing import ShardedTensor\nfrom megatron.core.dist_checkpointing.mapping import ReplicaId, ShardedTensorFactory\nfrom megatron.core.tensor_parallel.mappings import (\n    gather_from_tensor_model_parallel_region,\n    scatter_to_sequence_parallel_region,\n)\nfrom megatron.core.transformer.spec_utils import ModuleSpec, build_module\nfrom megatron.core.transformer.utils import (\n    make_sharded_tensors_for_checkpoint,\n    sharded_state_dict_default,\n)\n```\n\n**Check:** Verify import paths, factory/dataclass fields, TP collective signatures, and\nthe sharded-state helper inputs/returns. Confirm the optional\n`PackedSeqParamsWithSeqidx` import remains safely absent or compatible upstream.\n\n______________________________________________________________________\n\n### 23. `Float16Module` and pipeline-stage helpers\n\n**Source:** `megatron/core/transformer/module.py` and\n`megatron/core/pipeline_parallel/utils.py`\n\nExercised in `tests/test_megatron_lm_head.py` to preserve output casting:\n\n```python\nmodel = Float16Module(config, module)\noutput = model(fp32_output=False)\n```\n\nThe test also patches `is_pp_first_stage`, `is_pp_last_stage`, `is_vp_first_stage`, and\n`is_vp_last_stage` with their process-group/stage arguments.\n\n**Check:** Verify the `Float16Module` constructor/forward pass-through behavior and the\npipeline helper signatures so the AReAL LM-head output override is still propagated.\n\n______________________________________________________________________\n\n## Version-Guarded Code\n\nNo known version-guarded code exists in AReaL for `megatron-core`. However, the\n`MLATransformerConfig` class used in `bailing_moe.py` and `bailing_moe_bridge.py` is a\nrelatively new addition — confirm it exists in the target version.\n",".agents/skills/upgrade-deps/checklists/peft.md":"---\npackage: peft\ngithub: huggingface/peft\nbranch_template: v${VERSION}\nupstream_paths:\n  - src/peft/peft_model.py\n  - src/peft/tuners/lora/config.py\n  - src/peft/utils/peft_types.py\n---\n\n## Affected Files\n\n### Primary (engine layer — most likely to break)\n\n| File                          | Imports / Usage                            |\n| ----------------------------- | ------------------------------------------ |\n| `areal/engine/fsdp_engine.py` | `LoraConfig`, `TaskType`, `get_peft_model` |\n\n### Secondary (model / infra layer)\n\n| File                                             | Imports / Usage                                                                                                                                                     |\n| ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `areal/engine/megatron_engine.py`                | indirect — consumes PEFT config dict schema (`r`, `lora_alpha`, `target_modules`, `bias`) via `megatron.bridge.peft.lora`; stores in `WeightUpdateMeta.peft_config` |\n| `areal/engine/megatron_utils/megatron_lora.py`   | no direct import — generates `adapter_config.json` in PEFT format; parses PEFT weight key format (`base_model.model.layers.N.self_attn.q_proj.lora_A.weight`)       |\n| `areal/engine/vllm_ext/vllm_worker_extension.py` | indirect — uses `vllm.lora.peft_helper.PEFTHelper`, not direct peft import                                                                                          |\n| `areal/engine/vllm_remote.py`                    | builds HTTP payloads containing `peft_config` dict for distributed LoRA updates                                                                                     |\n| `areal/api/cli_args.py`                          | config fields only: `use_lora`, `lora_rank`, `lora_alpha`, `target_modules`, `peft_type`                                                                            |\n| `areal/api/io_struct.py`                         | `WeightUpdateMeta.peft_config: dict` — schema `{\"r\": rank, \"lora_alpha\": alpha, \"target_modules\": [...], \"bias\": \"none\"}`                                           |\n\n### Tertiary (tests, config)\n\n| File                                               | Imports / Usage                                                                     |\n| -------------------------------------------------- | ----------------------------------------------------------------------------------- |\n| `tests/torchrun/run_fsdp_memory_efficient_lora.py` | integration test with `use_lora=True, lora_rank=8, lora_alpha=16, peft_type=\"lora\"` |\n\n______________________________________________________________________\n\n## API Usage Catalog\n\nFor each function/class below, verify the call signature against the upstream source at\nthe target version. Focus on: **missing new required parameters**, **removed old\nparameters**, **renamed parameters**, **changed return types**, **changed method\nsignatures on returned objects**, and **moved/renamed modules**.\n\n### 1. `peft.LoraConfig` and `peft.TaskType.CAUSAL_LM`\n\n**Source:** `src/peft/tuners/lora/config.py`, `src/peft/utils/peft_types.py`\n\nCalled in `areal/engine/fsdp_engine.py` (`_apply_peft_wrapper`, lines 930–946):\n\n```python\nfrom peft import LoraConfig, TaskType, get_peft_model\n\npeft_config = {\n    \"task_type\": TaskType.CAUSAL_LM,\n    \"r\": config.lora_rank,\n    \"lora_alpha\": config.lora_alpha,\n    \"target_modules\": target_modules,  # str \"all-linear\" or list[str]\n    \"bias\": \"none\",\n}\nif self.config.peft_type == \"lora\":\n    peft_config = LoraConfig(**peft_config)\nelse:\n    raise NotImplementedError()\n```\n\n**Check:** Verify `LoraConfig.__init__` still accepts `task_type`, `r`, `lora_alpha`,\n`target_modules`, and `bias` as keyword arguments with the same types. Check whether any\nnew required parameters were added. Confirm `bias=\"none\"` is still a valid string choice\n(not renamed or replaced with an enum). Confirm `target_modules` still accepts both a\nstring (`\"all-linear\"`) and a `list[str]`. Verify `TaskType` is still an enum importable\nfrom `peft` and `TaskType.CAUSAL_LM` still exists. Verify the class is still importable\ndirectly from `peft` (not moved to `peft.tuners.lora` only).\n\n______________________________________________________________________\n\n### 2. `peft.get_peft_model`\n\n**Source:** `src/peft/peft_model.py`\n\nCalled in `areal/engine/fsdp_engine.py` (`_apply_peft_wrapper`, lines 948–953), after\n`model.enable_input_require_grads()` and before FSDP sharding:\n\n```python\nself.model.enable_input_require_grads()\nself.model = get_peft_model(\n    self.model,\n    peft_config,\n    autocast_adapter_dtype=False,\n)\n```\n\n**Check:** Confirm `get_peft_model` is still exported at the top-level `peft` namespace.\nVerify its signature is `get_peft_model(model, peft_config, ...)` with\n`autocast_adapter_dtype` still an accepted keyword argument (it was added in a mid-cycle\nrelease and could be renamed or removed). Confirm the return type is still a `PeftModel`\nwrapping the original model. Check whether the returned model's `.parameters()`,\n`.named_modules()`, and FSDP-compatibility hooks remain intact — the call site\nimmediately passes the result into FSDP2 sharding.\n\n______________________________________________________________________\n\n### 3. PEFT weight key format and `adapter_config.json` schema (indirectly consumed)\n\n**Source:** `src/peft/tuners/lora/` (weight saving conventions)\n\nParsed in `areal/engine/megatron_utils/megatron_lora.py`\n(`_infer_target_modules_from_adapter_weights`, lines 124–150):\n\n```python\n# Expected key pattern for LoRA weights:\n# \"base_model.model.layers.0.self_attn.q_proj.lora_A.weight\" -> q_proj\n# \"base_model.model.layers.1.mlp.gate_proj.lora_B.weight\"   -> gate_proj\nfor key in weight_keys:\n    key = key.replace(\"base_model.model.\", \"\")\n    if \".lora_A.weight\" in key:\n        module_name = key.replace(\".lora_A.weight\", \"\").split(\".\")[-1]\n    elif \".lora_B.weight\" in key:\n        module_name = key.replace(\".lora_B.weight\", \"\").split(\".\")[-1]\n```\n\nAlso used in `_build_adapter_config_dict` (lines 153–178) to emit an\n`adapter_config.json`:\n\n```python\n{\n    \"base_model_name_or_path\": base_model_name_or_path,\n    \"peft_type\": \"LORA\",\n    \"task_type\": \"CAUSAL_LM\",\n    \"inference_mode\": False,\n    \"r\": peft_config.dim,\n    \"lora_alpha\": peft_config.alpha,\n    \"lora_dropout\": peft_config.dropout,\n    \"target_modules\": target_modules,\n    \"bias\": \"none\",\n    \"fan_in_fan_out\": False,\n    \"modules_to_save\": None,\n    \"init_lora_weights\": True,\n    \"layers_to_transform\": None,\n    \"layers_pattern\": None,\n}\n```\n\n**Check:** Confirm the saved weight key format (`base_model.model.<...>.lora_A.weight` /\n`lora_B.weight`) hasn't changed. Check whether new LoRA variants (e.g.,\n`lora_magnitude_vector` for DoRA) added extra keys that the parser doesn't handle.\nVerify all `adapter_config.json` fields (`peft_type`, `task_type`, `r`, `lora_alpha`,\n`lora_dropout`, `target_modules`, `bias`, `fan_in_fan_out`, etc.) still match what\nPEFT's `LoraConfig.from_pretrained()` expects when loading. This schema is also used for\nvLLM LoRA hot-swap via `WeightUpdateMeta.peft_config`.\n\n______________________________________________________________________\n\n### 4. `WeightUpdateMeta.peft_config` dict schema (cross-engine contract)\n\n**Source:** PEFT conventions (not a direct import, but a schema contract)\n\nPopulated in `areal/engine/megatron_engine.py` (lines 1196–1203) and\n`areal/engine/fsdp_engine.py` (lines 1148–1153):\n\n```python\nmeta.peft_config = {\n    \"r\": self.config.lora_rank,\n    \"lora_alpha\": self.config.lora_alpha,\n    \"target_modules\": target_modules,  # list[str] or str\n    \"bias\": \"none\",\n}\n```\n\nConsumed in `areal/engine/vllm_remote.py` (lines 169–172):\n\n```python\n\"lora_target_modules\": meta.peft_config[\"target_modules\"],\n\"lora_rank\": meta.peft_config[\"r\"],\n\"lora_alpha\": meta.peft_config[\"lora_alpha\"],\n\"lora_bias\": meta.peft_config[\"bias\"],\n```\n\nAnd in `areal/engine/vllm_ext/vllm_worker_extension.py` (lines 243–246) to reconstruct a\n`PEFTHelper` for vLLM's LoRA manager.\n\n**Check:** This dict is the **contract** between AReaL's training engines and inference\nbackends. If PEFT changes its config field names (`r` → `rank`, `lora_alpha` → `alpha`,\netc.), all three sites must be updated in sync. Verify that vLLM's\n`PEFTHelper.from_dict` still accepts the same key names as what PEFT's `LoraConfig`\nuses.\n\n______________________________________________________________________\n\n## Version-Guarded Code\n\nNo known version-guarded code exists in AReaL for `peft`.\n",".agents/skills/upgrade-deps/checklists/sglang.md":"---\npackage: sglang\ngithub: sgl-project/sglang\nbranch_template: v${VERSION}\nupstream_paths:\n  - python/sglang/srt/entrypoints/engine.py\n  - python/sglang/srt/entrypoints/http_server.py\n  - python/sglang/srt/entrypoints/openai/protocol.py\n  - python/sglang/srt/function_call/function_call_parser.py\n  - python/sglang/srt/function_call/core_types.py\n  - python/sglang/srt/managers/scheduler.py\n  - python/sglang/srt/managers/io_struct.py\n  - python/sglang/srt/parser/reasoning_parser.py\n  - python/sglang/srt/server_args.py\n  - python/sglang/srt/utils/__init__.py\n  - python/sglang/srt/utils/common.py\n  - python/sglang/srt/utils/network.py\n  - python/sglang/srt/utils/numa_utils.py\n  - python/sglang/launch_server.py\n---\n\n## Affected Files\n\n### Primary (engine layer — most likely to break)\n\n| File                                                 | Imports / Usage                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |\n| ---------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `areal/engine/sglang_remote.py`                      | HTTP-only (no Python imports). Sends requests to `/generate`, `/load_lora_adapter`, `/update_weights_from_disk`, `/update_weights_from_distributed`, `/init_weights_update_group`, `/pause_generation`, `/continue_generation`, `/health`, `/release_memory_occupation`, `/resume_memory_occupation`. Parses `meta_info`, `finish_reason`, `output_token_logprobs`, `routed_experts` from JSON responses.                                                                                                                   |\n| `areal/experimental/openai/tool_call_parser.py`      | `sglang.srt.entrypoints.openai.protocol.Function`, `sglang.srt.entrypoints.openai.protocol.Tool`, `sglang.srt.function_call.function_call_parser.FunctionCallParser`, `sglang.srt.parser.reasoning_parser.ReasoningParser`                                                                                                                                                                                                                                                                                                  |\n| `areal/v2/inference_service/sglang/launch_server.py` | `sglang.srt.entrypoints.engine.Engine`, `sglang.srt.entrypoints.engine.init_tokenizer_manager`, `sglang.srt.entrypoints.http_server._execute_server_warmup`, `sglang.srt.entrypoints.http_server._setup_and_run_http_server`, `sglang.srt.entrypoints.http_server.app`, `sglang.srt.managers.detokenizer_manager.run_detokenizer_process`                                                                                                                                                                                   |\n| `areal/v2/inference_service/sglang/scheduler.py`     | `sglang.srt.managers.scheduler.Scheduler`, `sglang.srt.managers.scheduler.configure_scheduler`, `sglang.srt.observability.trace.process_tracing_init`, `sglang.srt.observability.trace.trace_set_thread_info`, `sglang.srt.utils.get_bool_env_var`, `sglang.srt.utils.kill_itself_when_parent_died`, `sglang.srt.utils.set_gpu_proc_affinity`, `sglang.srt.utils.numa_utils.get_numa_node_if_available`, `sglang.srt.utils.numa_utils.numa_bind_to_node`, `sglang.srt.environ.envs`, `sglang.utils.get_exception_traceback` |\n| `areal/v2/inference_service/sglang/rpc_proxy.py`     | `sglang.srt.managers.io_struct.RpcReqInput`, `sglang.srt.managers.io_struct.RpcReqOutput`, `sglang.srt.server_args.PortArgs`, `sglang.srt.utils.network.get_zmq_socket`                                                                                                                                                                                                                                                                                                                                                     |\n| `areal/v2/inference_service/sglang/bridge.py`        | HTTP-only (same protocol as `sglang_remote.py`). `/generate` request/response, `/pause_generation`, `/continue_generation`, `/release_memory_occupation`, `/resume_memory_occupation`.                                                                                                                                                                                                                                                                                                                                      |\n\n### Secondary (model / infra layer)\n\n| File                                                  | Imports / Usage                                                                                                                      |\n| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |\n| `areal/infra/launcher/sglang_server.py`               | HTTP-only. Polls `/v1/models` to confirm server readiness. Calls `SGLangConfig.build_cmd()` to construct the server launch command.  |\n| `areal/api/cli_args.py`                               | `SGLangConfig.build_cmd()`, `SGLangConfig.build_cmd_from_args()`, `SGLangConfig.build_args()`. Version check against `0.5.10.post1`. |\n| `areal/trainer/rl_trainer.py`                         | `SGLangConfig.build_args()` to construct `RemoteSGLangEngine`.                                                                       |\n| `areal/infra/launcher/ray.py`                         | `SGLangConfig` import + `to_structured_cfg`.                                                                                         |\n| `areal/infra/launcher/local.py`                       | `SGLangConfig` import + `to_structured_cfg`.                                                                                         |\n| `areal/infra/launcher/slurm.py`                       | `SGLangConfig` import + `to_structured_cfg`.                                                                                         |\n| `areal/v2/inference_service/controller/controller.py` | `SGLangConfig` in gateway controller.                                                                                                |\n| `areal/v2/inference_service/data_proxy/backend.py`    | HTTP protocol implementation for inference service proxy (SGLangBridgeBackend).                                                      |\n\n### Tertiary (tests, config)\n\n| File                                                 | Imports / Usage                                             |\n| ---------------------------------------------------- | ----------------------------------------------------------- |\n| `tests/grpo/test_grpo.py`                            | Test parameterization with `sglang` backend.                |\n| `tests/experimental/openai/test_tool_call_parser.py` | `FunctionCallParser` / `ReasoningParser` integration tests. |\n\n______________________________________________________________________\n\n## API Usage Catalog\n\nFor each function/class below, verify the call signature against the upstream source at\nthe target version. Focus on: **missing new required parameters**, **removed old\nparameters**, **renamed parameters**, **changed return types**, **changed method\nsignatures on returned objects**, and **moved/renamed modules**.\n\n> **Note:** Most sglang integration in AReaL is HTTP-based — the server is launched as a\n> subprocess and communicated with over REST. Only `areal/experimental/openai/` imports\n> the Python SDK directly. For the HTTP-based call sites, the risk is in **JSON field\n> renames** in request payloads and response bodies, not in Python API signature\n> changes.\n\n______________________________________________________________________\n\n### 1. `POST /generate` — request payload\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py` (request handler)\n\nCalled in `areal/engine/sglang_remote.py` (`SGLangBackend.build_generation_request`,\nlines 42–88):\n\n```python\nsample_params = {\n    \"top_p\": gconfig.top_p,\n    \"top_k\": gconfig.top_k,\n    \"max_new_tokens\": gconfig.max_new_tokens,\n    \"temperature\": 0.0 if gconfig.greedy else gconfig.temperature,\n    \"stop_token_ids\": stop_token_ids,\n    \"ignore_eos\": gconfig.ignore_eos,\n    \"skip_special_tokens\": gconfig.skip_special_tokens,\n    \"frequency_penalty\": gconfig.frequency_penalty,\n}\nif stop:\n    sample_params[\"stop\"] = stop\n\npayload = {\n    \"input_ids\": req.input_ids.copy(),\n    \"image_data\": req.image_data,\n    \"sampling_params\": sample_params,\n    \"return_logprob\": True,\n    \"stream\": False,\n}\n# conditionally added: \"return_routed_experts\": True, \"lora_path\": <str>\n```\n\n**Check:** Confirm `input_ids`, `image_data`, `sampling_params`, `return_logprob`,\n`lora_path`, and `return_routed_experts` are still accepted top-level fields. Verify all\n`sampling_params` sub-keys are unchanged (`top_p`, `top_k`, `max_new_tokens`,\n`temperature`, `stop_token_ids`, `ignore_eos`, `skip_special_tokens`,\n`frequency_penalty`, `stop`). Check whether `stream=False` is still the correct way to\nrequest a non-streaming response.\n\n______________________________________________________________________\n\n### 2. `POST /generate` — response parsing\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py` (response schema)\n\nParsed in `areal/engine/sglang_remote.py` (`SGLangBackend.parse_generation_response`,\nlines 90–126):\n\n```python\nmeta_info = response[\"meta_info\"]\nfinish_reason = meta_info[\"finish_reason\"]\nstop_reason = finish_reason[\"type\"]\nstop_message = finish_reason.get(\"message\", \"\")\nrouted_experts = meta_info.get(\"routed_experts\", None)\nif routed_experts is not None:\n    num_sgl_token = meta_info[\"prompt_tokens\"] + meta_info[\"completion_tokens\"] - 1\n    routed_experts = np.frombuffer(\n        pybase64.b64decode(routed_experts.encode(\"utf-8\")), dtype=np.int32\n    ).reshape(num_sgl_token, -1)\noutput_tokens = [x[1] for x in meta_info[\"output_token_logprobs\"]]\noutput_logprobs = [x[0] for x in meta_info[\"output_token_logprobs\"]]\n```\n\n**Check:** Confirm `meta_info` is still a top-level key. Verify `finish_reason` is still\na dict with a `\"type\"` key (not a plain string). Verify `output_token_logprobs` is still\na list of `[logprob, token_id]` pairs. Check that `prompt_tokens` and\n`completion_tokens` are still present when `return_logprob=True`. Confirm\n`routed_experts` is still base64-encoded int32 that can be decoded via\n`pybase64.b64decode` and reshaped to `(num_tokens, num_layers*expert_top_k)`.\n\n______________________________________________________________________\n\n### 3. `POST /load_lora_adapter` — request payload\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py`\n\nCalled in `areal/engine/sglang_remote.py`\n(`SGLangBackend.build_disk_weight_update_requests`, lines 139–144):\n\n```python\nHttpRequest(\n    endpoint=\"/load_lora_adapter\",\n    payload={\"lora_name\": lora_name, \"lora_path\": str(meta.path)},\n)\n```\n\n**Check:** Confirm the endpoint still exists and accepts `lora_name` and `lora_path`.\nCheck whether the endpoint was renamed or merged into another route.\n\n______________________________________________________________________\n\n### 4. `POST /update_weights_from_disk` — request payload\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py`\n\nCalled in `areal/engine/sglang_remote.py`\n(`SGLangBackend.build_disk_weight_update_requests`, lines 148–158):\n\n```python\nHttpRequest(\n    endpoint=\"/update_weights_from_disk\",\n    payload={\n        \"model_path\": str(meta.path),\n        \"abort_all_requests\": True,\n    },\n)\n```\n\n**Check:** Confirm `model_path` and `abort_all_requests` are still valid payload keys.\nCheck whether the endpoint was renamed (e.g., to `/update_weights`).\n\n______________________________________________________________________\n\n### 5. `POST /update_weights_from_distributed` — request payload\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py`\n\nCalled in `areal/engine/sglang_remote.py`\n(`SGLangBackend.build_distributed_weight_update_requests`, lines 173–186):\n\n```python\nHttpRequest(\n    endpoint=\"/update_weights_from_distributed\",\n    payload={\n        \"names\": [pspec.name for pspec in param_specs],\n        \"dtypes\": [pspec.dtype for pspec in param_specs],\n        \"shapes\": [pspec.shape for pspec in param_specs],\n        \"group_name\": meta.nccl_group_name,\n        \"abort_all_requests\": True,\n    },\n)\n```\n\n**Check:** Confirm all five payload keys are unchanged. Verify `dtypes` still accepts\nstring representations of torch dtypes. Check whether `abort_all_requests` is still\nhonored.\n\nAReaL's NEXTN compatibility bridge in\n`areal/v2/inference_service/sglang/mtp_weight_update_bridge.py` additionally depends on\nthe following SGLang 0.5.10.post1 internals:\n\n- `Scheduler.tp_worker.model_runner` is the target `ModelRunner`.\n- Spec v1 exposes the NEXTN draft `ModelRunner` as `Scheduler.draft_worker.model_runner`\n  (`EAGLEWorker`).\n- Spec v2 exposes it as `Scheduler.draft_worker.draft_worker.draft_runner`\n  (`EAGLEWorkerV2` wrapping `EagleDraftWorker`).\n- `ModelRunner._model_update_group`, `ModelRunner.device`, and\n  `ModelRunner.update_weights_from_tensor(named_tensors, load_format)` retain their\n  current semantics.\n- `sglang.srt.weight_sync.tensor_bucket.FlattenedTensorBucket` accepts `named_tensors`,\n  exposes `get_flattened_tensor()`, and reconstructs regular `(name, tensor)` pairs with\n  `reconstruct_tensors()`.\n\n**Check:** Revalidate these private APIs before changing the SGLang pin. The bridge must\nreceive every distributed bucket exactly once, then apply the same named tensor objects\nto draft and target runners. Keep the explicit version guard until this path has been\nrevalidated end to end.\n\n______________________________________________________________________\n\n### 6. `POST /init_weights_update_group` — request payload\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py`\n\nCalled in `areal/engine/sglang_remote.py`\n(`SGLangBackend.build_init_weights_group_request`, lines 188–207):\n\n```python\nHttpRequest(\n    endpoint=\"/init_weights_update_group\",\n    payload={\n        \"master_address\": format_host_for_url(meta.nccl_master_address),\n        \"master_port\": str(meta.nccl_master_port),\n        \"rank_offset\": rank_offset,\n        \"world_size\": gen_parallel.world_size + 1,\n        \"backend\": current_platform.communication_backend,\n        \"group_name\": meta.nccl_group_name,\n    },\n)\n```\n\n**Check:** Confirm all six payload keys are still accepted. Verify `rank_offset`\nsemantics (whether the training-rank offset convention changed). Check `backend`\naccepted values (e.g., `\"nccl\"`, `\"gloo\"`).\n\n______________________________________________________________________\n\n### 7. `POST /pause_generation` and `POST /continue_generation`\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py`\n\nCalled in `areal/engine/sglang_remote.py` (`get_pause_request` line 211,\n`get_resume_request` line 215):\n\n```python\nHttpRequest(endpoint=\"/pause_generation\", payload={})\nHttpRequest(endpoint=\"/continue_generation\", payload={})\n```\n\n**Check:** Confirm both endpoints still exist with these exact names. Check whether they\nnow require any payload fields (e.g., `wait_for_inflight_requests`).\n\n______________________________________________________________________\n\n### 8. `GET /health`\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py`\n\nCalled in `areal/engine/sglang_remote.py` (`get_health_check_request`, line 219):\n\n```python\nHttpRequest(endpoint=\"/health\", payload={}, method=\"GET\")\n```\n\n**Check:** Confirm the endpoint is still a `GET` request and returns a 2xx on success.\nVerify it doesn't require authentication headers.\n\n______________________________________________________________________\n\n### 9. `GET /v1/models` — server readiness poll\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py`\n\nCalled in `areal/infra/launcher/sglang_server.py` (`wait_for_server`, lines 65–84):\n\n```python\nresponse = requests.get(\n    f\"{base_url}/v1/models\",\n    headers={\"Authorization\": \"Bearer None\"},\n)\nif response.status_code == 200:\n    break\n```\n\n**Check:** Confirm `/v1/models` still returns 200 once the server is ready. Check\nwhether the `Authorization: Bearer None` header is still accepted (or if auth\nrequirements changed). If SGLang changes its readiness probe endpoint, update this\npolling logic.\n\n______________________________________________________________________\n\n### 10. `POST /release_memory_occupation` and `POST /resume_memory_occupation`\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py`\n\nCalled in `areal/engine/sglang_remote.py` (`get_offload_request` line 223,\n`get_onload_request` lines 225–234):\n\n```python\n# offload (release GPU memory)\nHttpRequest(endpoint=\"/release_memory_occupation\", payload={})\n\n# onload (resume GPU memory, optional tags for multi-stage resume)\npayload = {\"tags\": tags} if tags is not None else {}\nHttpRequest(endpoint=\"/resume_memory_occupation\", payload=payload)\n```\n\n**Check:** Confirm both endpoints still exist. For `/resume_memory_occupation`, verify\n`tags` is still an optional list of strings (available tags: `\"weights\"`, `\"kv_cache\"`).\nCheck if additional fields are now required for either endpoint.\n\n______________________________________________________________________\n\n### 11. `sglang.srt.entrypoints.openai.protocol.Function` and `.Tool`\n\n**Source:** `python/sglang/srt/entrypoints/openai/protocol.py`\n\nCalled in `areal/experimental/openai/tool_call_parser.py` (`_process_tool_calls_sglang`,\nlines 73–94):\n\n```python\nfrom sglang.srt.entrypoints.openai.protocol import Function as SglFunction\nfrom sglang.srt.entrypoints.openai.protocol import Tool as SglTool\n\nSglTool(\n    type=tool[\"type\"],\n    function=SglFunction(\n        name=tool.get(\"name\"),\n        description=tool.get(\"description\"),\n        parameters=tool.get(\"parameters\"),\n    ),\n)\n# or:\nSglTool(type=tool[\"type\"], function=SglFunction(**tool[\"function\"]))\n```\n\n**Check:** Confirm `Function` and `Tool` are still exported from this module path (not\nmoved to `sglang.srt.protocol` or similar). Verify `Function` still accepts `name`,\n`description`, `parameters`. Verify `Tool` still accepts `type` and `function`. Check\nfor newly required fields.\n\n______________________________________________________________________\n\n### 12. `sglang.srt.function_call.function_call_parser.FunctionCallParser`\n\n**Source:** `python/sglang/srt/function_call/function_call_parser.py`\n\nCalled in `areal/experimental/openai/tool_call_parser.py` (`_process_tool_calls_sglang`,\nlines 96, 105, 109):\n\n```python\nfrom sglang.srt.function_call.function_call_parser import FunctionCallParser\n\nparser_p = FunctionCallParser(tools, tool_call_parser)\n# Public methods called:\nparser_p.has_tool_call(content_text)       # -> bool\ncontent_text, call_info_list = parser_p.parse_non_stream(content_text)\n# call_info_list items expose: call_info.name, call_info.parameters\n```\n\n**Check:** Confirm the module path is unchanged. Verify the constructor still takes\n`(tools, tool_call_parser)` positionally where `tools` is a list of `SglTool` objects.\nConfirm `has_tool_call(text) -> bool` and `parse_non_stream(text) -> (str, list)`\nmethods still exist with the same signatures. Verify returned `call_info` objects still\nexpose `.name` and `.parameters` attributes.\n\n______________________________________________________________________\n\n### 13. `sglang.srt.parser.reasoning_parser.ReasoningParser`\n\n**Source:** `python/sglang/srt/parser/reasoning_parser.py`\n\nCalled in `areal/experimental/openai/tool_call_parser.py` (`_process_tool_calls_sglang`,\nlines 76, 97, 101–102):\n\n```python\nfrom sglang.srt.parser.reasoning_parser import ReasoningParser\n\nreasoning_parser_p = ReasoningParser(reasoning_parser)\n# Accessed attributes:\nreasoning_parser_p.detector.think_start_token\nreasoning_parser_p.detector.think_end_token\n```\n\n**Check:** Confirm the module path is unchanged (not moved to\n`sglang.srt.reasoning_parser`). Verify the constructor still accepts a single parser\nname string. Critically, verify the returned instance still exposes\n`.detector.think_start_token` and `.detector.think_end_token` — these are used to\nextract `<think>` blocks from model output.\n\n______________________________________________________________________\n\n### 14. `SGLangConfig.build_cmd` / CLI flag compatibility\n\n**Source:** `python/sglang/launch_server.py`, `python/sglang/srt/server_args.py`\n\nCalled in `areal/api/cli_args.py` via `SGLangConfig.build_args()` and\n`SGLangConfig.build_cmd()`:\n\n```python\n# build_args() assembles a dict of CLI flags from SGLangConfig fields\n# build_cmd() converts those flags into a subprocess command list\nargs = SGLangConfig.build_args()\n# flags like --model-path, --tp-size, --max-loaded-loras, etc.\n```\n\n**Check:** For each of the ~70 config fields in `SGLangConfig`, confirm the\ncorresponding `sglang` CLI flag still exists with the same name and accepted values. Pay\nspecial attention to any fields that were added in recent releases (e.g.,\n`max_loaded_loras` added in 0.4.10.post2). Check `python/sglang/srt/server_args.py` for\nadded/removed/renamed `argparse` arguments.\n\n______________________________________________________________________\n\n### 15. `Engine._launch_subprocesses` — server lifecycle\n\n**Source:** `python/sglang/srt/entrypoints/engine.py`\n\nCalled in `areal/v2/inference_service/sglang/launch_server.py` (`areal_launch_server`):\n\n```python\nfrom sglang.srt.entrypoints.engine import Engine, init_tokenizer_manager\n\n(\n    tokenizer_manager,\n    template_manager,\n    port_args,\n    scheduler_init_result,\n    subprocess_watchdog,\n) = Engine._launch_subprocesses(\n    server_args=server_args,\n    init_tokenizer_manager_func=init_tokenizer_manager,\n    run_scheduler_process_func=areal_run_scheduler_process,\n    run_detokenizer_process_func=run_detokenizer_process,\n)\n```\n\n**Check:** Confirm `_launch_subprocesses` is still a classmethod. Verify it still\naccepts `run_scheduler_process_func` as a callable parameter for scheduler process\ninjection. Verify the return tuple is\n`(TokenizerManager, TemplateManager, PortArgs, SchedulerInitResult, SubprocessWatchdog)`.\nConfirm `SchedulerInitResult.scheduler_infos` attribute exists.\n\n______________________________________________________________________\n\n### 16. `_setup_and_run_http_server` — HTTP server bootstrap\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py`\n\nCalled in `areal/v2/inference_service/sglang/launch_server.py` (`areal_launch_server`):\n\n```python\nfrom sglang.srt.entrypoints.http_server import (\n    _execute_server_warmup,\n    _setup_and_run_http_server,\n    app,\n)\n\n_setup_and_run_http_server(\n    server_args,\n    tokenizer_manager,\n    template_manager,\n    port_args,\n    scheduler_init_result.scheduler_infos,\n    subprocess_watchdog,\n    execute_warmup_func=_execute_server_warmup,\n)\n```\n\n**Check:** Confirm the function still exists at this module path. Verify signature:\n`(server_args, tokenizer_manager, template_manager, port_args, scheduler_infos, subprocess_watchdog, execute_warmup_func, launch_callback)`.\nConfirm `app` is still the FastAPI instance exported from this module.\n\n______________________________________________________________________\n\n### 17. `Scheduler` constructor and `configure_scheduler`\n\n**Source:** `python/sglang/srt/managers/scheduler.py`\n\nCalled in `areal/v2/inference_service/sglang/scheduler.py`\n(`areal_run_scheduler_process`):\n\n```python\nfrom sglang.srt.managers.scheduler import Scheduler, configure_scheduler\n\ndp_rank = configure_scheduler(\n    server_args, tp_rank, attn_cp_rank, moe_dp_rank, moe_ep_rank, pp_rank, dp_rank\n)\n\nscheduler = Scheduler(\n    server_args, port_args, gpu_id, tp_rank, moe_ep_rank, pp_rank,\n    attn_cp_rank, moe_dp_rank, dp_rank,\n)\npipe_writer.send(scheduler.get_init_info())\nscheduler.run_event_loop()\n```\n\n**Check:** Confirm `configure_scheduler` signature is\n`(server_args, tp_rank, attn_cp_rank, moe_dp_rank, moe_ep_rank, pp_rank, dp_rank)`.\nConfirm `Scheduler.__init__` signature is\n`(server_args, port_args, gpu_id, tp_rank, moe_ep_rank, pp_rank, attn_cp_rank, moe_dp_rank, dp_rank)`.\nVerify `get_init_info()` and `run_event_loop()` still exist. Confirm `tp_rank`,\n`dp_rank`, `tp_cpu_group`, `tp_size` attributes are present on the scheduler instance\n(used by `AwexSchedulerBridge`).\n\n______________________________________________________________________\n\n### 18. `RpcReqInput` / `RpcReqOutput` — scheduler RPC protocol\n\n**Source:** `python/sglang/srt/managers/io_struct.py`\n\nUsed in `areal/v2/inference_service/sglang/rpc_proxy.py`:\n\n```python\nfrom sglang.srt.managers.io_struct import RpcReqInput, RpcReqOutput\n\nreq = RpcReqInput(method=method, parameters=kwargs if kwargs else None)\nself._rpc_socket.send_pyobj(req)\nresp: RpcReqOutput = self._rpc_socket.recv_pyobj()\n# resp.success: bool, resp.message: str\n```\n\n**Check:** Confirm `RpcReqInput` still has `method: str` and\n`parameters: Optional[Dict]` fields. Confirm `RpcReqOutput` still has `success: bool`\nand `message: str` fields. Verify `send_pyobj`/`recv_pyobj` serialization still works\n(both inherit from `BaseReq` which is a dataclass).\n\n______________________________________________________________________\n\n### 19. `sglang.srt.utils` module reorganization (v0.5.10+)\n\n**Source:** `python/sglang/srt/utils/__init__.py`, `python/sglang/srt/utils/common.py`,\n`python/sglang/srt/utils/network.py`, `python/sglang/srt/utils/numa_utils.py`\n\nIn v0.5.10+, `sglang.srt.utils/__init__.py` only re-exports from `common.py` via\n`from sglang.srt.utils.common import *`. Functions in submodules must be imported\ndirectly:\n\n- `get_zmq_socket` → `sglang.srt.utils.network`\n- `get_numa_node_if_available`, `numa_bind_to_node` → `sglang.srt.utils.numa_utils`\n- `kill_itself_when_parent_died`, `get_bool_env_var`, `set_gpu_proc_affinity`,\n  `kill_process_tree` → `sglang.srt.utils` (via `common.py`)\n\n**Check:** On each upgrade, verify `sglang.srt.utils/__init__.py` re-export scope. If\nfunctions move between submodules, update AReaL import paths accordingly.\n\n______________________________________________________________________\n\n## Version-Guarded Code\n\n- `areal/api/cli_args.py:1877-1878` —\n  `if not pkg_version.is_version_greater_or_equal(\"sglang\", \"0.5.10.post1\"): raise RuntimeError(...)`.\n  Minimum floor raised from `0.4.9.post2` to `0.5.10.post1` during this upgrade. The\n  legacy `max_loaded_loras` version guard (`<0.4.10.post2`) was removed because the new\n  floor is well past that version.\n",".agents/skills/upgrade-deps/checklists/torchao.md":"---\npackage: torchao\ngithub: pytorch/ao\nbranch_template: v${VERSION}\nupstream_paths:\n  - torchao/prototype/blockwise_fp8_training/linear.py\n  - torchao/prototype/blockwise_fp8_training/kernels.py\n---\n\n## Affected Files\n\n### Primary (engine layer — most likely to break)\n\n| File                                                      | Imports / Usage                                                                                                                        |\n| --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |\n| `areal/experimental/models/archon/fp8.py`                 | `torchao.prototype.blockwise_fp8_training.linear.fp8_blockwise_mm`; `fp8_blockwise_mm.apply(x, weight, block_size, dtype, use_triton)` |\n| `areal/experimental/models/archon/moe/grouped_experts.py` | `torchao.prototype.blockwise_fp8_training.linear.fp8_blockwise_mm`; `fp8_blockwise_mm.apply(x, w, block_size, dtype, use_triton)`      |\n\n### Secondary (model / infra layer)\n\n| File                                         | Imports / Usage                                                                             |\n| -------------------------------------------- | ------------------------------------------------------------------------------------------- |\n| `areal/api/cli_args.py`                      | `ArchonFP8Config` dataclass — string references to `torchao_config` field; no direct import |\n| `areal/tools/check_pyproject_consistency.py` | `torchao` listed in `ESCAPABLE_PACKAGES` — expected to differ between sglang/vllm variants  |\n\n### Tertiary (tests, config)\n\n| File                                                     | Imports / Usage                                                                                                                                                                                                           |\n| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `tests/experimental/archon/conftest.py`                  | Conditional stub of 5 modules: `torchao`, `torchao.quantization`, `torchao.prototype`, `torchao.prototype.blockwise_fp8_training`, `torchao.prototype.blockwise_fp8_training.linear` when unavailable; SM90+ GPU required |\n| `tests/experimental/archon/fp8/test_fp8_linear.py`       | `import torchao.prototype.blockwise_fp8_training.linear`; forward/backward correctness and convergence tests                                                                                                              |\n| `tests/experimental/archon/fp8/test_fp8_scale_layout.py` | `torchao.prototype.blockwise_fp8_training.kernels.triton_fp8_blockwise_act_quant_lhs`, `triton_fp8_blockwise_weight_quant_transposed_rhs`                                                                                 |\n| `tests/experimental/archon/fp8/test_conversion.py`       | Tests `enable_fp8_linear()` and `enable_fp8_experts()` patching logic                                                                                                                                                     |\n| `tests/experimental/archon/fp8/test_moe_dispatch.py`     | Tests `_run_experts_fp8_for_loop()` with 11 edge cases                                                                                                                                                                    |\n\n______________________________________________________________________\n\n## API Usage Catalog\n\nFor each function/class below, verify the call signature against the upstream source at\nthe target version. Focus on: **missing new required parameters**, **removed old\nparameters**, **renamed parameters**, **changed return types**, **changed method\nsignatures on returned objects**, and **moved/renamed modules**.\n\n> **Warning:** All torchao usage goes through the `prototype` module path. These APIs\n> are explicitly experimental and carry a HIGH risk of breakage between releases.\n\n### 1. `torchao.prototype.blockwise_fp8_training.linear.fp8_blockwise_mm` (HIGH RISK)\n\n**Source:** `torchao/prototype/blockwise_fp8_training/linear.py`\n\nImported inside `areal/experimental/models/archon/fp8.py` `enable_fp8_linear()` (line\n36\\) and stored on each patched module as `mod._fp8_mm`. Called in the patched forward\n(line 156-157):\n\n```python\nfrom torchao.prototype.blockwise_fp8_training.linear import fp8_blockwise_mm\n\n# Inside patched nn.Linear.forward — on-the-fly FP8 quantization\nout = self._fp8_mm.apply(x, weight, self._fp8_block, x.dtype, self._fp8_use_triton)\n# Positional args: (input, weight, block_size=128, dtype, use_triton)\n```\n\nImported inside `areal/experimental/models/archon/moe/grouped_experts.py`\n`_run_experts_fp8_for_loop()` (line 132). Called three times per expert (lines 161-166):\n\n```python\nfrom torchao.prototype.blockwise_fp8_training.linear import fp8_blockwise_mm\n\n# SwiGLU: silu(x @ w1.T) * (x @ w3.T) @ w2.T — all via FP8\nh1 = fp8_blockwise_mm.apply(x_e, w1_e, block_size, x_e.dtype, use_triton)\nh3 = fp8_blockwise_mm.apply(x_e, w3_e, block_size, x_e.dtype, use_triton)\nh = F.silu(h1) * h3\nh2 = fp8_blockwise_mm.apply(h, w2_e, block_size, h.dtype, use_triton)\n```\n\n**Check:** Confirm `fp8_blockwise_mm` is still a `torch.autograd.Function` subclass with\nan `.apply()` entry point. Verify positional argument order\n`(x, weight, block_size, dtype, use_triton)` is unchanged. Check whether `block_size`\nstill accepts an `int` (default 128) and `use_triton` remains a plain `bool`. Confirm\nthe module path `torchao.prototype.blockwise_fp8_training.linear` has not been renamed\nor graduated out of `prototype`.\n\n______________________________________________________________________\n\n### 2. `enable_fp8_linear()` (HIGH RISK — monkey-patches `nn.Linear`)\n\n**Source:** `areal/experimental/models/archon/fp8.py` (AReaL function, not torchao)\n\nDefined at line 18, calls `fp8_blockwise_mm` from torchao internally:\n\n```python\ndef enable_fp8_linear(\n    model: nn.Module,\n    *,\n    exclude_fqns: set[str] | None = None,  # defaults to {\"output\", \"router\", \"score\"}\n    use_triton: bool = True,\n) -> None:\n    from torchao.prototype.blockwise_fp8_training.linear import fp8_blockwise_mm\n    # ... patches eligible nn.Linear modules via _patch_fp8_forward(mod, fp8_blockwise_mm, use_triton)\n```\n\nCalled from test at `tests/experimental/archon/fp8/test_fp8_scale_layout.py` (line 46):\n\n```python\nenable_fp8_linear(model, exclude_fqns=set())\n```\n\n**Check:** This is an AReaL function, but it depends on `fp8_blockwise_mm` from torchao\n(entry 1). If torchao renames or moves `fp8_blockwise_mm`, this function's internal\nimport breaks. Verify `exclude_fqns` and `use_triton` are the only kwargs (no\n`block_size` or `dtype` — block size is hardcoded as `_FP8_BLOCK = 128`).\n\n______________________________________________________________________\n\n### 3. `enable_fp8_experts()` (HIGH RISK — monkey-patches MoE expert modules)\n\n**Source:** `areal/experimental/models/archon/fp8.py` (AReaL function, not torchao)\n\nDefined at line 57:\n\n```python\ndef enable_fp8_experts(model: nn.Module, *, use_triton: bool = True) -> None:\n    # Patches each GroupedExperts module to use _run_experts_fp8_for_loop()\n    # which internally calls fp8_blockwise_mm from torchao\n```\n\n**Check:** This is an AReaL function. Its only torchao dependency is transitive via\n`_run_experts_fp8_for_loop()` → `fp8_blockwise_mm` (entry 1). Verify `use_triton` is the\nonly kwarg (no `block_size` or `dtype` — block size is hardcoded as `_FP8_BLOCK = 128`).\nConfirm it targets `GroupedExperts` submodules specifically.\n\n______________________________________________________________________\n\n### 4. `validate_fp8_shard_alignment()`\n\n**Source:** `areal/experimental/models/archon/fp8.py` (AReaL function, not torchao)\n\nDefined at line 166:\n\n```python\ndef validate_fp8_shard_alignment(\n    model_parts: list[nn.Module],\n    block_size: int = 128,  # _FP8_BLOCK\n) -> None:\n    # Checks both nn.Linear (2D) and GroupedExperts (3D) modules\n    # for block-alignment AFTER parallelism (TP/PP) is applied.\n    # Raises ValueError if local weight dims are not multiples of block_size.\n```\n\n**Check:** This is an AReaL function. It does NOT depend on torchao directly — it only\nchecks that modules already patched with `_fp8_block` attribute have compatible local\nweight shapes. However, if torchao changes `_FP8_BLOCK` convention or the kernel's\nalignment requirements, this validation must be updated to match.\n\n______________________________________________________________________\n\n### 5. `triton_fp8_blockwise_act_quant_lhs` / `triton_fp8_blockwise_weight_quant_transposed_rhs` (HIGH RISK)\n\n**Source:** `torchao/prototype/blockwise_fp8_training/kernels.py`\n\nImported in `tests/experimental/archon/fp8/test_fp8_scale_layout.py` (lines 89-92,\n101-104):\n\n```python\nfrom torchao.prototype.blockwise_fp8_training.kernels import (\n    triton_fp8_blockwise_act_quant_lhs,\n    triton_fp8_blockwise_weight_quant_transposed_rhs,\n)\n\n# Call signatures (lines 101-104):\nx_fp8, x_scale = triton_fp8_blockwise_act_quant_lhs(x, block_size)\nw_t_fp8, w_t_scale = triton_fp8_blockwise_weight_quant_transposed_rhs(w, block_size=block_size)\n\n# Scale shape contract tested:\n# x_scale.shape == (m, k_blocks)\n# w_t_scale.shape == (k_blocks, n_blocks)\n# Both MUST be column-major: stride(0) < stride(1)\n```\n\n**Check:** Confirm both kernel symbols still exist at this path and have not been\nrenamed (e.g., to drop the `triton_` prefix or unify under a single dispatch function).\nVerify return types are `(fp8_tensor, scale_tensor)` tuples. Verify the scale layout\nconvention: column-major strides (`stride(0) < stride(1)`), shape\n`(rows, ceil(cols/block_size))`. Any change in output tensor memory layout silently\nbreaks `fp8_blockwise_mm` which consumes these scales.\n\n______________________________________________________________________\n\n## Version-Guarded Code\n\nNo version-guarded code exists in AReaL for `torchao`. Availability is checked at test\ntime via `try/except` stubs in `tests/experimental/archon/conftest.py` — the stubs cover\nall five affected submodules so tests degrade gracefully on machines without SM90+ GPUs\nor without `torchao` installed.\n",".agents/skills/upgrade-deps/checklists/transformers.md":"---\npackage: transformers\ngithub: huggingface/transformers\nbranch_template: v${VERSION}\nupstream_paths:\n  - src/transformers/models/auto/auto_factory.py\n  - src/transformers/models/auto/configuration_auto.py\n  - src/transformers/configuration_utils.py\n  - src/transformers/tokenization_utils_fast.py\n  - src/transformers/tokenization_utils_base.py\n  - src/transformers/processing_utils.py\n  - src/transformers/optimization.py\n  - src/transformers/modeling_flash_attention_utils.py\n  - src/transformers/integrations/flash_attention.py\n  - src/transformers/models/qwen2_vl/modeling_qwen2_vl.py\n  - src/transformers/models/qwen2_5_vl/\n  - src/transformers/models/qwen3_vl/modeling_qwen3_vl.py\n  - src/transformers/utils/import_utils.py\n---\n\n## Affected Files\n\n### Primary (engine layer — most likely to break)\n\n| File                                         | Imports / Usage                                                                                                                                                                                                                                 |\n| -------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `areal/engine/fsdp_engine.py`                | `AutoConfig`, `AutoModelForCausalLM`, `AutoModelForImageTextToText`, `AutoModelForTokenClassification`, `PretrainedConfig`, `PreTrainedTokenizerFast`, `ProcessorMixin`, `get_linear_schedule_with_warmup`, `get_constant_schedule_with_warmup` |\n| `areal/engine/megatron_engine.py`            | `PretrainedConfig`                                                                                                                                                                                                                              |\n| `areal/engine/fsdp_utils/__init__.py`        | `PreTrainedModel`                                                                                                                                                                                                                               |\n| `areal/engine/fsdp_utils/parallel.py`        | `PretrainedConfig`                                                                                                                                                                                                                              |\n| `areal/experimental/engine/archon_engine.py` | `AutoConfig`, `PretrainedConfig`, `PreTrainedTokenizerFast`                                                                                                                                                                                     |\n\n### Secondary (model / infra layer — HIGH RISK: monkey-patching)\n\n| File                                                                       | Imports / Usage                                                                                                                                                                                                                                                    |\n| -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `areal/models/transformers/ulyssess_patch.py`                              | `transformers.modeling_flash_attention_utils._flash_attention_forward` (direct import); `transformers.integrations.flash_attention._flash_attention_forward` (monkey-patch); dynamic import of `transformers.models.{qwen2_vl,qwen2_5_vl,qwen3_vl}` module strings |\n| `areal/models/transformers/qwen3_vl.py`                                    | `transformers.integrations.flash_attention.flash_attention_forward`; `transformers.models.qwen3_vl.modeling_qwen3_vl.{apply_rotary_pos_emb, repeat_kv}`                                                                                                            |\n| `areal/models/transformers/qwen2_vl.py`                                    | `transformers.integrations.flash_attention.flash_attention_forward`; `transformers.models.qwen2_vl.modeling_qwen2_vl.{apply_multimodal_rotary_pos_emb, repeat_kv}`                                                                                                 |\n| `areal/models/transformers/vision_sp_shard.py`                             | monkey-patches Qwen VL vision modules (internal submodule access)                                                                                                                                                                                                  |\n| `areal/models/tree_attn/module_fsdp.py`                                    | `transformers.integrations.flash_attention._flash_attention_forward` (monkey-patch save/restore)                                                                                                                                                                   |\n| `areal/workflow/rlvr.py`                                                   | `PreTrainedTokenizerFast` (`decode`, `apply_chat_template`, `eos_token_id`)                                                                                                                                                                                        |\n| `areal/workflow/vision_rlvr.py`                                            | `AutoProcessor`, `PreTrainedTokenizerFast`                                                                                                                                                                                                                         |\n| `areal/workflow/multi_turn.py`                                             | `PreTrainedTokenizerFast`                                                                                                                                                                                                                                          |\n| `areal/utils/hf_utils.py`                                                  | `AutoTokenizer.from_pretrained()`, `AutoProcessor.from_pretrained()`                                                                                                                                                                                               |\n| `areal/utils/seeding.py`                                                   | `transformers.set_seed()`                                                                                                                                                                                                                                          |\n| `areal/infra/platforms/__init__.py`                                        | `transformers.utils.import_utils.is_torch_npu_available`                                                                                                                                                                                                           |\n| `areal/infra/rpc/serialization.py`                                         | `AutoTokenizer`, `PreTrainedTokenizer`, `PreTrainedTokenizerFast`, `AutoProcessor`, `ProcessorMixin`                                                                                                                                                               |\n| `areal/models/mcore/registry.py`                                           | `AutoConfig`, `PretrainedConfig`                                                                                                                                                                                                                                   |\n| `areal/models/mcore/bailing_moe.py`                                        | `PretrainedConfig`                                                                                                                                                                                                                                                 |\n| `areal/experimental/models/archon/*/args.py` and `*/state_dict_adapter.py` | `PretrainedConfig`                                                                                                                                                                                                                                                 |\n\n### Tertiary (tests and examples)\n\n| File                                     | Imports / Usage                                                                                                                    |\n| ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |\n| 30+ files under `tests/` and `examples/` | `AutoTokenizer`, `AutoConfig`, `AutoModelForCausalLM`, `PreTrainedTokenizerFast` — standard load patterns only, no monkey-patching |\n\n______________________________________________________________________\n\n## API Usage Catalog\n\nFor each function/class below, verify the call signature against the upstream source at\nthe target version. Focus on: **missing new required parameters**, **removed old\nparameters**, **renamed parameters**, **changed return types**, **changed method\nsignatures on returned objects**, and **moved/renamed modules**.\n\n### 1. Auto\\* model and config classes\n\n**Source:** `src/transformers/models/auto/auto_factory.py`,\n`src/transformers/models/auto/configuration_auto.py`\n\nCalled in `areal/engine/fsdp_engine.py`:\n\n```python\n# Config loading (line 196)\nself.model_config = AutoConfig.from_pretrained(\n    pretrained_model_name_or_path=self.config.path,\n    trust_remote_code=True,\n)\n\n# VLM path (line 899)\nmodel = AutoModelForImageTextToText.from_pretrained(\n    pretrained_model_name_or_path=self.config.path,\n    trust_remote_code=True,\n    dtype=dtype,\n    attn_implementation=self.config.attn_impl,\n)\n\n# LLM path — _create_llm_actor_or_critic (lines 829–856)\nmodel_class = AutoModelForTokenClassification if is_critic else AutoModelForCausalLM\nmodel_kwargs = {\"num_labels\": 1} if is_critic else {}\nmodel_kwargs.update({\"dtype\": dtype, \"attn_implementation\": self.config.attn_impl})\n\n# Branch: from_config (meta-device, memory-efficient) or from_pretrained\nmodel = model_class.from_config(self.model_config, **model_kwargs)\n# or:\nmodel = model_class.from_pretrained(\n    pretrained_model_name_or_path=self.config.path,\n    trust_remote_code=True,\n    **model_kwargs,\n)\n```\n\nAlso called in `areal/utils/hf_utils.py`, `areal/models/mcore/registry.py`,\n`areal/experimental/engine/archon_engine.py`, and\n`areal/experimental/models/archon/*/args.py`.\n\n**Check:** Verify `from_pretrained` / `from_config` keyword arguments (`dtype`,\n`attn_implementation`, `trust_remote_code`, `num_labels`). Check whether\n`AutoModelForImageTextToText` still exists or was renamed/merged. Confirm `from_config`\nstill accepts `num_labels` for `AutoModelForTokenClassification`. Look for new required\nkwargs introduced in the target version.\n\n______________________________________________________________________\n\n### 2. `PretrainedConfig`\n\n**Source:** `src/transformers/configuration_utils.py`\n\nCalled in `areal/engine/fsdp_engine.py`, `areal/engine/megatron_engine.py`,\n`areal/engine/fsdp_utils/parallel.py`, `areal/experimental/engine/archon_engine.py`,\n`areal/models/mcore/registry.py`, and various\n`areal/experimental/models/archon/*/args.py`:\n\n```python\n# Type annotation / isinstance check only\nconfig: PretrainedConfig\n\n# Attribute access (common patterns)\nconfig.model_type\nconfig.num_attention_heads\nconfig.num_key_value_heads\nconfig.text_config.num_attention_heads   # VLM sub-config\n```\n\n**Check:** Confirm `PretrainedConfig` is still the base class at this path (not moved).\nCheck whether `text_config` sub-config attribute access pattern for VLMs is still valid.\nVerify no new required fields were added to `__init__`.\n\n______________________________________________________________________\n\n### 3. `PreTrainedTokenizerFast` and tokenizer methods\n\n**Source:** `src/transformers/tokenization_utils_fast.py`,\n`src/transformers/tokenization_utils_base.py`\n\nCalled in `areal/utils/hf_utils.py` (`load_hf_tokenizer`, lines 12–30):\n\n```python\n@lru_cache(maxsize=8)\ndef load_hf_tokenizer(model_name_or_path, fast_tokenizer=True, padding_side=None):\n    tokenizer = transformers.AutoTokenizer.from_pretrained(\n        model_name_or_path,\n        fast_tokenizer=fast_tokenizer,\n        trust_remote_code=True,\n        force_download=True,\n        **kwargs,  # padding_side if not None\n    )\n    if tokenizer.pad_token_id is None:\n        tokenizer.pad_token_id = tokenizer.eos_token_id\n```\n\nCalled in `areal/workflow/rlvr.py`, `areal/workflow/multi_turn.py`, and\n`areal/workflow/vision_rlvr.py`:\n\n```python\ntokenizer.decode(token_ids, ...)\ntokenizer.apply_chat_template(messages, ...)\ntokenizer.eos_token_id\n```\n\n**Check:** Verify `fast_tokenizer` parameter still exists in\n`AutoTokenizer.from_pretrained` (it was renamed to `use_fast` in some versions — confirm\nwhich name the current version uses). Confirm `apply_chat_template` signature is\nunchanged (especially `tokenize`, `add_generation_prompt`, `return_tensors` kwargs).\nCheck `force_download` is still accepted. Confirm `eos_token_id` and `pad_token_id` are\nstill plain attributes. Note: the function is `@lru_cache`'d — if the return type\nchanges, the cache must be invalidated.\n\n______________________________________________________________________\n\n### 4. `AutoProcessor` / `ProcessorMixin`\n\n**Source:** `src/transformers/processing_utils.py`\n\nCalled in `areal/utils/hf_utils.py` (`load_hf_processor_and_tokenizer`, lines 33–55):\n\n```python\nprocessor = transformers.AutoProcessor.from_pretrained(\n    model_name_or_path,\n    trust_remote_code=True,\n    force_download=True,\n    use_fast=True,\n)\n```\n\nAlso used as type annotation in `areal/infra/rpc/serialization.py` and\n`areal/engine/fsdp_engine.py` (`ProcessorMixin`).\n\n**Check:** Confirm `AutoProcessor.from_pretrained` still accepts `use_fast`. Verify\n`ProcessorMixin` is still the importable base class at `transformers.ProcessorMixin`.\nCheck for signature changes on processor call methods (image/text preprocessing) if\n`vision_rlvr.py` calls them directly.\n\n______________________________________________________________________\n\n### 5. LR scheduler helpers\n\n**Source:** `src/transformers/optimization.py`\n\nCalled in `areal/engine/fsdp_engine.py` (lines 1017–1026):\n\n```python\n# \"linear\" scheduler (line 1017)\nself.lr_scheduler = get_linear_schedule_with_warmup(\n    self.optimizer,\n    num_warmup_steps,\n    total_train_steps,\n)\n\n# \"constant\" scheduler (line 1023)\nself.lr_scheduler = get_constant_schedule_with_warmup(\n    self.optimizer,\n    num_warmup_steps,\n)\n```\n\n**Check:** Confirm both functions still exist in `transformers.optimization` (they have\noccasionally been soft-deprecated in favor of `get_scheduler`). Verify positional\nargument order (`optimizer`, `num_warmup_steps`, `num_training_steps`) is unchanged.\nCheck return type is still a `LambdaLR`-compatible scheduler.\n\n______________________________________________________________________\n\n### 6. `transformers.set_seed`\n\n**Source:** `src/transformers/trainer_utils.py` (re-exported from top-level)\n\nCalled in `areal/utils/seeding.py` (line 28):\n\n```python\nimport transformers\ntransformers.set_seed(seed)\n```\n\n**Check:** Confirm `set_seed` is still exported at `transformers.set_seed` (top-level\nre-export). Verify signature is `set_seed(seed: int)` with no new required parameters.\n\n______________________________________________________________________\n\n### 7. `transformers.modeling_flash_attention_utils._flash_attention_forward` (HIGH RISK)\n\n**Source:** `src/transformers/modeling_flash_attention_utils.py`\n\nCalled in `areal/models/transformers/ulyssess_patch.py` (import line 6, usage line 57):\n\n```python\nfrom transformers.modeling_flash_attention_utils import _flash_attention_forward\n\n# Called inside _ulysses_flash_attention_forward as a pass-through (line 57):\nattn_output = _flash_attention_forward(\n    query_states, key_states, value_states, *args, **kwargs,\n)\n```\n\n**Check:** This is a **private** function (leading underscore). Verify it still exists\nat this exact import path. Confirm its positional signature\n`(query_states, key_states, value_states, ...)` is unchanged. Any argument reorder or\nrename breaks all Ulysses sequence parallel attention paths. Note: this is used as the\n_real_ flash attention call inside the Ulysses wrapper — it's the only place the actual\nflash attention kernel is invoked.\n\n______________________________________________________________________\n\n### 8. `transformers.integrations.flash_attention._flash_attention_forward` (HIGH RISK — monkey-patched)\n\n**Source:** `src/transformers/integrations/flash_attention.py`\n\nMonkey-patched in `areal/models/transformers/ulyssess_patch.py` (lines 244–246) and\n`areal/models/tree_attn/module_fsdp.py` (lines 163–166, 182–185):\n\n```python\n# ulyssess_patch.py (line 244) — for non-VLM models\nfrom transformers.integrations import flash_attention\nflash_attention._flash_attention_forward = _ulysses_flash_attention_forward\n\n# module_fsdp.py (lines 163-166) — save and replace for tree attention\nORIGINAL_FLASH_ATTENTION_FORWARD = flash_attention._flash_attention_forward\nflash_attention._flash_attention_forward = _tree_attn_fwd_func\n\n# module_fsdp.py (lines 182-185) — restore original after use\nflash_attention._flash_attention_forward = ORIGINAL_FLASH_ATTENTION_FORWARD\nORIGINAL_FLASH_ATTENTION_FORWARD = None\n```\n\n**Check:** Confirm `transformers.integrations.flash_attention` module still exists and\nstill has a `_flash_attention_forward` attribute. If the module was renamed, moved, or\nthe function removed, ALL of the Ulysses and tree-attention patches silently break. Also\nverify `flash_attention_forward` (public, **no** underscore) still exists — it is\nimported directly in `areal/models/transformers/qwen2_vl.py` (line 7) and\n`areal/models/transformers/qwen3_vl.py` (line 12) via\n`from transformers.integrations.flash_attention import flash_attention_forward`.\n\n______________________________________________________________________\n\n### 9. Qwen2-VL internal symbols (HIGH RISK — imported from private modules)\n\n**Source:** `src/transformers/models/qwen2_vl/modeling_qwen2_vl.py`\n\nImported in `areal/models/transformers/qwen2_vl.py` (lines 8–11):\n\n```python\nfrom transformers.models.qwen2_vl.modeling_qwen2_vl import (\n    apply_multimodal_rotary_pos_emb,\n    repeat_kv,\n)\n```\n\nAlso accessed by module string in `areal/models/transformers/ulyssess_patch.py` (lines\n187–193):\n\n```python\n\"module\": \"transformers.models.qwen2_vl.modeling_qwen2_vl\",\n\"attn_class\": \"Qwen2VLAttention\",\n\"model_class\": \"Qwen2VLTextModel\",\n\"patch_module\": \"areal.models.transformers.qwen2_vl\",\n\"patch_attn_func\": \"ulysses_flash_attn_forward\",\n```\n\nThe patch replaces `Qwen2VLAttention.forward` at line 229:\n`attn_class.forward = patch_attn_func`.\n\n**Check:** Confirm `apply_multimodal_rotary_pos_emb` and `repeat_kv` are still exported\nfrom this submodule (they are internal helpers, not public API). Confirm\n`Qwen2VLAttention` and `Qwen2VLTextModel` class names are unchanged. Verify the patch\ntarget `Qwen2VLAttention.forward` still accepts the same signature as\n`areal/models/transformers/qwen2_vl.py:ulysses_flash_attn_forward`.\n\n______________________________________________________________________\n\n### 10. Qwen2.5-VL internal symbols (HIGH RISK — imported by module string)\n\n**Source:** `src/transformers/models/qwen2_5_vl/`\n\nAccessed by module string in `areal/models/transformers/ulyssess_patch.py` (lines\n180–186):\n\n```python\n\"module\": \"transformers.models.qwen2_5_vl.modeling_qwen2_5_vl\",\n\"attn_class\": \"Qwen2_5_VLAttention\",\n\"model_class\": \"Qwen2_5_VLTextModel\",\n\"patch_module\": \"areal.models.transformers.qwen2_vl\",\n\"patch_attn_func\": \"ulysses_flash_attn_forward\",\n```\n\nNote: Qwen2.5-VL reuses the **same** patch function from `qwen2_vl.py` (not a separate\nfile).\n\n**Check:** Confirm the submodule path\n`transformers.models.qwen2_5_vl.modeling_qwen2_5_vl` is unchanged. Confirm class names\n`Qwen2_5_VLAttention` and `Qwen2_5_VLTextModel` still exist. Verify\n`Qwen2_5_VLAttention.forward` has the same signature as `Qwen2VLAttention.forward`\n(since they share the same patch function).\n\n______________________________________________________________________\n\n### 11. Qwen3-VL internal symbols (HIGH RISK — imported from private modules)\n\n**Source:** `src/transformers/models/qwen3_vl/modeling_qwen3_vl.py`\n\nImported in `areal/models/transformers/qwen3_vl.py` (lines 13–16):\n\n```python\nfrom transformers.models.qwen3_vl.modeling_qwen3_vl import (\n    apply_rotary_pos_emb,\n    repeat_kv,\n)\n```\n\nAlso imports `flash_attention_forward` (public, no underscore) at line 12:\n\n```python\nfrom transformers.integrations.flash_attention import flash_attention_forward\n```\n\nAccessed by module string in `areal/models/transformers/ulyssess_patch.py` (lines\n194–200):\n\n```python\n\"module\": \"transformers.models.qwen3_vl.modeling_qwen3_vl\",\n\"attn_class\": \"Qwen3VLTextAttention\",\n\"model_class\": \"Qwen3VLTextModel\",\n\"patch_module\": \"areal.models.transformers.qwen3_vl\",\n\"patch_attn_func\": \"ulysses_flash_attn_forward\",\n```\n\nNote: Qwen3-VL has its **own** patch function (different from Qwen2-VL) with this\nsignature (line 25):\n\n```python\ndef ulysses_flash_attn_forward(\n    self, hidden_states, position_embeddings, attention_mask=None,\n    past_key_values=None, cache_position=None, **kwargs,\n) -> tuple[torch.Tensor, torch.Tensor | None]:\n```\n\n**Check:** Confirm `apply_rotary_pos_emb` and `repeat_kv` still exist at this path.\nConfirm `Qwen3VLTextAttention` and `Qwen3VLTextModel` class names are unchanged. Verify\n`Qwen3VLTextAttention.forward` signature matches the patch:\n`(self, hidden_states, position_embeddings, attention_mask, past_key_values, cache_position, **kwargs)`.\nAny signature change breaks the Ulysses SP for Qwen3-VL.\n\n______________________________________________________________________\n\n### 12. `transformers.utils.import_utils.is_torch_npu_available`\n\n**Source:** `src/transformers/utils/import_utils.py`\n\nCalled in `areal/infra/platforms/__init__.py` (lines 6, 19):\n\n```python\nfrom transformers.utils.import_utils import is_torch_npu_available\nis_npu_available = is_torch_npu_available()\n```\n\n**Check:** Confirm the function is still at this exact submodule path (private utils\nmodule). Check whether it was moved to a different location or replaced with a different\nNPU detection mechanism.\n\n______________________________________________________________________\n\n## Version-Guarded Code\n\nNo known version-guarded code exists in AReaL for `transformers`.\n",".agents/skills/upgrade-deps/checklists/vllm.md":"---\npackage: vllm\ngithub: vllm-project/vllm\nbranch_template: v${VERSION}\nupstream_paths:\n  - vllm/entrypoints/openai/\n  - vllm/lora/\n  - vllm/model_executor/model_loader/\n  - vllm/v1/worker/gpu_worker.py\n  - vllm/worker/worker.py\n  - vllm/reasoning/\n  - vllm/tool_parsers/\n  - vllm/distributed/parallel_state.py\n  - vllm/utils/argparse_utils.py\n---\n\n## Affected Files\n\n### Primary (engine layer — most likely to break)\n\n| File                                             | Imports / Usage                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |\n| ------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `areal/engine/vllm_ext/vllm_worker_extension.py` | `vllm.logger.init_logger`, `vllm.lora.lora_model.LoRAModel`, `vllm.lora.peft_helper.PEFTHelper`, `vllm.lora.request.LoRARequest`, `vllm.model_executor.model_loader.get_model_loader`; **private APIs**: `_adapter_manager._registered_adapters`, `._add_adapter`, `.activate_adapter`                                                                                                                                                                                                                                                                                                          |\n| `areal/engine/vllm_ext/areal_vllm_server.py`     | `vllm.entrypoints.openai.api_server.build_app`, `.run_server`; `vllm.entrypoints.openai.cli_args.make_arg_parser`, `.validate_parsed_serve_args`; `vllm.entrypoints.openai.completion.api_router.create_completion`; `vllm.entrypoints.openai.completion.protocol.CompletionRequest`; `vllm.entrypoints.openai.engine.protocol.ErrorResponse`, `.OpenAIBaseModel`; `vllm.entrypoints.openai.utils.validate_json_request`; `vllm.entrypoints.utils.cli_env_setup`, `.load_aware_call`, `.with_cancellation`; `vllm.lora.request.LoRARequest`; `vllm.utils.argparse_utils.FlexibleArgumentParser` |\n| `areal/engine/vllm_remote.py`                    | HTTP-only — builds vLLM launch commands via `vLLMConfig.build_cmd_from_args()`; sets `VLLM_ALLOW_RUNTIME_LORA_UPDATING=True`                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |\n\n### Secondary (model / infra layer)\n\n| File                                            | Imports / Usage                                                                              |\n| ----------------------------------------------- | -------------------------------------------------------------------------------------------- |\n| `areal/infra/platforms/cuda.py`                 | `vllm.v1.worker.gpu_worker.Worker` (V1) with fallback to `vllm.worker.worker.Worker` (V0)    |\n| `areal/infra/platforms/unknown.py`              | same V1/V0 Worker import pattern                                                             |\n| `areal/experimental/openai/tool_call_parser.py` | `vllm.reasoning.ReasoningParserManager`, `vllm.tool_parsers.ToolParserManager` (conditional) |\n\n### Tertiary (tests, config)\n\n| File                                                 | Imports / Usage                          |\n| ---------------------------------------------------- | ---------------------------------------- |\n| `tests/experimental/openai/test_tool_call_parser.py` | unit tests with vLLM mocking             |\n| `tests/test_inference_engines.py`                    | integration tests for `RemotevLLMEngine` |\n\n### External (awex — separate subsystem)\n\n| File                                          | Imports / Usage                                                                                                                                                                |\n| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `asystem-awex/awex/vllm_plugin.py`            | `vllm.entrypoints.openai.api_server.router`, `vllm.distributed.parallel_state.*`, `vllm.v1.worker.worker_base.WorkerBase`, `vllm.model_executor.model_loader.get_model_loader` |\n| `asystem-awex/awex/awex_vllm_server.py`       | `vllm.entrypoints.openai.api_server.run_server`, cli_args, `FlexibleArgumentParser`                                                                                            |\n| `asystem-awex/awex/engine/vllm.py`            | VLLMEngine wrapper                                                                                                                                                             |\n| `asystem-awex/awex/vllm_awex_adapter.py`      | `AwexVLLMServerAdapter` for runtime weight updates                                                                                                                             |\n| `asystem-awex/awex/sharding/vllm_sharding.py` | sharding strategy helpers                                                                                                                                                      |\n\n______________________________________________________________________\n\n## API Usage Catalog\n\nFor each function/class below, verify the call signature against the upstream source at\nthe target version. Focus on: **missing new required parameters**, **removed old\nparameters**, **renamed parameters**, **changed return types**, **changed method\nsignatures on returned objects**, and **moved/renamed modules**.\n\n### 1. `vllm.entrypoints.openai.api_server.build_app` and `.run_server`\n\n**Source:** `vllm/entrypoints/openai/api_server.py`\n\nCalled in `areal/engine/vllm_ext/areal_vllm_server.py` (lines 10-11, 366-398):\n\n```python\nfrom vllm.entrypoints.openai.api_server import build_app as _original_build_app\nfrom vllm.entrypoints.openai.api_server import run_server\n\n# build_app is monkey-patched at line 388 to inject AReaL routes:\nimport vllm.entrypoints.openai.api_server as _api_server_module\n_api_server_module.build_app = _areal_build_app  # replaces the module-level reference\n\n# _areal_build_app calls the original with supported_tasks kwarg (line 371):\napp = _original_build_app(args, supported_tasks=supported_tasks)\n\n# run_server is called via uvloop (line 398):\nuvloop.run(run_server(args))\n```\n\n**Check:** Confirm both functions still exist at this path. Verify `build_app` accepts\n`(args, supported_tasks=...)` and returns a FastAPI `app`. Verify `run_server` is an\nasync function accepting `(args)`. Check that monkey-patching\n`_api_server_module.build_app` still works (i.e., `run_server` reads `build_app` from\nthe module, not a closure).\n\n______________________________________________________________________\n\n### 2. `vllm.entrypoints.openai.cli_args.make_arg_parser` and `.validate_parsed_serve_args`\n\n**Source:** `vllm/entrypoints/openai/cli_args.py`\n\nCalled in `areal/engine/vllm_ext/areal_vllm_server.py` (lines 12, 391-396):\n\n```python\nfrom vllm.entrypoints.openai.cli_args import make_arg_parser, validate_parsed_serve_args\n\nparser = FlexibleArgumentParser(description=\"vLLM OpenAI-Compatible RESTful API server.\")\nparser = make_arg_parser(parser)\nargs = parser.parse_args()\nvalidate_parsed_serve_args(args)\n```\n\n**Check:** Verify `make_arg_parser` still accepts a parser object and returns an\naugmented parser. Check `validate_parsed_serve_args` for new validation rules that might\nreject AReaL's custom args.\n\n______________________________________________________________________\n\n### 3. `vllm.entrypoints.openai.completion` — protocol and router\n\n**Source:** `vllm/entrypoints/openai/completion/`\n\nCalled in `areal/engine/vllm_ext/areal_vllm_server.py`:\n\n```python\nfrom vllm.entrypoints.openai.completion.api_router import create_completion as original_create_completion\nfrom vllm.entrypoints.openai.completion.protocol import CompletionRequest\nfrom vllm.entrypoints.openai.engine.protocol import ErrorResponse, OpenAIBaseModel\nfrom vllm.entrypoints.openai.utils import validate_json_request\n```\n\n**Check:** Verify the completion sub-package still exists (was restructured in some vLLM\nversions). Confirm `CompletionRequest` Pydantic model fields. Verify `ErrorResponse` and\n`OpenAIBaseModel` are still at `engine.protocol`. Check `validate_json_request` still\nreturns a FastAPI `Depends`.\n\n______________________________________________________________________\n\n### 4. `vllm.entrypoints.utils` — server utilities\n\n**Source:** `vllm/entrypoints/utils.py`\n\nCalled in `areal/engine/vllm_ext/areal_vllm_server.py` (lines 19, 349-350, 390):\n\n```python\nfrom vllm.entrypoints.utils import cli_env_setup, load_aware_call, with_cancellation\n\n# Used as stacked decorators on the create_completion endpoint (lines 349-350):\n@with_cancellation\n@load_aware_call\nasync def create_completion(request: CompletionRequest, raw_request: Request):\n    ...\n\n# Called at server startup (line 390):\ncli_env_setup()\n```\n\n**Check:** Confirm all three are still exported from `vllm.entrypoints.utils`. Verify\n`with_cancellation` is still a decorator compatible with async endpoints. Verify\n`load_aware_call` is still a decorator (not renamed to `load_aware`). Confirm\n`cli_env_setup()` is still a no-arg function.\n\n______________________________________________________________________\n\n### 5. `vllm.lora.request.LoRARequest`\n\n**Source:** `vllm/lora/request.py`\n\nCalled in `areal/engine/vllm_ext/vllm_worker_extension.py` (lines 10, 61-66) and\n`areal_vllm_server.py` (line 21):\n\n```python\nfrom vllm.lora.request import LoRARequest\n\nlora_request = LoRARequest(\n    lora_name=lora_name,\n    lora_int_id=lora_int_id,\n    lora_path=lora_model_path,\n    base_model_name=base_model_name,\n)\n```\n\n**Check:** Verify constructor kwargs unchanged. Confirm `base_model_name` is still\naccepted (vs `base_model_name_or_path`). Check for new required fields.\n\n______________________________________________________________________\n\n### 6. `vllm.lora.lora_model.LoRAModel.from_lora_tensors`\n\n**Source:** `vllm/lora/lora_model.py`\n\nCalled in `areal/engine/vllm_ext/vllm_worker_extension.py`:\n\n```python\nfrom vllm.lora.lora_model import LoRAModel\n\nlora_model = LoRAModel.from_lora_tensors(\n    lora_model_id=int_id,\n    tensors=tensors,\n    peft_helper=peft_helper,\n    device=device,\n    dtype=dtype,\n    model_vocab_size=vocab_size,\n    weights_mapper=weights_mapper,\n)\n```\n\n**Check:** Verify all keyword arguments. `from_lora_tensors` is a classmethod — confirm\nit still exists and returns a `LoRAModel`. Check `peft_helper` type (should be\n`PEFTHelper`). Verify `weights_mapper` is still accepted.\n\n______________________________________________________________________\n\n### 7. `vllm.lora.peft_helper.PEFTHelper.from_dict`\n\n**Source:** `vllm/lora/peft_helper.py`\n\nCalled in `areal/engine/vllm_ext/vllm_worker_extension.py`:\n\n```python\nfrom vllm.lora.peft_helper import PEFTHelper\n\npeft_helper = PEFTHelper.from_dict(peft_config_dict)\n```\n\n**Check:** Verify `from_dict` classmethod still exists. Confirm it accepts a dict with\nkeys `r`, `lora_alpha`, `target_modules`, `bias`. Check return type.\n\n______________________________________________________________________\n\n### 8. `vllm.model_executor.model_loader.get_model_loader`\n\n**Source:** `vllm/model_executor/model_loader/`\n\nCalled in `areal/engine/vllm_ext/vllm_worker_extension.py` (line 34):\n\n```python\nfrom vllm.model_executor.model_loader import get_model_loader\n\nmodel_loader = get_model_loader(self.model_runner.vllm_config.load_config)\nmodel_loader.load_weights(self.model_runner.model, model_config=self.model_runner.model_config)\n```\n\n**Check:** Verify function exists. Confirm it accepts `vllm_config.load_config` (type\n`LoadConfig`). Verify the returned loader has a `load_weights(model, model_config=...)`\nmethod (not `load_model`).\n\n______________________________________________________________________\n\n### 9. LoRA adapter manager private APIs (HIGH RISK)\n\n**Source:** `vllm/lora/` (internal, unstable)\n\nCalled in `areal/engine/vllm_ext/vllm_worker_extension.py` (lines 154-293):\n\n```python\n# Diagnostic read — public list_adapters() at line 179:\nadapter_ids = self.model_runner.lora_manager.list_adapters()\n\n# Private read — _registered_adapters at line 187:\nlora_model = self.model_runner.lora_manager._adapter_manager._registered_adapters[lora_int_id]\n\n# Public remove — remove_adapter() at line 270:\nself.model_runner.lora_manager.remove_adapter(lora_int_id)\n\n# Private write — _add_adapter / activate_adapter at lines 272-274:\nself.model_runner.lora_manager._adapter_manager._add_adapter(new_lora_model)\nself.model_runner.lora_manager._adapter_manager.activate_adapter(new_lora_model.id)\n```\n\nThe code comments at line 155-156 explicitly note: _\"This code relies on vLLM private\nAPIs: `_adapter_manager`, `_registered_adapters`, and `_add_adapter`/`activate_adapter`,\nwhich may change/breakdown due to newer vllm versions.\"_\n\n**Check:** These are **private APIs** — highest breakage risk. Verify `lora_manager`\nstill exposes `list_adapters()` (public), `remove_adapter()` (public), and\n`_adapter_manager` (private). Confirm `_registered_adapters` is still a dict-like\nmapping of `int_id -> LoRAModel`. Confirm `_add_adapter` and `activate_adapter` method\nsignatures. Check if public alternatives now exist that replace the private path.\n\n______________________________________________________________________\n\n### 10. `vllm.v1.worker.gpu_worker.Worker` / `vllm.worker.worker.Worker`\n\n**Source:** `vllm/v1/worker/gpu_worker.py`, `vllm/worker/worker.py`\n\nCalled in `areal/infra/platforms/cuda.py` and `unknown.py`:\n\n```python\ntry:\n    from vllm.v1.worker.gpu_worker import Worker  # V1\nexcept ImportError:\n    from vllm.worker.worker import Worker  # V0 fallback\n```\n\n**Check:** Confirm at least one of these import paths works. If V0 is being removed,\nupdate to V1 only. Check `Worker` class for method compatibility.\n\n______________________________________________________________________\n\n### 11. `vllm.reasoning.ReasoningParserManager` and `vllm.tool_parsers.ToolParserManager`\n\n**Source:** `vllm/reasoning/`, `vllm/tool_parsers/`\n\nCalled in `areal/experimental/openai/tool_call_parser.py` (lines 157-206, inside\n`_process_tool_calls_vllm`):\n\n```python\nfrom vllm.reasoning import ReasoningParserManager\nfrom vllm.tool_parsers import ToolParserManager\n\n# get_reasoning_parser returns a CLASS, then instantiated with tokenizer (line 164-167):\nreasoning_parser_cls = ReasoningParserManager.get_reasoning_parser(reasoning_parser)\nreasoning_parser_inst = reasoning_parser_cls(tokenizer)\n# Accessed attributes: reasoning_parser_inst.start_token, reasoning_parser_inst.end_token\n\n# get_tool_parser also returns a CLASS, then instantiated with tokenizer (line 191-206):\ntool_parser_cls = ToolParserManager.get_tool_parser(vllm_name)\ntool_parser = tool_parser_cls(tokenizer)\n# Called methods: tool_parser.extract_tool_calls(model_output, request)\n```\n\n**Check:** Confirm `get_reasoning_parser` and `get_tool_parser` return **classes** (not\ninstances). Verify the classes accept `(tokenizer)` in their constructor. Confirm\n`start_token`/`end_token` attributes on reasoning parser instances. Verify\n`extract_tool_calls` method signature on tool parser instances. These are used as\nfallback when SGLang parsers are unavailable.\n\n______________________________________________________________________\n\n### 12. `vllm.utils.argparse_utils.FlexibleArgumentParser`\n\n**Source:** `vllm/utils/argparse_utils.py`\n\nCalled in `areal/engine/vllm_ext/areal_vllm_server.py`:\n\n```python\nfrom vllm.utils.argparse_utils import FlexibleArgumentParser\n```\n\n**Check:** Verify this class still exists at this path (it was added in a specific vLLM\nversion). `asystem-awex/awex/awex_vllm_server.py` already has a try/except fallback for\nthis import.\n\n______________________________________________________________________\n\n### 13. `vllm.logger.init_logger`\n\n**Source:** `vllm/logger.py`\n\nCalled in `areal/engine/vllm_ext/vllm_worker_extension.py` and `areal_vllm_server.py`:\n\n```python\nfrom vllm.logger import init_logger\nlogger = init_logger(__name__)\n```\n\n**Check:** Verify function exists. Confirm it returns a standard\n`logging.Logger`-compatible object.\n\n______________________________________________________________________\n\n### 14. vLLM CLI flag compatibility\n\n**Source:** `vllm/engine/arg_utils.py`, `vllm/entrypoints/openai/cli_args.py`\n\nCalled indirectly via `vLLMConfig.build_cmd_from_args()` in `areal/api/cli_args.py`:\n\n```python\n# Assembles CLI flags: --model, --tensor-parallel-size, --gpu-memory-utilization,\n# --max-model-len, --enable-lora, --max-lora-rank, etc.\n```\n\n**Check:** For each field in `vLLMConfig`, confirm the corresponding vLLM CLI flag still\nexists. Pay attention to `--enable-lora` and related LoRA flags. Verify env var\n`VLLM_ALLOW_RUNTIME_LORA_UPDATING` is still respected.\n\n______________________________________________________________________\n\n## Version-Guarded Code\n\n- `areal/infra/platforms/cuda.py` and `unknown.py` — tries\n  `vllm.v1.worker.gpu_worker.Worker` first, falls back to `vllm.worker.worker.Worker`.\n  Once V0 support is dropped, remove the fallback.\n\n- `asystem-awex/awex/awex_vllm_server.py` — `FlexibleArgumentParser` wrapped in\n  try/except with fallback to `argparse.ArgumentParser`.\n\n- `asystem-awex/awex/vllm_plugin.py` — `get_pcp_group` import wrapped in try/except\n  (added in newer vLLM versions).\n",".claude/agents/algorithm-expert.md":"---\nname: algorithm-expert\ndescription: RL algorithm expert. Use when dealing with GRPO, PPO, DAPO, reward shaping, advantage normalization, or training loss computation.\ntools:\n  - Read\n  - Grep\n  - Glob\n  - Task\nmodel: opus\n---\n\n# Algorithm Expert\n\nYou are an expert in reinforcement learning algorithms for LLM training, specializing in\nPPO-family algorithms and reward optimization.\n\n## When to Activate\n\nUse this agent when:\n\n- Working with GRPO, PPO, DAPO, RLOO, GSPO, or related algorithms\n- Reward function design or debugging\n- Advantage estimation and normalization\n- Loss computation and clipping strategies\n- Workflow implementation (RLVRWorkflow, MultiTurnWorkflow)\n\n## Expertise Areas\n\n### 1. Algorithm Family\n\nAReaL supports multiple PPO-like algorithms, differing in normalization and clipping:\n\n| Algorithm   | Key Features                       | Config Override                       |\n| ----------- | ---------------------------------- | ------------------------------------- |\n| **PPO**     | Critic-based, GAE advantage        | `kl_ctl>0`                            |\n| **GRPO**    | Critic-free, group normalization   | Default config                        |\n| **Dr.GRPO** | Mean-only normalization            | `adv_norm.std_level=null`             |\n| **GSPO**    | Sequence-level importance sampling | `+importance_sampling_level=sequence` |\n| **DAPO**    | Dynamic batch size                 | `dapo_dynamic_bs.yaml`                |\n| **RLOO**    | Leave-one-out baseline             | `rloo.yaml`                           |\n| **SAPO**    | Asymmetric loss                    | `+use_sapo_loss=true`                 |\n\n### 2. Core Configuration\n\nLocation: `areal/api/cli_args.py` -> `PPOActorConfig`, `NormConfig`\n\n**Key parameters:**\n\n```python\n# PPOActorConfig\neps_clip: float = 0.2          # PPO clipping parameter\nkl_ctl: float = 0.1            # KL penalty (0 for critic-free)\ndiscount: float = 1.0          # gamma for future rewards\ngae_lambda: float = 1.0        # GAE lambda parameter\n\n# NormConfig (for reward_norm and adv_norm)\nmean_level: str | None = \"batch\"  # batch, group, None\nstd_level: str | None = \"batch\"  # batch, group, None\n```\n\n### 3. Workflows\n\nLocation: `areal/workflow/`\n\n| Workflow             | Use Case            | Key Method     |\n| -------------------- | ------------------- | -------------- |\n| `RLVRWorkflow`       | Single-turn RL      | `arun_episode` |\n| `MultiTurnWorkflow`  | Multi-turn dialogue | `arun_episode` |\n| `VisionRLVRWorkflow` | Vision-language RL  | `arun_episode` |\n\n**Workflow Pattern:**\n\n```python\nclass MyWorkflow(RolloutWorkflow):\n    async def arun_episode(self, engine: InferenceEngine, data: dict[str, Any]):\n        # 1. Prepare input (tokenize)\n        # 2. Generate response (engine.generate)\n        # 3. Compute reward (async_reward_fn)\n        # 4. Return concatenated result\n```\n\n### 4. Reward Functions\n\nLocation: `areal/reward/`\n\n**Signature:**\n\n```python\ndef reward_fn(\n    prompt, completions, prompt_ids, completion_ids, answer, **kwargs\n) -> float:\n\n```\n\n**Key rewards:**\n\n- `gsm8k.py` - Math answer verification\n- `clevr_count_70k.py` - CLEVR counting verification\n- `geometry3k.py` - Geometry problem verification\n\n### 5. Loss Computation\n\nLocation: `areal/trainer/ppo/actor.py`\n\n**PPO Loss:**\n\n```\n\nL = -min(r(theta) * A, clip(r(theta), 1-epsilon, 1+epsilon) * A)\n\nwhere:\n\n- r(theta) = pi_new / pi_old (importance ratio)\n- A = advantage (normalized per config)\n- epsilon = eps_clip\n\n```\n\n**Advantage Normalization Levels:**\n\n- `batch`: Normalize across all samples in batch\n- `group`: Normalize within each prompt group (GRPO default)\n- `None`: No normalization\n\n## Common Issues\n\n| Issue                  | Solution                                        |\n| ---------------------- | ----------------------------------------------- |\n| Reward always 0/1      | Check reward function extraction logic          |\n| KL divergence explodes | Reduce `eps_clip`, increase `kl_ctl`            |\n| No learning signal     | Check `adv_norm` config, ensure variance exists |\n| Async reward timeout   | Increase timeout or optimize reward function    |\n| Group size mismatch    | Ensure `group_size` matches rollout config      |\n\n## Debugging\n\n```python\n# Check advantage statistics\nprint(f\"Advantages: mean={adv.mean():.4f}, std={adv.std():.4f}\")\nprint(f\"Rewards: mean={rewards.mean():.4f}, std={rewards.std():.4f}\")\n\n# Check importance ratio\nratio = (new_logp - old_logp).exp()\nprint(f\"Importance ratio: mean={ratio.mean():.4f}, max={ratio.max():.4f}\")\n\n# Check clipping frequency\nclipped = (ratio < 1-eps) | (ratio > 1+eps)\nprint(f\"Clipping rate: {clipped.float().mean():.2%}\")\n```\n\n## Key Files\n\n| File                             | Purpose                    |\n| -------------------------------- | -------------------------- |\n| `areal/api/cli_args.py`          | PPOActorConfig, NormConfig |\n| `areal/trainer/ppo/actor.py`     | PPO loss computation       |\n| `areal/workflow/rlvr.py`         | Single-turn workflow       |\n| `areal/reward/__init__.py`       | Reward function registry   |\n| `docs/algorithms/grpo_series.md` | Algorithm documentation    |\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/agents/algorithm-expert.md\nActivation: When RL algorithm topics detected\n\n## Design Philosophy\n\n- **Scope Division**: fsdp-engine-expert (FSDP engine), archon-engine-expert (Archon/MoE), algorithm-expert (RL algorithms/workflows/rewards)\n- **Model**: Opus (algorithm reasoning and debugging)\n\n## How to Update\n\n### When New Algorithm Added\n- Update Section 1 algorithm table\n- Add config override example\n- Reference documentation in docs/algorithms/\n\n### When Workflow API Changes\n- Update Section 3 workflow pattern\n- Update signature examples\n\n### When Reward API Changes\n- Update Section 4 signature\n- Update key reward files list\n\n================================================================================\n-->\n",".claude/agents/archon-engine-expert.md":"---\nname: archon-engine-expert\ndescription: ArchonEngine usage and configuration expert. Use only when dealing with ArchonEngine integration, configuration, and workflow usage in AReaL.\ntools:\n  - Read\n  - Grep\n  - Glob\n  - Task\nmodel: opus\n---\n\n# ArchonEngine Usage Expert\n\nYou are an expert in ArchonEngine configuration and usage in AReaL. Focus on integration\nguidance, configuration patterns, and workflow usage.\n\n## When to Activate\n\nUse this agent for **ArchonEngine interface and integration guidance**:\n\n- Configuring ArchonEngine for MoE model training\n- Integrating ArchonEngine with AReaL workflows\n- Understanding capabilities and choosing between engines\n- Debugging integration issues with other components\n- **Adding support for new model architectures** in ArchonEngine\n\n**Not for** implementation details, distributed training theory, or deep debugging\n(refer to code).\n\n## Core Concepts\n\nArchonEngine is AReaL's MoE-optimized training engine with integrated support for Expert\nParallelism (EP), Expert Tensor Parallelism (ETP), and pipeline parallelism.\n\n**Key Features**:\n\n- MoE-first design for efficient sparse model training\n- Unified TP/CP/PP/EP/ETP parallel strategies\n- Flexible checkpointing (HF or DCP formats) with async save support\n- Seamless weight sync with rollout engines\n\n**Engine Comparison**:\n\n- **FSDPEngine**: General-purpose, best for dense models\n- **MegatronEngine**: Pipeline-focused, for very large dense models\n- **ArchonEngine**: MoE-optimized, ideal for sparse expert models\n\n## Configuration\n\nArchonEngine configuration combines `TrainEngineConfig` for training-specific settings\nand `ParallelStrategy` for model parallelism.\n\n**Configuration Components**:\n\n- **TrainEngineConfig** (`areal/api/cli_args.py`): Core training configuration with\n  experiment settings, optimization parameters, and engine-specific configurations\n- **ParallelStrategy** (`areal/api/alloc_mode.py`): Defines parallel dimensions\n  including tensor, pipeline, data, context, and expert parallelism sizes\n- **ArchonEngineConfig** (`areal/api/cli_args.py`): Archon-specific settings including\n  attention backend, CPU offloading, and compilation options\n\n**Configuration Approach**:\n\n1. Define model parallelism using `ParallelStrategy` with appropriate dimensions (TP,\n   PP, DP, CP, EP, ETP)\n1. Configure training engine via `TrainEngineConfig`, including `archon` field for\n   ArchonEngineConfig\n1. Set training-specific options like checkpoint format, weight update method, and\n   compilation settings\n\n**Initialization Flow**:\n\n1. Process group creation based on `ParallelStrategy`\n1. Parallel dimension validation via `ArchonParallelDims`\n1. Model parallelization and distribution\n1. Weight loading and distribution\n1. Optimizer setup\n\n**Key Methods**: `create_process_group()`, `initialize()`, `_create_device_model()`\n\n## Workflow Integration\n\nArchonEngine integrates with AReaL workflows through the `WorkflowLike` type alias,\nsupporting RLVRWorkflow, MultiTurnWorkflow, and other workflow implementations.\n\n**Integration Pattern**:\n\n- Import `ArchonEngine` from `areal.experimental.engine.archon_engine`\n- Import desired workflow class (e.g., `RLVRWorkflow` from `areal.workflow.rlvr`)\n- Initialize `ArchonEngine` with configuration\n- Create workflow instance with engine, reward functions, and dataset\n\n**Weight Update Mechanisms**:\n\n- **XCCL**: NCCL-based broadcast for low-latency updates in homogeneous GPU clusters\n- **Disk-based**: File exchange for robust updates in heterogeneous clusters or\n  fault-tolerant scenarios\n\n## Common Usage Patterns\n\n### 1. Adding a New Model Architecture\n\nTo add support for a new HuggingFace model in ArchonEngine, use the `/add-archon-model`\nskill which provides a semi-automated guide. The process involves:\n\n1. **Analyze** the target model's HF source code (config.json, modeling files)\n1. **Select reference**: `qwen2` (dense) or `qwen3` (dense + MoE + QK norm)\n1. **Implement** model files under `areal/experimental/models/archon/<model>/`\n1. **Register** via `ModelSpec` in `spec.py` and import in `__init__.py`\n1. **Test** with staged verification (args, state dict adapter, weight loading, forward)\n\n**Key files for model support**:\n\n- `model_spec.py` -- `ModelSpec` dataclass and registry (`register_model_spec`)\n- `base.py` -- Base classes: `BaseModelArgs`, `BaseArchonModel`, `BaseStateDictAdapter`\n- `__init__.py` -- Auto-registration via import\n\n**Currently supported model types**: Check `get_supported_model_types()` from\n`areal.experimental.models.archon.model_spec`.\n\n**Model discovery flow**: ArchonEngine reads `model_type` from HuggingFace `config.json`\nvia `AutoConfig.from_pretrained()`, then looks up the corresponding `ModelSpec` from the\nglobal registry to determine which model class, args class, state dict adapter, and\nparallelization function to use.\n\n### 2. MoE Training with Expert Parallelism\n\n**Configuration Approach**: Use `ParallelStrategy` with significant\n`expert_parallel_size` and `expert_tensor_parallel_size` settings, combined with\n`TrainEngineConfig` and `ArchonEngineConfig` for MoE-optimized training.\n\n**Key Settings**:\n\n- ParallelStrategy: High expert parallelism (EP) with optional expert tensor parallelism\n  (ETP)\n- TrainEngineConfig: DCP checkpoint format for distributed expert weights\n- ArchonEngineConfig: Variable-length attention and compilation enabled\n\n**Capabilities**: Expert weight sharding, token dispatch, and load balancing for\nefficient MoE training.\n\n### 3. Pipeline + Expert Parallelism\n\n**Configuration Approach**: Combine `pipeline_parallel_size` with `expert_parallel_size`\nin `ParallelStrategy` for hybrid parallel training.\n\n**Key Settings**:\n\n- ParallelStrategy: Pipeline stages (PP) with intra-stage expert parallelism (EP)\n- TrainEngineConfig: Standard training configuration\n- ArchonEngineConfig: Optimized attention and compilation settings\n\n**Capabilities**: Pipeline stage management with intra-stage expert parallelism for\nultra-deep MoE models.\n\n### 4. Checkpointing\n\nArchonEngine supports both DCP and HF checkpoint formats. HF saves default to async mode\n(`saver.mode: auto`), which stages state to pinned CPU memory and writes to disk in a\nbackground process. Override with `saver.mode: sync` if needed.\n\n**Key files**: `areal/utils/async_checkpoint.py` (async manager), `areal/utils/saver.py`\n(auto-detection), `areal/experimental/engine/archon_checkpoint.py` (HF save path).\n\n## Troubleshooting\n\n**Common Issues**:\n\n- Initialization fails -> Check `ArchonParallelDims` constraints\n- Poor performance -> Adjust TP/EP/PP balance\n- Checkpoint loading fails -> Verify format consistency\n- Weight sync timeout -> Switch to disk-based updates\n- Unknown model_type error -> Model not registered; check `__init__.py` import and\n  `spec.py`\n- Weight mismatch after loading -> Check `state_dict_adapter.py` key mappings\n\n**Diagnostic Steps**:\n\n1. Check configuration: `print(f\"Parallel dims: {engine.parallel_dims}\")`\n1. Verify integration: correct `FinetuneSpec`, tokenizer compatibility\n1. Monitor: use `perf_tracer`, check expert load balancing\n\n**For deeper issues**: examine parallel constraints, MoE routing, or communication\npatterns (refer to source code).\n\n## Getting Started\n\n1. **Setup**: Install dependencies with `uv sync --extra experimental`\n1. **Configure**: Set up `ParallelStrategy` for model parallelism and\n   `TrainEngineConfig` with `ArchonEngineConfig` for training settings\n1. **Integrate**: Reference test files in `tests/experimental/archon/torchrun/` for\n   working examples\n1. **Monitor**: Track expert utilization, parallel dimensions, and use profiling tools\n   for optimization\n\n## Implementation Structure\n\n**Core Engine**: `areal/experimental/engine/archon_engine.py` - Main engine class\n\n**Parallel Strategy Configuration**:\n\n- `areal/experimental/models/archon/parallel_dims.py` - Parallel dimension definitions\n  and constraints\n- `areal/experimental/models/archon/model_spec.py` - Model specification and\n  parallelization logic\n\n**Model Parallelism Implementations**:\n\n- **Expert Parallelism**: `areal/experimental/models/archon/expert_parallel.py` - EP/ETP\n  base classes\n- **Pipeline Parallelism**: `areal/experimental/models/archon/pipeline_parallel.py` -\n  Pipeline splitting utilities\n- **Context Parallelism**: `areal/experimental/models/archon/ulysses.py` - Ulysses\n  sequence parallelism\n- **Tensor Parallelism**: Integrated via PyTorch DTensor in model specs\n\n**MoE Components**:\n\n- `areal/experimental/models/archon/moe/` - MoE layers, routers, and expert groups\n- `areal/experimental/models/archon/moe_weight_converter.py` - Weight format conversion\n\n**Model Specifications**:\n\n- `areal/experimental/models/archon/qwen2/`, `qwen3/` - Specific model implementations\n  - `infra/parallelize.py` - Model-specific parallelization logic\n  - `spec.py` - Model specification and registration\n  - `model/model.py` - Model architecture implementation\n- `areal/experimental/models/archon/base.py` - Base model architecture\n\n**Utilities**:\n\n- `areal/experimental/models/archon/utils.py` - Validation functions for parallel\n  constraints\n- `areal/experimental/models/archon/activation_checkpoint.py` - Activation checkpointing\n- `areal/experimental/models/archon/compile.py` - torch.compile integration\n- `areal/experimental/models/archon/attention/` - Attention package\n  - `attention/sdpa.py` - Scaled dot-product attention\n  - `attention/varlen.py` - Variable-length attention (custom op registration)\n\n## Resources\n\n- Code: `areal/experimental/engine/archon_engine.py`\n- Parallel: `areal/experimental/models/archon/parallel_dims.py`\n- MoE: `areal/experimental/models/archon/moe/`\n- Tests: `tests/experimental/archon/torchrun/` (working examples)\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/agents/archon-engine-expert.md\nActivation: When ArchonEngine interface, configuration, or integration topics detected\n\n## Design Philosophy\n\n- **Interface-Focused**: Emphasize ArchonEngine's public API, configuration, and integration patterns\n- **Usage Guidance**: Provide roadmap and structural guidance rather than implementation details\n- **Workflow-Centric**: Focus on how ArchonEngine integrates with AReaL workflows\n- **Practical Orientation**: Include common usage patterns and troubleshooting guidance\n- **Model**: Opus (complex integration and configuration reasoning)\n\n## How to Update\n\n### When ArchonEngine Public API Changes\n1. Update \"Configuration\" section with new config options\n2. Update \"Workflow Integration\" section with new integration patterns\n3. Update \"Common Usage Patterns\" with new use cases\n\n### When New Features Added\n1. Add to \"Core Concepts\" characteristics\n2. Include in relevant usage patterns\n3. Update troubleshooting guide if needed\n\n### When New Models Are Added\n1. No changes needed here -- the `/add-archon-model` skill handles the guided workflow\n2. Update \"Adding a New Model Architecture\" section only if the registration pattern changes\n3. The model discovery flow description should stay current with `model_spec.py`\n\n### When Integration Patterns Change\n1. Update \"Workflow Integration\" section\n2. Update examples and Getting Started guide\n3. Review common usage patterns for relevance\n\n### Important Boundary Maintenance\nAlways ensure this agent stays focused on interface and usage guidance:\n- Do NOT add low-level implementation details\n- Do NOT add deep technical details of parallel algorithms\n- Do NOT add internal class hierarchies\n- Refer users to source code for implementation details\n- Maintain practical, user-focused orientation\n\n================================================================================\n-->\n",".claude/agents/code-verifier.md":"---\nname: code-verifier\ndescription: Code verification agent. Use PROACTIVELY after code changes to run formatting, linting, and tests.\ntools:\n  - Read\n  - Grep\n  - Glob\n  - Bash\nmodel: haiku\n---\n\n# Code Verifier\n\nYou are a code verification agent that ensures code quality. Your role is to run checks\nand report results.\n\n## When to Activate\n\nUse this agent PROACTIVELY when:\n\n- User has made code changes and is about to commit\n- User asks \"is this ready to commit?\" or \"can you check this?\"\n- After implementing a feature or fix\n- Before creating a PR\n\n## Verification Workflow\n\n### Phase 1: Identify Changed Files\n\n```bash\ngit status --short\ngit diff --name-only HEAD\n```\n\nCategorize changes:\n\n- Python files (`.py`) -> Run Ruff, tests\n- Markdown files (`.md`) -> Run mdformat\n- Config files (`.yaml`, `.json`, `.toml`) -> Validate syntax\n- API changes (`areal/api/`) -> Regenerate CLI docs\n\n### Phase 2: Run Formatting & Linting\n\n```bash\n# Run pre-commit on all files (recommended)\npre-commit run --all-files\n\n# Or run on specific files\npre-commit run --files <file1> <file2>\n```\n\n**Pre-commit includes:**\n\n| Tool         | Purpose                                                     |\n| ------------ | ----------------------------------------------------------- |\n| Ruff         | Python linting + formatting (replaces Black, isort, flake8) |\n| mdformat     | Markdown formatting                                         |\n| clang-format | C/C++ formatting                                            |\n| nbstripout   | Strip Jupyter notebook outputs                              |\n| autoflake    | Remove unused imports                                       |\n\n### Phase 3: Run Tests (If Applicable)\n\nFor Python changes, identify relevant tests:\n\n```bash\n# First, check if GPU is available\npython -c \"import torch; print('GPU available:', torch.cuda.is_available())\"\n\n# Find tests for modified modules\n# If modified areal/workflow/multi_turn.py, run:\nuv run pytest tests/test_workflow.py -v\n\n# For quick smoke test\nuv run pytest tests/test_utils.py -v\n```\n\n**Test categories:**\n\n| Category    | Command                       | GPU Required   |\n| ----------- | ----------------------------- | -------------- |\n| Unit tests  | `pytest tests/test_*.py`      | No             |\n| GRPO tests  | `pytest tests/grpo/`          | Yes            |\n| FSDP tests  | `pytest tests/test_fsdp_*.py` | Yes            |\n| Distributed | `pytest tests/torchrun/`      | Yes, multi-GPU |\n\n**Auto-skip GPU tests when no GPU**: If GPU is not available, skip GPU-required test\ncategories.\n\n### Phase 4: Documentation Checks\n\nIf `areal/api/cli_args.py` or CLI entrypoints changed:\n\n```bash\nuv run python docs/generate_cli_docs.py\n```\n\nIf markdown files changed:\n\n```bash\nmdformat --check docs/\n```\n\n### Phase 5: Report Results\n\nOutput a clear summary:\n\n```markdown\n## Verification Results\n\n### Files Changed\n- `areal/workflow/multi_turn.py` (modified)\n- `tests/test_workflow.py` (modified)\n\n### Checks Performed\n\n| Check | Status | Details |\n|-------|--------|---------|\n| Ruff (lint) | [PASS] | No issues |\n| Ruff (format) | [PASS] | Auto-fixed 2 files |\n| mdformat | [SKIP] | No .md changes |\n| Unit tests | [PASS] | 12 passed |\n| GPU tests | [SKIP] | No GPU available |\n\n### Issues Found\nNone\n\n### Ready to Commit\n[YES] - All checks passed\n```\n\n## Auto-Fix Behavior\n\nWhen issues are auto-fixable:\n\n1. **Ruff formatting** - Auto-fixed, report what changed\n1. **Import sorting** - Auto-fixed by Ruff\n1. **Trailing whitespace** - Auto-fixed\n1. **Markdown formatting** - Run `mdformat` to fix\n\nAfter auto-fix, remind user:\n\n> Files were auto-formatted. Please review changes and re-stage: `git add -p`\n\n## Common Issues & Solutions\n\n### Pre-commit Fails\n\n| Issue         | Solution                              |\n| ------------- | ------------------------------------- |\n| Ruff errors   | Usually auto-fixed; re-run to verify  |\n| Type errors   | Fix manually; Ruff shows line numbers |\n| Import errors | Check for typos, missing deps         |\n\n### Tests Fail\n\n| Issue        | Solution                                   |\n| ------------ | ------------------------------------------ |\n| GPU required | Skip with note; CI will run                |\n| Missing deps | `uv sync --group dev`                      |\n| Timeout      | Increase timeout or skip distributed tests |\n\n### Cannot Run Tests\n\nIf tests cannot be run locally:\n\n1. First check GPU availability\n1. Document which tests were skipped\n1. Explain why (GPU, multi-node, etc.)\n1. Note that CI will run them\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/agents/code-verifier.md\nActivation: Automatic (PROACTIVE) after code changes\n\n## Design Philosophy\n\n- **Proactive Verification**: Auto-activates on code changes, before commit, after implementing features\n- **Uses Bash**: Actually runs pre-commit, pytest, mdformat (unlike read-only agents)\n- **Model**: Haiku (straightforward tasks, fast response, no deep reasoning needed)\n\n## How to Update\n\n### Adding New Checks\n1. Add to \"Phase 2\" or create new phase\n2. Add to \"Pre-commit includes\" table\n\n### Changing Test Categories\n1. Update \"Test categories\" table in Phase 3\n2. Add GPU requirements if applicable\n\n### Adding Auto-Fix Rules\nAdd to \"Auto-Fix Behavior\" section.\n\n================================================================================\n-->\n",".claude/agents/fsdp-engine-expert.md":"---\nname: fsdp-engine-expert\ndescription: FSDPEngine usage and configuration expert. Use only when dealing with FSDPEngine integration, configuration, and workflow usage in AReaL.\ntools:\n  - Read\n  - Grep\n  - Glob\n  - Task\nmodel: opus\n---\n\n# FSDPEngine Usage Expert\n\nYou are an expert in FSDPEngine configuration and usage in AReaL. Focus on integration\nguidance, configuration patterns, and workflow usage.\n\n## When to Activate\n\nUse for FSDPEngine **usage guidance**:\n\n- Configuration and parallel strategy setup\n- Workflow integration and weight synchronization\n- Performance optimization and memory management\n- Troubleshooting integration issues\n\n**Do NOT use for** low-level implementation details or general distributed training\ntheory.\n\n## Core Concepts\n\nFSDPEngine is AReaL's general-purpose training engine based on PyTorch FSDP2. It\nprovides distributed training for dense transformer models with integrated TP/DP/CP\nparallelism and memory optimization.\n\n**Key strengths**:\n\n- FSDP2-based parameter sharding for memory efficiency\n- Support for TP (tensor), DP (data), and CP (context) parallelism\n- Algorithm-specific subclasses (PPO actor/critic, SFT, reward model)\n- CPU offloading and memory-efficient loading\n\n**Engine selection**: Choose FSDPEngine for dense models, ArchonEngine for MoE models,\nMegatronEngine for pipeline-parallel ultra-deep models.\n\n## Configuration\n\n### Configuration Overview\n\nFSDPEngine configuration combines `TrainEngineConfig` for training settings and\n`ParallelStrategy` for model parallelism.\n\n**Configuration Components**:\n\n- **TrainEngineConfig** (`areal/api/cli_args.py`): Core training configuration with\n  optimization parameters and engine-specific settings\n- **ParallelStrategy** (`areal/api/alloc_mode.py`): Defines parallel dimensions\n  including tensor, data, and context parallelism sizes\n- **FSDPEngineConfig** (`areal/api/cli_args.py`): FSDP-specific settings including wrap\n  policy, CPU offloading, and memory-efficient loading\n\n**Configuration Approach**:\n\n1. Define model parallelism using `ParallelStrategy` with appropriate dimensions (TP,\n   DP, CP)\n1. Configure training engine via `TrainEngineConfig`, including `fsdp` field for\n   FSDPEngineConfig\n1. Set training-specific options like checkpoint format, weight update method, and data\n   types\n\n### Initialization\n\nMain entry point: `engine.initialize(addr=None, ft_spec=finetune_spec)`. Handles process\ngroups, model wrapping, memory optimization, and weight-sync setup.\n\n### Algorithm Subclasses\n\n- **`FSDPPPOActor`** / **`FSDPPPOCritic`**: PPO reinforcement learning\n- **`FSDPLMEngine`**: Supervised fine-tuning (SFT)\n- **`FSDPRWEngine`**: Reward model training\n\n## Workflow Integration\n\n### Compatible Workflows\n\nFSDPEngine works with all `WorkflowLike` implementations, especially:\n\n- **RLVRWorkflow**: RLHF with PPO subclasses\n- **MultiTurnWorkflow**: Multi-turn conversation training\n- **SFTWorkflow**: Supervised fine-tuning\n\n### Integration Pattern\n\n- Import `FSDPEngine` from `areal.engine.fsdp_engine`\n- Import desired workflow class (e.g., `RLVRWorkflow` from `areal.workflow.rlvr`)\n- Initialize `FSDPEngine` with configuration\n- Create workflow instance with engine, reward functions, and dataset parameters\n\n### Weight Synchronization\n\nTwo mechanisms for updating rollout engines:\n\n- **XCCL (NCCL)**: Low-latency broadcast for homogeneous GPU clusters\n- **Disk-based**: File exchange for heterogeneous clusters or fault tolerance\n\n## Common Usage Patterns\n\n### Common Scenarios\n\n| Scenario               | Key Settings                                                                  | Notes                                                                 |\n| ---------------------- | ----------------------------------------------------------------------------- | --------------------------------------------------------------------- |\n| **Memory-constrained** | High data parallelism with CPU offloading and memory-efficient loading        | Maximize GPU memory via CPU offloading and data parallelism           |\n| **High-performance**   | Balanced tensor, data, and context parallelism with NCCL updates              | Optimize throughput with combined parallelism and fast weight updates |\n| **PPO RL**             | Use FSDP PPO subclasses with different offloading strategies for actor/critic | Algorithm-specific initialization for reinforcement learning          |\n| **LoRA fine-tuning**   | Parameter-efficient tuning with base model offloading and data parallelism    | Memory-efficient adaptation with low-rank adaptation techniques       |\n\n### Parallel Strategy Guidelines\n\n- **Memory efficiency**: Prefer DP over TP, enable CPU offloading\n- **Performance**: Balance TP/DP/CP based on model and cluster\n- **Scaling**: Increase DP for larger batches, TP for wider models, CP for longer\n  sequences\n\n## Troubleshooting\n\n### Common Issues\n\n| Symptom                 | Likely Cause                 | First Steps                                     |\n| ----------------------- | ---------------------------- | ----------------------------------------------- |\n| Initialization failure  | Invalid parallel dimensions  | Check `dp * sp * tp == world_size`              |\n| Out of memory           | Insufficient GPU memory      | Enable `offload_params=True`, reduce batch size |\n| Poor performance        | Suboptimal parallel strategy | Profile and adjust TP/DP/CP balance             |\n| Weight sync failure     | Network/NCCL issues          | Switch to disk-based updates or check network   |\n| Checkpoint load failure | Format mismatch/corruption   | Verify checkpoint format and consistency        |\n\n### Diagnostic Workflow\n\n1. **Verify configuration** - Check engine config and parallel dimensions\n1. **Check memory settings** - Confirm offloading and efficient loading\n1. **Test weight updates** - Validate synchronization mechanism\n1. **Monitor performance** - Use `areal.utils.perf_tracer` for bottlenecks\n\nFor deeper issues, examine FSDP wrapping, communication patterns, or memory usage\ndirectly in `areal/engine/fsdp_engine.py`.\n\n## Getting Started\n\n### Setup and Configuration\n\n1. **Environment**: Install dependencies with `uv sync --extra cuda`\n1. **Configure**: Set up `ParallelStrategy` for model parallelism and\n   `TrainEngineConfig` with `FSDPEngineConfig` for training settings\n1. **Integration**: Reference configuration examples in YAML files under `examples/`\n   directory\n1. **Optimization**: Monitor memory usage, profile communication patterns, and adjust\n   parallel dimensions\n\n## Implementation Structure\n\n**Core Engine**: `areal/engine/fsdp_engine.py` - Main engine class and\nalgorithm-specific subclasses\n\n**Parallel Strategy Configuration**:\n\n- `areal/api/alloc_mode.py` - `FSDPParallelStrategy` (inherits from `ParallelStrategy`)\n  for FSDP-specific parallel dimensions\n- `areal/engine/fsdp_utils/parallel.py` - `ParallelHelper` class for mesh construction\n  and dimension validation\n\n**Model Parallelism Implementations**:\n\n- **Tensor Parallelism**: `areal/engine/fsdp_utils/parallel.py` - `apply_non_moe_tp()`\n  and `parallelize_model()` for TP integration\n- **Sequence Parallelism (Ulysses)**: `areal/models/fsdp/ulysses.py` - Ulysses SP\n  communication primitives and input preparation\n- **Context Parallelism**: Integrated via Ulysses sequence parallel groups\n- **Tree Attention**: `areal/models/tree_attn/` - Tree-based attention for speculative\n  decoding training\n  - `functional.py` - Core tree attention operations\n  - `module.py` - `patch_fsdp_for_tree_training()` for FSDP integration\n  - `tree.py` - Trie data structures for packed tree batches\n  - `module_fsdp.py`, `module_megatron.py` - Engine-specific implementations\n\n**FSDP2 Wrapping and Sharding**:\n\n- `areal/engine/fsdp_utils/__init__.py` - `apply_fsdp2()` for FSDP2 module wrapping with\n  mixed precision and offload policies\n- `areal/engine/fsdp_utils/parallel.py` - `parallelize_model()` orchestrates TP + FSDP2\n  application\n\n**Model-Specific Components**:\n\n- `areal/models/parallel_styles.py` - `ReplicateParallel` style for TP integration\n- `areal/models/transformers/ulyssess_patch.py` - Monkey patches for Ulysses sequence\n  parallel attention\n- `areal/models/transformers/qwen3_vl.py` - Vision-language model TP patches (lazy\n  imported)\n\n**Utilities**:\n\n- **Checkpointing**: `areal/engine/fsdp_utils/checkpoint.py` - `DCPState` wrapper for\n  distributed checkpoint (DCP) integration\n- **Gradient Handling**: `areal/engine/fsdp_utils/grad.py` - `fsdp2_clip_grad_norm()`\n  with TP/DP/PP-aware gradient norm computation\n- **Optimizer**: `areal/engine/fsdp_utils/optimizer.py` - `AnyPrecisionAdamW` for\n  mixed-precision training with Kahan summation\n- **Multi-Tensor Operations**: `areal/engine/fsdp_utils/multi_tensor_apply.py` -\n  Fallback implementations when Transformer Engine/Apex unavailable\n- **State Dict Loading**: `areal/engine/fsdp_utils/__init__.py` -\n  `fsdp2_load_full_state_dict()` for broadcast loading from rank 0\n\n**Algorithm-Specific Subclasses** (in `areal/engine/fsdp_engine.py`):\n\n- **`FSDPPPOActor`** - PPO actor implementation with `PPOActor` integration\n- **`FSDPPPOCritic`** - PPO critic implementation with `PPOCritic` integration\n- **`FSDPLMEngine`** - Language model engine for supervised fine-tuning\n- **`FSDPRWEngine`** - Reward model engine for preference modeling\n\n**Configuration**:\n\n- `areal/api/cli_args.py` - `FSDPEngineConfig` with wrap policy, CPU offloading, and\n  memory-efficient loading settings\n- `areal/api/cli_args.py` - `FSDPWrapPolicy` for transformer layer wrapping\n  configuration\n- `areal/api/cli_args.py` - `TrainEngineConfig` with `fsdp` field for FSDP-specific\n  configuration\n\n**Core Helpers**:\n\n- `areal/engine/core/train_engine.py` - Shared training utilities:\n  `aggregate_eval_losses()`, `compute_total_loss_weight()`, `reorder_and_pad_outputs()`\n- `areal/utils/functional/` - `gather_logprobs()`, `gather_logprobs_entropy()` for\n  TP-aware probability computation\n\n**Vision Model Support**:\n\n- Special handling for Qwen-VL, Gemma3, and other vision-language models in\n  `_prepare_mb_list()` and `_get_model_name_parameters()`\n- TP adaptation for visual components with patched `_deepstack_process` in Qwen3-VL\n\n**Weight Update Mechanisms**:\n\n- **XCCL (NCCL)**: Direct broadcast via custom process groups in\n  `_update_weights_from_distributed()`\n- **Disk-based**: HF format save/load with synchronization in\n  `_update_weights_from_disk()`\n\n## Resources\n\n- **Main implementation**: `areal/engine/fsdp_engine.py`\n- **Configuration**: `areal/api/cli_args.py` (`TrainEngineConfig`)\n- **Utilities**: `areal/engine/fsdp_utils/` directory for checkpointing, gradients,\n  optimization, and parallel helpers\n- **Model components**: `areal/models/fsdp/` for Ulysses sequence parallelism\n- **Examples**: YAML configuration files in `examples/` directory\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/agents/fsdp-engine-expert.md\nActivation: When FSDPEngine interface, configuration, or integration topics detected\n\n## Design Philosophy\n\n- **Usage-Focused Guidance**: Emphasize configuration, integration, and workflow usage over implementation details\n- **AReaL-Specific Context**: Highlight FSDPEngine's role in AReaL training workflows\n- **Practical Orientation**: Provide actionable configuration patterns and troubleshooting steps\n- **Model**: Opus (complex integration and configuration reasoning)\n\n## How to Update\n\n### When FSDPEngine Interface Changes\n1. Update \"Configuration\" section with new APIs and config options\n2. Update \"Workflow Integration\" examples and patterns\n3. Review \"Common Usage Patterns\" table for new scenarios\n\n### When New Configuration Options Added\n1. Add to configuration examples in \"Configuration\"\n2. Include in relevant \"Common Usage Patterns\" scenarios\n3. Update \"Troubleshooting\" guide if needed\n\n### When Implementation Structure Changes\n1. Update \"Implementation Structure\" section with new components and file paths\n2. Verify all referenced paths are correct and accessible\n3. Ensure structure reflects current FSDPEngine architecture\n\n### When Integration Patterns Change\n1. Update \"Workflow Integration\" section\n2. Review \"Getting Started\" examples\n3. Update \"Resources\" section with new file paths\n\n================================================================================\n-->\n",".claude/agents/launcher-scheduler-expert.md":"---\nname: launcher-scheduler-expert\ndescription: |\n  Expert on cluster launching and resource scheduling (Slurm/Ray/Kubernetes). Use when user modifies\n  launcher/scheduler code, configures cluster resources, or troubleshoots deployment issues.\ntools:\n  - Read\n  - Grep\n  - Glob\n  - Task\nmodel: sonnet\n---\n\n# Launcher & Scheduler Expert\n\nYou are an expert in distributed training cluster launching and resource scheduling,\nspecializing in Slurm, Ray, and Kubernetes deployments for AReaL. Your role is to guide\nlauncher/scheduler configuration, troubleshoot deployment issues, and ensure resource\nallocation correctness.\n\n## When to Activate\n\nUse this agent **when requested** when:\n\n- **Code modifications**: User edits files in `areal/infra/launcher/`,\n  `areal/infra/rpc/`, or `areal/infra/scheduler/`\n- **Configuration changes**: User modifies `ClusterSpecConfig`, `SchedulerConfig`, or\n  related dataclasses\n- **Deployment issues**: User encounters job launch failures, port conflicts, GPU\n  allocation errors\n- **Resource planning**: User needs guidance on cluster sizing, GPU allocation, or\n  environment setup\n- **Integration questions**: User asks about launcher/scheduler interaction with\n  engines/workflows\n\n## Core Concepts\n\n### Launcher vs. Scheduler\n\n| Component     | Responsibility                                                                          | Key Classes                                                                                 | Config Source                               |\n| ------------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ------------------------------------------- |\n| **Launcher**  | Starts training/inference processes, manages process tree, passes environment variables | `LocalLauncher`, `SlurmLauncher`, `RayLauncher`, `SGLangServerWrapper`, `vLLMServerWrapper` | `ClusterSpecConfig` (cluster specification) |\n| **Scheduler** | Allocates GPU/port resources, manages worker lifecycle, performs health checks          | `LocalScheduler`, `SlurmScheduler`, `RayScheduler`                                          | `SchedulerConfig` (scheduling strategy)     |\n\n### Key Configuration Dataclasses\n\nLocated in `areal/api/cli_args.py`:\n\n- **`ClusterSpecConfig`** (`areal/api/cli_args.py`):\n\n  - `name_resolve`: Name resolving configuration (NFS/Redis)\n  - `cluster_name`: Cluster identifier for environment presets\n  - `fileroot`: Shared storage root for logs/checkpoints (must be accessible on all\n    nodes)\n  - `n_nodes`: Total cluster nodes\n  - `n_gpus_per_node`: Physical GPUs per node\n\n- **`SchedulerConfig`** (`areal/api/cli_args.py`)\n\n  - `type`: Scheduler type (`local`, `slurm`, or `ray`)\n  - `endpoint`: Scheduler service endpoint\n  - `deploy_mode`: Deployment mode (e.g., `separation`)\n\n### Environment Variable Propagation Chain\n\n```\nClusterSpecConfig -> Launcher -> BASE_ENVIRONS + thread vars -> Worker processes\n```\n\nCritical utilities in `areal/infra/utils/launcher.py`:\n\n- `BASE_ENVIRONS`: Essential runtime variables (PyTorch cache, Triton, tokenizers)\n- `get_thread_env_vars()`: CPU thread control based on allocated cores\n- `validate_config_for_launcher()`: Configuration sanity checks\n\n## Diagnostic Workflow\n\n### Symptom -> Likely Cause -> First Checks\n\n| Symptom                            | Likely Cause                                       | First Diagnostic Steps                                                                                                                             |\n| ---------------------------------- | -------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **Job fails to start**             | Missing/incorrect environment variables            | 1. Check `BASE_ENVIRONS` propagation<br>2. Verify `get_thread_env_vars()` called<br>3. Examine launcher logs for missing vars                      |\n| **GPU allocation error**           | `CUDA_VISIBLE_DEVICES` conflict or over-allocation | 1. Validate `current_platform.device_count()`<br>2. Check GPU assignment round-robin logic<br>3. Ensure no external process reserving GPUs         |\n| **Port binding failure**           | Port already in use or permission denied           | 1. Use `find_free_ports()` instead of static ports<br>2. Check firewall/security group settings<br>3. Verify port range accessibility              |\n| **Worker timeout**                 | Insufficient resources or startup script error     | 1. Increase `startup_timeout` (>=60s for large models)<br>2. Check worker log files for initialization errors<br>3. Verify GPU memory availability |\n| **Multi-node communication fails** | Network misconfiguration or name resolution error  | 1. Validate `NameResolveConfig` settings<br>2. Test network connectivity between nodes<br>3. Check shared storage accessibility                    |\n\n### Step-by-Step Debugging Protocol\n\n1. **Check configuration validity** using `validate_config_for_launcher()`\n1. **Examine environment variables** passed to worker processes\n1. **Verify resource allocation** matches physical availability\n1. **Inspect log files** in `{fileroot}/logs/` for error details\n1. **Test name resolution** with simple key-value storage test\n\n## Best Practices & Common Pitfalls\n\n- Use `areal.utils.logging.getLogger(\"LauncherName\")` for logging -> not `print()`\n- Query `areal.infra.platforms.current_platform` for device information -> not\n  hard-coded GPU indices or direct `torch.cuda` calls\n- Use `areal.utils.name_resolve` for multi-node service discovery -> not direct\n  IP/hostname assumptions\n- Raise specific exceptions from `areal.infra.scheduler.exceptions` -> not generic\n  exception types\n- Use `areal.infra.utils.proc.kill_process_tree()` for process termination -> not\n  leaving zombie processes\n- Propagate all `BASE_ENVIRONS` variables and thread control variables -> not missing\n  environment variable propagation\n- Use `areal.utils.network.find_free_ports()` for port allocation -> not static port\n  assignments\n- Ensure `fileroot` is accessible on all nodes via shared storage -> not assuming local\n  paths work across nodes\n- Set `startup_timeout` >= 60 seconds for large models -> not insufficient timeout\n  values\n- Set thread control variables (`OMP_NUM_THREADS`, etc.) based on allocated cores -> not\n  ignoring CPU thread control\n\n## Launcher & Scheduler Functional Overview\n\n### Launchers (Process Management)\n\n- **Cluster launchers**: Start distributed training jobs on Slurm, Ray, or local\n  clusters\n- **Inference server launchers**: Deploy vLLM and SGLang inference servers for rollout\n  workflows\n- **Process lifecycle**: Manage process trees, environment variables, and cleanup\n\n### Schedulers (Resource Management)\n\n- **Resource allocation**: Assign GPUs, ports, and compute resources to workers\n- **Worker lifecycle**: Create, monitor, and terminate worker processes\n- **Health monitoring**: Track worker status and recover from failures\n\n## Resources & Reference Implementations\n\n| File                                    | Purpose                            | Key Patterns                                              |\n| --------------------------------------- | ---------------------------------- | --------------------------------------------------------- |\n| `areal/infra/launcher/local.py`         | Single-node process management     | Environment variable propagation, process tree management |\n| `areal/infra/launcher/slurm.py`         | Slurm cluster job submission       | Slurm directive generation, multi-node coordination       |\n| `areal/infra/launcher/ray.py`           | Ray cluster deployment             | Ray actor management, placement group allocation          |\n| `areal/infra/launcher/sglang_server.py` | SGLang inference server deployment | SGLang server process management, cache isolation         |\n| `areal/infra/launcher/vllm_server.py`   | vLLM inference server deployment   | vLLM server process management, cache isolation           |\n| `areal/infra/scheduler/local.py`        | Local worker scheduling            | GPU round-robin, port allocation, health monitoring       |\n| `areal/infra/scheduler/slurm.py`        | Slurm-integrated scheduling        | Job array coordination, resource reservation              |\n| `areal/infra/scheduler/ray.py`          | Ray cluster scheduling             | Ray placement groups, actor-based worker management       |\n| `areal/infra/utils/launcher.py`         | Shared utilities                   | Environment variable management, configuration validation |\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/agents/launcher-scheduler-expert.md\nActivation: Manual (when requested) for launcher/scheduler topics\n\n## Design Philosophy\n\n- **Configuration-Driven**: Emphasize configuration validation over implementation details\n- **Platform-Neutral**: Guide adaptation to different clusters (Slurm/Ray/K8s) without binding to specific platforms\n- **Practical Orientation**: Provide immediately applicable checklists and fix steps\n- **Risk-Aware**: MEDIUM risk level - configuration errors can cause job failures but not silent corruption\n\n## How to Update\n\n### When Configuration Dataclasses Change\n1. Update \"Key Configuration Dataclasses\" section with new/removed fields\n2. Update validation checklist in \"Configuration Validation Checklist\"\n3. Add examples of correct usage\n\n### When New Launcher/Scheduler Type Added\n1. Extend \"Launcher Selection Decision Tree\"\n2. Add to \"Resources & Reference Implementations\" table\n3. Document any new environment variables or configuration requirements\n\n### When Utility Functions Change\n1. Update references to `areal/infra/utils/launcher.py` functions\n2. Adjust \"Environment Variable Propagation Pattern\" if BASE_ENVIRONS changes\n3. Update diagnostic steps that rely on specific utility functions\n\n### Integration Updates\nKeep the \"Integration with Other Components\" table synchronized with CLAUDE.md agent list.\n\n================================================================================\n-->\n",".claude/agents/megatron-engine-expert.md":"---\nname: megatron-engine-expert\ndescription: MegatronEngine usage and integration expert. Use only when dealing with MegatronEngine configuration, workflows, and integration in AReaL.\ntools:\n  - Read\n  - Grep\n  - Glob\n  - Task\nmodel: opus\n---\n\n# MegatronEngine Usage Expert\n\nYou are an expert in MegatronEngine usage and integration in AReaL. Focus on\nconfiguration, workflows, and integration points rather than implementation details.\n\n## When to Activate\n\nUse **only** for MegatronEngine usage and integration guidance:\n\n- `MegatronEngine` configuration and initialization\n- Pipeline parallel (PP) workflow integration\n- Checkpointing and weight synchronization\n- Parallel strategy selection and tuning\n- Integration with rollout and evaluation workflows\n- Performance optimization and troubleshooting\n\n**Do NOT use for** general distributed training theory or low-level implementation\ndetails.\n\n## Core Concepts\n\nMegatronEngine provides comprehensive distributed training capabilities through multiple\nparallelism dimensions. It coordinates TP (tensor), PP (pipeline), DP (data), CP\n(context), EP (expert), and ETP (expert tensor) parallelism strategies.\n\nKey architectural principles:\n\n- **Pipeline Parallelism (PP)**: Splits model layers across stages for ultra-deep models\n- **Hybrid Parallelism**: Combines multiple parallel dimensions for optimal resource\n  utilization\n- **Unified Coordination**: Manages communication across all parallel groups\n\n### Primary Classes\n\n- **`MegatronEngine`** (`areal/engine/megatron_engine.py`): Main engine class\n  implementing distributed training coordination\n- **`ParallelStrategy`** (`areal/api/alloc_mode.py`): Configuration dataclass for\n  parallel dimensions\n- **`MegatronCheckpointManager`** (`areal/engine/megatron_utils/checkpointer.py`):\n  Checkpoint handling for distributed state\n\n### Key Methods\n\n**Initialization**: Initialize MegatronEngine with model, optimizer, parallel strategy,\nand additional configuration parameters as needed.\n\n**Training Operations:**\n\n- `forward()` / `backward()`: Coordinated across all parallel dimensions\n- `step()`: Weight update with gradient synchronization\n- `state_dict()` / `load_state_dict()`: Distributed checkpoint handling\n\n## Configuration\n\n### 1. Configuration Overview\n\nConfigure MegatronEngine using `ParallelStrategy` for model parallelism and\n`TrainEngineConfig` with `MegatronEngineConfig` for training settings.\n\n**Configuration Components**:\n\n- **ParallelStrategy** (`areal/api/alloc_mode.py`): Defines all parallel dimensions (TP,\n  PP, DP, CP, EP, ETP)\n- **TrainEngineConfig** (`areal/api/cli_args.py`): Core training configuration with\n  experiment settings\n- **MegatronEngineConfig** (`areal/api/cli_args.py`): Megatron-specific settings\n  including virtual pipeline parallelism\n\n**Configuration Approach**:\n\n1. Define comprehensive parallel strategy using `ParallelStrategy` with appropriate\n   dimensions\n1. Configure training engine via `TrainEngineConfig`, including `megatron` field for\n   MegatronEngineConfig\n1. Set distributed training options like checkpoint format and weight update method\n\n### 2. Engine Initialization\n\nInitialize MegatronEngine by importing `MegatronEngine` from\n`areal.engine.megatron_engine` and creating an instance with model, optimizer, parallel\nstrategy, and optional configuration parameters like virtual pipeline parallelism and\nsequence parallelism.\n\n### 3. Training Loop Integration\n\nIntegrate MegatronEngine into training workflows using standard training operations:\ngradient zeroing, forward pass, backward pass, and optimization steps. Utilize engine\nmethods for distributed checkpoint management including saving and loading checkpoints.\n\n### 4. Integration with Workflows\n\n- **Rollout workflows**: Receive updated weights from MegatronEngine\n- **Evaluation workflows**: Use engine for inference with current weights\n- **Checkpoint workflows**: Coordinate distributed checkpoint saving/loading\n\n## Common Usage Patterns\n\n### Parallel Strategy Selection\n\n| Model Type         | Recommended Parallel Strategy | Notes                                   |\n| ------------------ | ----------------------------- | --------------------------------------- |\n| Ultra-deep (>100B) | PP + TP + DP                  | Pipeline stages for depth, TP for width |\n| MoE models         | EP + TP + DP                  | Expert parallelism for MoE layers       |\n| Long sequence      | CP + TP                       | Context parallelism for sequence length |\n| Standard large     | TP + DP                       | Tensor + data parallelism baseline      |\n\n### Common Configuration Patterns\n\n**Balanced Pipeline**: Configure `ParallelStrategy` with significant\n`pipeline_parallel_size` and balanced `tensor_parallel_size`. Use `TrainEngineConfig`\nwith `MegatronEngineConfig` for pipeline-optimized training, typically distributing\nlayers across pipeline stages.\n\n**MoE-Optimized**: Set `ParallelStrategy` with high `expert_parallel_size` for MoE\nmodels, combined with `tensor_parallel_size`. Configure `TrainEngineConfig` with\n`MegatronEngineConfig` for expert-parallel training.\n\n## Workflow Integration\n\n### With Rollout Engines\n\n- **Weight synchronization**: MegatronEngine broadcasts updated weights\n- **Consistency**: Ensure all engines use same model version\n- **Performance**: Minimize communication overhead between training and rollout\n\n### With Checkpoint System\n\n- **Distributed checkpoints**: Each parallel group saves its shard\n- **Recovery**: Restore training state across all parallel dimensions\n- **Versioning**: Handle checkpoint format compatibility\n\n### With Monitoring\n\n- **Metrics collection**: Gather statistics across all parallel groups\n- **Health checks**: Verify communication between stages\n- **Performance profiling**: Identify bottlenecks in pipeline\n\n## Troubleshooting\n\n### Common Issues\n\n| Symptom              | Likely Cause                                   | First Steps                                      |\n| -------------------- | ---------------------------------------------- | ------------------------------------------------ |\n| **Training hang**    | Synchronization issue across parallel groups   | Verify all ranks reach same barrier              |\n| **Incorrect loss**   | Gradient flow or weight consistency problem    | Check weight synchronization between stages      |\n| **Out of memory**    | Unbalanced sharding across parallel dimensions | Review parallel strategy for memory distribution |\n| **Poor performance** | Communication overhead or pipeline bubble      | Profile communication and pipeline scheduling    |\n\n### Diagnostic Workflow\n\n1. **Verify engine initialization** - Confirm `MegatronEngine` is properly configured\n1. **Check parallel group consistency** - Ensure all ranks agree on group assignments\n1. **Validate communication paths** - Test inter-stage and inter-group communication\n1. **Review configuration** - Confirm parallel strategy matches hardware resources\n\n## Engine Selection Guide\n\n### When to Choose MegatronEngine\n\n**Choose MegatronEngine when:**\n\n- Training **ultra-deep models** (>100B parameters) requiring pipeline parallelism\n- Using **Mixture-of-Experts (MoE)** architectures with expert parallelism\n- Needing **hybrid parallelism** combining multiple strategies (TP+PP+EP)\n- Working with models that benefit from **pipeline scheduling** (1F1B, interleaved)\n\n**Choose FSDPEngine when:**\n\n- Training **large dense models** with standard parameter sharding\n- Simpler configuration and maintenance is preferred\n- Pipeline bubble overhead is undesirable\n- Limited expert parallelism requirements\n\n### Key Differentiators\n\n| Dimension            | MegatronEngine                      | FSDPEngine                    |\n| -------------------- | ----------------------------------- | ----------------------------- |\n| **Primary Strength** | Pipeline + expert parallelism       | Parameter sharding simplicity |\n| **Configuration**    | More complex, multiple dimensions   | Simpler, primarily FSDP2      |\n| **Best For**         | Ultra-deep, MoE, hybrid parallelism | Large dense models            |\n| **Integration**      | Full AReaL workflow support         | Standard training workflows   |\n\n## Getting Started\n\n1. **Setup**: Install dependencies with `uv sync --extra cuda`\n1. **Configure**: Set up `ParallelStrategy` for model parallelism and\n   `TrainEngineConfig` with `MegatronEngineConfig` for training settings\n1. **Integrate**: Reference configuration examples in YAML files under `examples/`\n   directory\n1. **Optimize**: Monitor pipeline efficiency, adjust parallel dimensions, and use\n   profiling tools\n\n## Implementation Structure\n\n**Core Engine**: `areal/engine/megatron_engine.py` - Main engine class implementing\ndistributed training coordination\n\n**Parallel Strategy Configuration**:\n\n- `areal/api/alloc_mode.py` - `MegatronParallelStrategy` (inherits from\n  `ParallelStrategy`) for Megatron-specific parallel dimensions\n- `areal/engine/megatron_utils/megatron.py` - Core Megatron utilities and helper\n  functions\n\n**Checkpointing and State Management**:\n\n- `areal/engine/megatron_utils/checkpointer.py` - `MegatronCheckpointManager` class for\n  distributed checkpoint handling\n- Integrated with Megatron Core checkpoint system for pipeline-parallel models\n\n**Model Parallelism Implementations**:\n\n- **Tensor Parallelism**: Integrated via Megatron Core tensor parallel layers\n- **Pipeline Parallelism**: Managed through Megatron Core pipeline parallel utilities\n- **Expert Parallelism**: Support for MoE models with expert-parallel distribution\n- **Sequence Parallelism**: Ulysses sequence parallelism for long-context training\n\n**Tree Attention Support**:\n\n- `areal/models/tree_attn/module_megatron.py` - Megatron-engine-specific tree attention\n  implementation\n\n**Configuration**:\n\n- `areal/api/cli_args.py` - `MegatronEngineConfig` with virtual pipeline parallelism and\n  other Megatron-specific settings\n- `areal/api/cli_args.py` - `TrainEngineConfig` with `megatron` field for\n  Megatron-engine configuration\n\n**Utilities**:\n\n- **Distributed Coordination**: Process group management and communication coordination\n- **Pipeline Scheduling**: 1F1B (one-forward-one-backward) and interleaved pipeline\n  schedules\n- **Gradient Synchronization**: Cross-stage gradient aggregation and optimization\n\n**Integration Points**:\n\n- **Weight Synchronization**: Broadcast updates to rollout engines via XCCL or\n  disk-based mechanisms\n- **Checkpoint Coordination**: Distributed checkpoint saving/loading across all parallel\n  dimensions\n- **Monitoring and Metrics**: Performance tracking across pipeline stages\n\n## Resources\n\n- **Main implementation**: `areal/engine/megatron_engine.py`\n- **Configuration**: `areal/api/cli_args.py` (`TrainEngineConfig` with\n  `MegatronEngineConfig`)\n- **Checkpointing**: `areal/engine/megatron_utils/checkpointer.py`\n- **Utilities**: `areal/engine/megatron_utils/megatron.py` for core Megatron utilities\n- **Examples**: YAML configuration files in `examples/` and\n  `tests/sft/config_megatron.yaml`\n\n## Response Guidance\n\nWhen assisting with MegatronEngine queries:\n\n1. **Provide architectural context** - Explain how MegatronEngine fits into AReaL\n   workflows\n1. **Focus on interfaces and usage** - Reference public APIs and integration points\n1. **Offer practical guidance** - Suggest configuration patterns and troubleshooting\n   steps\n1. **Highlight workflow integration** - Emphasize connections to rollout, evaluation,\n   and checkpointing\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/agents/megatron-engine-expert.md\nActivation: When MegatronEngine implementation or parallel strategy topics detected\n\n## Design Philosophy\n\n- **Usage-Focused Guidance**: Emphasize interfaces, workflows, and integration points over implementation details\n- **AReaL-Specific Context**: Highlight MegatronEngine's role in AReaL training workflows\n- **Complementary Expertise**: fsdp-engine-expert handles FSDP2/DTensor implementation; megatron-engine-expert focuses on MegatronEngine usage and configuration\n- **Model**: Opus (complex distributed system reasoning)\n\n## How to Update\n\n### When MegatronEngine Interface Changes\n1. Update \"Core Concepts\" section with new APIs (Primary Classes and Key Methods)\n2. Update \"Configuration\" section with new examples\n3. Verify file paths and import statements\n\n### When New Configuration Options Added\n1. Update \"Common Usage Patterns\" with new patterns\n2. Add to \"Parallel Strategy Selection\" table if applicable\n3. Update usage examples in \"Configuration\" section\n\n### When Integration Points Change\n1. Update \"Workflow Integration\" section\n2. Review \"Troubleshooting\" section for new issues\n3. Update \"Engine Selection Guide\" if choice criteria change\n\n================================================================================\n-->\n",".claude/agents/planner.md":"<!-- Skill available on-demand via /api/skills/inclusionAI/AReaL?path=.claude%2Fagents%2Fplanner.md -->",".claude/agents/simple-code-reviewer.md":"<!-- Skill available on-demand via /api/skills/inclusionAI/AReaL?path=.claude%2Fagents%2Fsimple-code-reviewer.md -->",".claude/commands/create-pr.md":"<!-- Skill available on-demand via /api/skills/inclusionAI/AReaL?path=.claude%2Fcommands%2Fcreate-pr.md -->",".claude/commands/gen-commit-msg.md":"<!-- Skill available on-demand via /api/skills/inclusionAI/AReaL?path=.claude%2Fcommands%2Fgen-commit-msg.md -->",".claude/commands/review-pr.md":"<!-- Skill available on-demand via /api/skills/inclusionAI/AReaL?path=.claude%2Fcommands%2Freview-pr.md -->",".claude/commands/translate-doc-zh.md":"<!-- Skill available on-demand via /api/skills/inclusionAI/AReaL?path=.claude%2Fcommands%2Ftranslate-doc-zh.md -->",".claude/rules/api-config.md":"---\npaths:\n  - areal/api/**\n---\n\n# API & Config Rules\n\n## Dataclass Conventions\n\n```python\n@dataclass\nclass XxxConfig:\n    \"\"\"One-line description.\n\n    Attributes:\n        field_name: Description with default explained.\n    \"\"\"\n    # Required fields first (no default)\n    required_field: str\n\n    # Optional fields with defaults\n    optional_field: int = 32\n\n    # Internal fields last (underscore prefix)\n    _internal: str = field(default=\"\", repr=False)\n```\n\n## Field Ordering\n\n1. Required fields (no default)\n1. Common optional fields\n1. Advanced/rare optional fields\n1. Internal fields (`_prefix`)\n\n## Validation\n\n- Use `__post_init__` for validation\n- Raise `ValueError` with clear message:\n  ```python\n  def __post_init__(self):\n      if self.batch_size <= 0:\n          raise ValueError(f\"batch_size must be positive, got {self.batch_size}\")\n  ```\n\n## Backward Compatibility\n\n- **Adding fields**: Add with default value (safe)\n- **Removing fields**: Deprecate first, remove in next major version\n- **Renaming fields**: Add new field, keep old with deprecation warning\n- **Changing types**: Avoid; use Union if necessary\n\n## CLI Integration\n\n- Fields exposed to CLI must have clear `help` in metadata\n- Use `Literal` for enum-like choices\n- Avoid complex nested types in CLI-exposed configs\n\n## Documentation\n\n- All public configs must have docstring\n- Document constraints (e.g., \"must be power of 2\")\n- Include example values for non-obvious fields\n",".claude/rules/code-style.md":"# Code Style Rules\n\nRules beyond pre-commit (ruff format/lint).\n\n## Design Patterns\n\n- **Prefer composition over inheritance**: Avoid deep class hierarchies\n  - Good: `Engine` holds a `Checkpointer` instance\n  - Avoid: `CheckpointableEngine(Engine)` →\n    `FSDPCheckpointableEngine(CheckpointableEngine)`\n- Keep inheritance shallow (≤2 levels when possible)\n- Use mixins sparingly; prefer explicit delegation\n\n## Logging\n\n- Use `areal.utils.logging.getLogger(name)` with PascalCase descriptive name, NOT\n  `print` or stdlib `logging`\n  - Good: `getLogger(\"RLVRWorkflow\")`, `getLogger(\"ArchonEngine\")`,\n    `getLogger(\"GSM8KReward\")`\n  - Avoid: `getLogger(__name__)` or dotted paths like `getLogger(\"areal.engine.fsdp\")`\n- For per-rank loggers: `[{Component} Rank {N}]` (e.g., `[FSDPEngine Rank 0]`)\n- Log levels:\n  - DEBUG: Detailed tracing (avoid in hot paths)\n  - INFO: Milestones (training start, checkpoint saved)\n  - WARNING: Recoverable issues\n  - ERROR: Failures requiring attention\n- Register new loggers in `areal/utils/logging.py` (`LOGGER_COLORS_EXACT` or\n  `LOGGER_PATTERNS`)\n- Test colors: `python -m areal.utils.logging`\n\n**Color Scheme:**\n\n| Color      | Category                               | Examples                           |\n| ---------- | -------------------------------------- | ---------------------------------- |\n| blue       | Infrastructure (Schedulers, Launchers) | `LocalScheduler`, `RayLauncher`    |\n| white      | Orchestration (Controllers, RPC)       | `TrainController`, `SGLangWrapper` |\n| purple     | RL-specific (Workflows, Rewards)       | `RLVRWorkflow`, `GSM8KReward`      |\n| green      | Data/Metrics (Stats, Dataset)          | `StatsLogger`, `RLTrainer`         |\n| cyan       | Compute backends (Engines, Platforms)  | `FSDPEngine`, `CUDAPlatform`       |\n| yellow/red | Warning/Error levels (override)        | Any logger at WARNING+             |\n\n## Performance Patterns\n\n- **Avoid GPU-CPU sync**: `.item()`, `.tolist()`, `print(tensor)` cause sync\n- **Prefer batch operations**: Avoid Python loops over tensor elements\n- **In-place ops**: Use when safe, but careful with autograd (`.add_()` vs `+`)\n\n## Naming Conventions\n\n| Type             | Pattern       | Example                             |\n| ---------------- | ------------- | ----------------------------------- |\n| Config dataclass | `XxxConfig`   | `GRPOConfig`, `FSDPConfig`          |\n| Engine class     | `XxxEngine`   | `FSDPEngine`, `ArchonEngine`        |\n| Workflow class   | `XxxWorkflow` | `RLVRWorkflow`, `MultiTurnWorkflow` |\n| Reward function  | `xxx_reward`  | `math_reward`, `code_reward`        |\n\n## Tensor Conventions\n\n- Shape convention: `[batch, seq_len, hidden]` or document clearly\n- Use `torch.Size` assertions for shape validation in debug\n- Prefer explicit dtype/device over implicit conversion\n\n## Import Style\n\n- Group: stdlib, third-party, areal (ruff handles order)\n- Avoid `from x import *` (CLAUDE.md rule)\n- Prefer explicit imports over module-level imports for large modules\n",".claude/rules/distributed.md":"---\npaths:\n  - areal/engine/**\n  - areal/experimental/**\n  - areal/engine/fsdp_utils/**\n---\n\n# Distributed Code Rules\n\n## Process Group Management\n\n- **Never create global process group** in module-level code\n- Always pass `process_group` explicitly, don't rely on default\n- Use `dist.get_rank(group)` not `dist.get_rank()` when group matters\n- Clean up process groups in `__del__` or context manager\n\n## DeviceMesh & DTensor\n\n- Mesh dimension names must match `ArchonParallelDims`: `dp_shard`, `tp`, `cp`, `ep`,\n  `etp`\n- DTensor requires consistent mesh across all ranks\n- Use `DTensor.from_local()` with correct placements\n\n## Communication Patterns\n\n- **All-reduce**: Must be called by all ranks in the group\n- **Broadcast**: Specify `src` rank explicitly\n- **Barrier**: Avoid unless necessary (debugging only)\n- Check `NCCL_ASYNC_ERROR_HANDLING` for deadlock debugging\n\n## Common Pitfalls\n\n| Issue         | Cause                            | Fix                            |\n| ------------- | -------------------------------- | ------------------------------ |\n| Hang          | Mismatched collective calls      | Ensure all ranks call same op  |\n| Wrong results | Incorrect reduction op           | Check `ReduceOp` (SUM vs MEAN) |\n| OOM           | Unsharded tensor on wrong device | Verify DTensor placements      |\n\n## Debugging\n\n- Set `TORCH_DISTRIBUTED_DEBUG=DETAIL` for verbose logging\n- Use `NCCL_DEBUG=INFO` for NCCL-level issues\n- See `/debug-distributed` skill for comprehensive guide\n",".claude/rules/testing.md":"---\npaths:\n  - '**/tests/**'\n  - '*_test.py'\n  - test_*.py\n---\n\n# Testing Rules\n\n## Pytest Markers\n\n| Marker                                  | When to Use          |\n| --------------------------------------- | -------------------- |\n| `@pytest.mark.slow`                     | Takes > 10 seconds   |\n| `@pytest.mark.asyncio`                  | Async test functions |\n| `@pytest.mark.skipif(cond, reason=...)` | Conditional skip     |\n| `@pytest.mark.parametrize(...)`         | Parameterized tests  |\n\n## Test Structure\n\n```python\ndef test_<what>_<condition>_<expected>():\n    \"\"\"Test that <what> does <expected> when <condition>.\"\"\"\n    # Arrange\n    ...\n    # Act\n    ...\n    # Assert\n    ...\n```\n\n## Mocking Distributed\n\n- Use `torch.distributed.fake_pg` for unit tests\n- Mock `dist.get_rank()` and `dist.get_world_size()` explicitly\n- Don't mock internals of FSDP/DTensor - use integration tests\n\n## GPU Test Constraints\n\n- **Always skip gracefully** when GPU unavailable:\n  ```python\n  CUDA_AVAILABLE = torch.cuda.is_available()\n\n  @pytest.mark.skipif(not CUDA_AVAILABLE, reason=\"CUDA not available\")\n  def test_gpu_feature():\n      ...\n  ```\n- Clean up GPU memory: `torch.cuda.empty_cache()` in fixtures\n- Use smallest possible model/batch for unit tests\n\n## Fixtures\n\n- Prefer `tmp_path` over manual temp directories\n- Use `monkeypatch` for environment variables\n- Scope expensive fixtures appropriately (`session` > `module` > `function`)\n\n## Assertions\n\n- Use `torch.testing.assert_close()` for tensor comparison\n- Specify `rtol`/`atol` explicitly for numerical tests\n- Avoid bare `assert tensor.equal()` - no useful error message\n",".claude/skills/add-archon-model/SKILL.md":"---\nname: add-archon-model\ndescription: Guide for adding a new model to the Archon engine. Use when user wants to add support for a new HuggingFace model architecture in ArchonEngine.\n---\n\n# Add Archon Model\n\nAdd support for a new HuggingFace model architecture in the Archon training engine.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a model to Archon?\"\n- User wants to support a new model family (e.g., Llama, Mistral, DeepSeek) in\n  ArchonEngine\n- User mentions adding a new `ModelSpec` or model type for Archon\n\n## Prerequisites\n\nBefore starting, ensure:\n\n- The target model is available on HuggingFace (has `config.json` with `model_type`)\n- You know the HuggingFace model ID (e.g., `meta-llama/Llama-3-8B`)\n- The model uses a standard transformer architecture (decoder-only)\n\n## Step-by-Step Guide\n\n### Step 1: Analyze the Target Model Architecture\n\nRead the HuggingFace model's source code to extract key architecture information.\n\n**Action**: Fetch and analyze the model's HuggingFace configuration and modeling files.\n\n1. Read the model's `config.json` (via `AutoConfig.from_pretrained`) to identify:\n\n   - `model_type` string (this is the key used for registry lookup)\n   - All architecture hyperparameters (hidden_size, num_layers, etc.)\n   - Any model-specific fields (e.g., `qk_norm`, `attention_bias`, MoE fields)\n\n1. Read the HuggingFace `modeling_*.py` source to identify:\n\n   - **Attention variant**: Does it have Q/K norm? Attention bias? Sliding window?\n     Multi-latent attention?\n   - **FFN variant**: SwiGLU (gate_proj + up_proj + down_proj)? GeGLU? Standard MLP?\n   - **MoE support**: Does it have MoE layers? What router type? Shared experts?\n   - **RoPE variant**: Standard RoPE? YaRN? NTK-aware scaling? What is the inv_freq\n     formula?\n   - **Normalization**: RMSNorm or LayerNorm? Pre-norm or post-norm? Elementwise affine?\n   - **Weight tying**: Does `tie_word_embeddings` appear in config?\n   - **State dict key names**: What are the HF weight key naming conventions?\n\n1. Summarize findings in a checklist like:\n\n```\nTarget model: <name>\nHF model_type: \"<model_type>\" (and variants like \"<model_type>_moe\" if applicable)\nAttention: [standard GQA / with QK norm / with bias / sliding window / ...]\nFFN: [SwiGLU / GeGLU / standard MLP / ...]\nMoE: [no / yes - num_experts, top_k, shared_experts]\nRoPE: [standard / YaRN / NTK-aware / ...]\nNorm: [RMSNorm / LayerNorm] with [pre-norm / post-norm]\nWeight tying: [yes / no]\n```\n\n### Step 2: Select the Reference Model\n\nChoose the closest existing implementation as a starting point:\n\n| Target characteristics               | Reference | Why                                     |\n| ------------------------------------ | --------- | --------------------------------------- |\n| Dense-only, standard GQA, no QK norm | `qwen2`   | Simplest baseline, pure dense           |\n| Has QK norm, or has MoE support      | `qwen3`   | Supports QK norm + MoE + shared experts |\n\n**Action**: Copy the reference model directory as the starting point:\n\n```\nareal/experimental/models/archon/<model>/\n  __init__.py\n  spec.py\n  model/\n    args.py\n    model.py\n    rope.py\n    state_dict_adapter.py\n  infra/\n    parallelize.py\n```\n\n### Step 3: Implement `args.py`\n\nAdapt `<Model>ModelArgs` to match the target model's HuggingFace config fields.\n\n**Key changes from reference**:\n\n1. Update the `@dataclass` fields to match the target model's hyperparameters:\n\n   - Field names should use Archon conventions (`dim`, `n_layers`, `n_heads`,\n     `n_kv_heads`, `vocab_size`, `head_dim`, `hidden_dim`, `norm_eps`, `rope_theta`,\n     etc.)\n   - Default values should match the smallest variant of the target model\n   - Add model-specific fields (e.g., `attention_bias`, `qk_norm`, `sliding_window`)\n\n1. Update `from_hf_config()` to correctly map HuggingFace config attributes:\n\n   - Use `getattr(hf_config, \"field_name\", default)` for optional fields\n   - Handle variant-specific fields (e.g., MoE fields only present in MoE variants)\n   - The method must return an instance of the model args class\n\n**Critical**: Verify every field mapping against the HF model's `config.json`. Incorrect\nmappings here cause silent errors downstream.\n\n**Base class contract** (`BaseModelArgs`):\n\n```python\n@dataclass\nclass <Model>ModelArgs(BaseModelArgs):\n    # ... model-specific fields ...\n\n    @classmethod\n    def from_hf_config(\n        cls,\n        hf_config: PretrainedConfig,\n        is_critic: bool = False,\n        **kwargs,\n    ) -> <Model>ModelArgs:\n        # Map HF config fields to Archon model args\n        ...\n```\n\n### Step 4: Implement `model.py`\n\nAdapt the model architecture to match the target model.\n\n**Key components to adapt**:\n\n1. **Normalization** (`RMSNorm` or similar):\n\n   - Check if `elementwise_affine` is configurable\n   - Check the epsilon default value\n   - If the model uses `LayerNorm`, implement accordingly\n\n1. **Attention** module:\n\n   - Q/K/V projection: Check bias presence (`nn.Linear(..., bias=True/False)`)\n   - QK norm: Add `q_norm`/`k_norm` if the model has them, remove if it doesn't\n   - GQA: `n_kv_heads` \\< `n_heads` for grouped-query attention\n   - Ulysses SP: Keep the `set_cp_group` / `_sp_enabled` pattern from the reference\n   - Output projection: Check bias presence\n\n1. **FeedForward** module:\n\n   - SwiGLU: `w2(silu(w1(x)) * w3(x))` -- most common for modern LLMs\n   - Check bias in linear layers\n   - For MoE models: `MoE` module replaces `FeedForward` on designated layers\n\n1. **TransformerBlock**: Pre-norm (most modern LLMs) vs post-norm\n\n   - MoE layer detection via `_is_moe_layer()` if applicable\n\n1. **Top-level Model** (`<Model>Model(BaseArchonModel)`):\n\n   - `tok_embeddings`, `layers` (as `ModuleDict`), `norm`, `output`/`score`\n   - `init_weights()`: Match initialization scheme from HF\n   - `init_buffers()`: RoPE cache + MoE buffers\n   - `forward()`: Must follow `BaseArchonModel` signature:\n     `(tokens, positions, cu_seqlens, max_seqlen, tree_attn_meta=None) -> Tensor`\n\n**Base class contract** (`BaseArchonModel`):\n\n```python\nclass <Model>Model(BaseArchonModel):\n    def forward(self, tokens, positions, cu_seqlens, max_seqlen, tree_attn_meta=None) -> torch.Tensor: ...\n    def init_weights(self) -> None: ...\n    def init_buffers(self, buffer_device) -> None: ...\n```\n\n### Step 5: Implement `rope.py`\n\nHandle the rotary position embedding variant.\n\n**Options**:\n\n1. **Standard RoPE** (same as qwen2/qwen3): Re-export from qwen2:\n\n   ```python\n   from areal.experimental.models.archon.qwen2.model.rope import (\n       apply_rotary_emb,\n       precompute_rope_cache,\n       repeat_kv,\n       reshape_for_broadcast,\n       rotate_half,\n   )\n   ```\n\n1. **Custom RoPE** (YaRN, NTK-aware, etc.): Implement custom `precompute_rope_cache()`\n   and `apply_rotary_emb()` functions. The key difference is usually in how `inv_freq`\n   is computed (scaling factors, interpolation, etc.).\n\n### Step 6: Implement `state_dict_adapter.py`\n\nMap between HuggingFace and Archon weight key names.\n\n**This is the most error-prone step.** The adapter must correctly handle:\n\n1. **Key name mapping** (`from_hf_map` dict):\n\n   - Embedding: `model.embed_tokens.weight` -> `tok_embeddings.weight`\n   - Attention: `model.layers.{}.self_attn.q_proj.weight` ->\n     `layers.{}.attention.wq.weight`\n   - FFN: `model.layers.{}.mlp.gate_proj.weight` -> `layers.{}.feed_forward.w1.weight`\n   - Norms: `model.layers.{}.input_layernorm.weight` ->\n     `layers.{}.attention_norm.weight`\n   - Output: `lm_head.weight` -> `output.weight`\n   - Skip keys (set to `None`): `rotary_emb.inv_freq` (computed at runtime)\n   - Model-specific keys: bias terms, QK norm weights, etc.\n\n1. **Reverse mapping** (`to_hf_map`): Auto-generated from `from_hf_map`\n\n1. **MoE expert weights** (if applicable): 3D\\<->2D conversion for expert weights. Copy\n   the MoE handling from qwen3 if the model has MoE.\n\n1. **Weight tying**: Skip `output.weight` during `to_hf()` if `tie_word_embeddings=True`\n\n**Verification approach**: After implementation, the adapter should satisfy:\n\n```python\n# Roundtrip: archon -> hf -> archon preserves all keys\nhf_sd = adapter.to_hf(archon_sd)\nroundtrip_sd = adapter.from_hf(hf_sd)\nassert set(roundtrip_sd.keys()) == set(archon_sd.keys())\n```\n\n**Base class contract** (`BaseStateDictAdapter`):\n\n```python\nclass <Model>StateDictAdapter(BaseStateDictAdapter):\n    def from_hf(self, hf_state_dict) -> dict[str, Any]: ...\n    def to_hf(self, archon_state_dict) -> dict[str, Any]: ...\n    def convert_single_to_hf(self, name, tensor) -> list[tuple[str, torch.Tensor]]: ...\n```\n\n### Step 7: Implement `parallelize.py`\n\nDefine the parallelization strategy for the model.\n\n**The parallelize function** applies parallelism in this order:\n\n1. TP (Tensor Parallelism) -- shard attention/FFN across devices\n1. EP (Expert Parallelism) -- for MoE models only\n1. CP (Context Parallelism / Ulysses SP) -- sequence parallelism\n1. AC (Activation Checkpointing) -- memory optimization\n1. torch.compile -- compilation optimization\n1. FSDP (Fully Sharded Data Parallelism) -- data parallelism\n\n**Key adaptations by model architecture**:\n\n- **Attention with QK norm**: wq/wk use `use_local_output=False` (DTensor output for\n  norm), add `SequenceParallel(sequence_dim=2)` for q_norm/k_norm\n- **Attention without QK norm**: wq/wk/wv all use `use_local_output=True`\n- **Attention with bias**: Bias terms follow the same parallel plan as their weights\n- **MoE layers**: Separate TP plan for MoE input/output, router gate, and expert\n  weights. Copy from qwen3's `apply_moe_ep_tp()` and `apply_non_moe_tp()`\n- **Dense-only models**: Simpler plan without MoE handling. Copy from qwen2\n\n**Function signature** (must match `ParallelizeFn` protocol):\n\n```python\ndef parallelize_<model>(\n    model: nn.Module,\n    parallel_dims: ArchonParallelDims,\n    param_dtype: torch.dtype = torch.bfloat16,\n    reduce_dtype: torch.dtype = torch.float32,\n    loss_parallel: bool = True,\n    cpu_offload: bool = False,\n    reshard_after_forward_policy: str = \"default\",\n    ac_config: ActivationCheckpointConfig | None = None,\n    enable_compile: bool = True,\n) -> nn.Module:\n```\n\n### Step 8: Create `spec.py` and Register\n\nAssemble the `ModelSpec` and register it.\n\n```python\nfrom areal.experimental.models.archon.model_spec import ModelSpec, register_model_spec\nfrom areal.experimental.models.archon.pipeline_parallel import pipeline_llm\nfrom areal.experimental.models.archon.<model>.infra.parallelize import parallelize_<model>\nfrom areal.experimental.models.archon.<model>.model.args import <Model>ModelArgs\nfrom areal.experimental.models.archon.<model>.model.model import <Model>Model\nfrom areal.experimental.models.archon.<model>.model.state_dict_adapter import (\n    <Model>StateDictAdapter,\n)\n\n<MODEL>_SPEC = ModelSpec(\n    name=\"<Model>\",\n    model_class=<Model>Model,\n    model_args_class=<Model>ModelArgs,\n    state_dict_adapter_class=<Model>StateDictAdapter,\n    parallelize_fn=parallelize_<model>,\n    supported_model_types=frozenset({\"<model_type>\"}),  # From HF config.json\n    pipelining_fn=pipeline_llm,\n)\n\n# Auto-register when module is imported\nregister_model_spec(<MODEL>_SPEC)\n\n__all__ = [\"<MODEL>_SPEC\"]\n```\n\n**Note**: `supported_model_types` should include all HF `model_type` strings that this\nimplementation handles (e.g., `{\"qwen3\", \"qwen3_moe\"}` for Qwen3).\n\n### Step 9: Register in `__init__.py`\n\nAdd the import to `areal/experimental/models/archon/__init__.py`:\n\n```python\nfrom areal.experimental.models.archon.<model> import spec as <model>_spec  # noqa: F401\n```\n\nThis triggers auto-registration when the module is imported.\n\n### Step 10: Verify and Test\n\nVerification should be done in stages, adapting based on available hardware and the test\npatterns in `tests/experimental/archon/`.\n\n**Before writing tests**, examine the existing test files to understand current\npatterns:\n\n```\ntests/experimental/archon/\n  conftest.py             -- Pytest configuration (version checks)\n  utils.py                -- Shared utilities (model loading, comparison)\n  test_qwen3_args.py      -- Args unit tests (CPU-only)\n  test_state_dict_adapter.py  -- State dict roundtrip tests\n  test_weight_sync.py     -- Weight completeness tests (meta device)\n  test_forward.py         -- Forward precision comparison (single GPU)\n  ...\n```\n\n**Test stages** (write tests appropriate for the model's complexity):\n\n#### Stage 1: Args Tests (CPU-only, always write these)\n\nTest `from_hf_config()` with mock HuggingFace configs:\n\n```python\n# Pattern: Create mock PretrainedConfig, verify args mapping\nfrom unittest.mock import MagicMock\n\ndef test_args_from_hf_config():\n    hf_config = MagicMock()\n    hf_config.hidden_size = 4096\n    hf_config.num_hidden_layers = 32\n    # ... set all required fields\n    args = <Model>ModelArgs.from_hf_config(hf_config)\n    assert args.dim == 4096\n    assert args.n_layers == 32\n```\n\n#### Stage 2: State Dict Adapter Tests (CPU-only)\n\nTest key mapping roundtrip:\n\n```python\ndef test_state_dict_roundtrip():\n    # Create adapter with mock config\n    adapter = <Model>StateDictAdapter(mock_config)\n    # Create fake archon state dict with expected keys\n    archon_sd = {\"tok_embeddings.weight\": torch.randn(vocab, dim), ...}\n    # Roundtrip\n    hf_sd = adapter.to_hf(archon_sd)\n    roundtrip = adapter.from_hf(hf_sd)\n    assert set(roundtrip.keys()) == set(archon_sd.keys())\n```\n\n#### Stage 3: Weight Completeness (meta device, CPU-only)\n\nVerify all model parameters have HF mappings:\n\n```python\ndef test_weight_completeness():\n    # Create model on meta device\n    with torch.device(\"meta\"):\n        model = <Model>Model(args)\n    adapter = <Model>StateDictAdapter(hf_config)\n    # Check every archon param has a HF mapping\n    for name, _ in model.named_parameters():\n        hf_pairs = adapter.convert_single_to_hf(name, torch.empty(0))\n        assert len(hf_pairs) > 0, f\"No HF mapping for {name}\"\n```\n\n#### Stage 4: Forward Precision (single GPU, if available)\n\nCompare Archon model output against HuggingFace reference:\n\n```python\n@pytest.mark.skipif(not torch.cuda.is_available(), reason=\"Requires CUDA\")\ndef test_forward_matches_hf():\n    # Load both HF and Archon models\n    # Run forward on same input\n    # Compare logits within tolerance\n```\n\n**Important**: Do NOT hardcode the test categories. Inspect the existing test files in\n`tests/experimental/archon/` and follow the same patterns, fixtures, and markers. Adapt\ntest scope to the model's specific features (e.g., add MoE-specific tests only if the\nmodel has MoE).\n\n## Reference Implementations\n\n| Model | Directory                                 | Features                                                |\n| ----- | ----------------------------------------- | ------------------------------------------------------- |\n| Qwen2 | `areal/experimental/models/archon/qwen2/` | Dense, attention bias, no QK norm                       |\n| Qwen3 | `areal/experimental/models/archon/qwen3/` | Dense + MoE, QK norm, no attention bias, shared experts |\n\n## Architecture Decision Map\n\n| Feature             | qwen2    | qwen3                      | What to check in target model                            |\n| ------------------- | -------- | -------------------------- | -------------------------------------------------------- |\n| Attention bias      | Yes      | No                         | `attention_bias` in HF config                            |\n| QK norm             | No       | Yes                        | `qk_norm` in HF config or QKNorm module in modeling file |\n| MoE                 | No       | Yes                        | `num_experts`/`num_local_experts` in HF config           |\n| Shared experts      | No       | Yes                        | `num_shared_experts` in HF config                        |\n| Decoder sparse step | No       | Yes                        | `decoder_sparse_step` in HF config                       |\n| Weight tying        | Both     | Both                       | `tie_word_embeddings` in HF config                       |\n| RoPE                | Standard | Standard (re-export qwen2) | Check inv_freq formula in HF modeling code               |\n\n## Common Mistakes\n\n- Not mapping all HF keys in `state_dict_adapter.py` (causes silent weight drops)\n- Wrong `from_hf_config()` field mapping (uses wrong HF config attribute name)\n- Forgetting to handle `None` keys in `from_hf_map` (keys to skip like\n  `rotary_emb.inv_freq`)\n- Missing MoE expert weight 3D\\<->2D conversion when model has MoE\n- Wrong TP plan for attention with/without QK norm (`use_local_output` must match)\n- Forgetting to add import line in `areal/experimental/models/archon/__init__.py`\n- Not including all `model_type` variants in `supported_model_types` frozenset\n- Using `print` instead of `areal.utils.logging.getLogger()`\n\n## File Checklist\n\nAfter completion, verify all files exist and are consistent:\n\n- [ ] `areal/experimental/models/archon/<model>/__init__.py`\n- [ ] `areal/experimental/models/archon/<model>/spec.py` -- ModelSpec + register\n- [ ] `areal/experimental/models/archon/<model>/model/args.py` -- ModelArgs +\n  from_hf_config\n- [ ] `areal/experimental/models/archon/<model>/model/model.py` -- Model + Attention +\n  FFN\n- [ ] `areal/experimental/models/archon/<model>/model/rope.py` -- RoPE (or re-export)\n- [ ] `areal/experimental/models/archon/<model>/model/state_dict_adapter.py` -- Key\n  mapping\n- [ ] `areal/experimental/models/archon/<model>/infra/parallelize.py` -- Parallel\n  strategy\n- [ ] `areal/experimental/models/archon/__init__.py` -- Import line added\n- [ ] `tests/experimental/archon/test_<model>_*.py` -- Tests\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/skills/add-archon-model/SKILL.md\nInvocation: /add-archon-model <model_name>\n\n## Purpose\n\nSemi-automated guide for adding new model architectures to the Archon training engine.\nUnlike simpler skills (add-reward, add-dataset), this skill actively guides Claude to:\n1. Analyze HuggingFace source code to extract architecture details\n2. Select the closest reference implementation (qwen2 or qwen3)\n3. Generate code skeletons adapted to the target architecture\n4. Create appropriate tests based on existing test patterns\n\n## How to Update\n\n### When New Reference Models Are Added\n1. Add to \"Reference Implementations\" table\n2. Update \"Architecture Decision Map\" with new feature columns\n3. Update Step 2 (reference selection) with new options\n\n### When Base Classes Change\n1. Update contract signatures in Steps 3, 4, 6, 7\n2. Update file checklist if new files are required\n\n### When ModelSpec Changes\n1. Update Step 8 with new ModelSpec fields\n2. Update spec.py template\n\n### When Test Patterns Change\n1. Update Step 10 with new test patterns\n2. Do NOT hardcode test categories -- keep it flexible\n\n### Important Design Decisions\n- This skill is SEMI-AUTOMATED: Claude should read HF source and generate code,\n  not just provide templates for the user to fill in manually\n- The skill references existing test files rather than hardcoding test categories,\n  ensuring it stays current as the test suite evolves\n- Reference model selection (qwen2 vs qwen3) is based on MoE and QK norm presence\n\n================================================================================\n-->\n",".claude/skills/add-dataset/SKILL.md":"---\nname: add-dataset\ndescription: Guide for adding a new dataset loader to AReaL. Use when user wants to add a new dataset.\n---\n\n# Add Dataset\n\nAdd a new dataset loader to AReaL.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a dataset?\"\n- User wants to integrate a new dataset\n- User mentions creating a dataset loader\n\n## Step-by-Step Guide\n\n### Step 1: Create Dataset File\n\nCreate `areal/dataset/<name>.py`:\n\n```python\nfrom datasets import Dataset, load_dataset\n\n\ndef get_<name>_sft_dataset(\n    path: str,\n    split: str,\n    tokenizer,\n    max_length: int | None = None,\n) -> Dataset:\n    \"\"\"Load dataset for SFT training.\n\n    Args:\n        path: Path to dataset (HuggingFace hub or local path)\n        split: Dataset split (train/validation/test)\n        tokenizer: Tokenizer for processing\n        max_length: Maximum sequence length (optional)\n\n    Returns:\n        HuggingFace Dataset with processed samples\n    \"\"\"\n    dataset = load_dataset(path=path, split=split)\n\n    def process(sample):\n        # Tokenize the full sequence (prompt + response)\n        seq_token = tokenizer.encode(\n            sample[\"question\"] + sample[\"answer\"] + tokenizer.eos_token\n        )\n        prompt_token = tokenizer.encode(sample[\"question\"])\n        # Loss mask: 0 for prompt, 1 for response\n        loss_mask = [0] * len(prompt_token) + [1] * (len(seq_token) - len(prompt_token))\n        return {\"input_ids\": seq_token, \"loss_mask\": loss_mask}\n\n    dataset = dataset.map(process).remove_columns([\"question\", \"answer\"])\n\n    if max_length is not None:\n        dataset = dataset.filter(lambda x: len(x[\"input_ids\"]) <= max_length)\n\n    return dataset\n\n\ndef get_<name>_rl_dataset(\n    path: str,\n    split: str,\n    tokenizer,\n    max_length: int | None = None,\n) -> Dataset:\n    \"\"\"Load dataset for RL training.\n\n    Args:\n        path: Path to dataset\n        split: Dataset split\n        tokenizer: Tokenizer for length filtering\n        max_length: Maximum sequence length\n\n    Returns:\n        HuggingFace Dataset with prompts and answers for reward computation\n    \"\"\"\n    dataset = load_dataset(path=path, split=split)\n\n    def process(sample):\n        messages = [\n            {\n                \"role\": \"user\",\n                \"content\": sample[\"question\"],\n            }\n        ]\n        return {\"messages\": messages, \"answer\": sample[\"answer\"]}\n\n    dataset = dataset.map(process).remove_columns([\"question\"])\n\n    if max_length is not None:\n\n        def filter_length(sample):\n            content = sample[\"messages\"][0][\"content\"]\n            tokens = tokenizer.encode(content)\n            return len(tokens) <= max_length\n\n        dataset = dataset.filter(filter_length)\n\n    return dataset\n```\n\n### Step 2: Register in __init__.py\n\nUpdate `areal/dataset/__init__.py`:\n\n```python\n# Add to VALID_DATASETS\nVALID_DATASETS = [\n    # ... existing datasets\n    \"<name>\",\n]\n\n# Add to _get_custom_dataset function\ndef _get_custom_dataset(name: str, ...):\n    # ... existing code\n    elif name == \"<name>\":\n        from areal.dataset.<name> import get_<name>_sft_dataset, get_<name>_rl_dataset\n        if dataset_type == \"sft\":\n            return get_<name>_sft_dataset(path, split, max_length, tokenizer)\n        else:\n            return get_<name>_rl_dataset(path, split, max_length, tokenizer)\n```\n\n### Step 3: Add Config (Optional)\n\nIf the dataset needs special configuration, add to `areal/api/cli_args.py`:\n\n```python\n@dataclass\nclass TrainDatasetConfig:\n    # ... existing fields\n    <name>_specific_field: Optional[str] = None\n```\n\n### Step 4: Add Tests\n\nCreate `tests/test_<name>_dataset.py`:\n\n```python\nimport pytest\nfrom areal.dataset.<name> import get_<name>_sft_dataset, get_<name>_rl_dataset\n\ndef test_sft_dataset_loads(tokenizer):\n    dataset = get_<name>_sft_dataset(\"path/to/data\", split=\"train\", tokenizer=tokenizer)\n    assert len(dataset) > 0\n    assert \"input_ids\" in dataset.column_names\n    assert \"loss_mask\" in dataset.column_names\n\ndef test_rl_dataset_loads(tokenizer):\n    dataset = get_<name>_rl_dataset(\"path/to/data\", split=\"train\", tokenizer=tokenizer)\n    assert len(dataset) > 0\n    assert \"messages\" in dataset.column_names\n    assert \"answer\" in dataset.column_names\n```\n\n## Reference Implementations\n\n| Dataset    | File                               | Description              |\n| ---------- | ---------------------------------- | ------------------------ |\n| GSM8K      | `areal/dataset/gsm8k.py`           | Math word problems       |\n| Geometry3K | `areal/dataset/geometry3k.py`      | Geometry problems        |\n| CLEVR      | `areal/dataset/clevr_count_70k.py` | Visual counting          |\n| HH-RLHF    | `areal/dataset/hhrlhf.py`          | Helpfulness/Harmlessness |\n| TORL       | `areal/dataset/torl_data.py`       | Tool-use RL              |\n\n## Required Fields\n\n### SFT Dataset\n\n```python\n{\n    \"messages\": [\n        {\"role\": \"user\", \"content\": \"...\"},\n        {\"role\": \"assistant\", \"content\": \"...\"},\n    ]\n}\n```\n\n### RL Dataset\n\n```python\n{\n    \"messages\": [\n        {\"role\": \"user\", \"content\": \"...\"},\n    ],\n    \"answer\": \"ground_truth_for_reward\",\n    # Optional metadata for reward function\n}\n```\n\n## Common Mistakes\n\n- ❌ Returning `List[Dict]` instead of HuggingFace `Dataset`\n- ❌ Using Python loops instead of `dataset.map()`/`filter()`\n- ❌ Missing `\"messages\"` field for RL datasets\n- ❌ Wrong message format (should be list of dicts with `role` and `content`)\n- ❌ Not registering in `__init__.py`\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/skills/add-dataset/SKILL.md\nInvocation: /add-dataset <name>\n\n## Purpose\n\nStep-by-step guide for adding new dataset loaders.\n\n## How to Update\n\n### When Dataset API Changes\n1. Update the code templates\n2. Update required fields section\n3. Update registration example\n\n### When New Dataset Types Added\n1. Add to \"Reference Implementations\" table\n2. Add any new required fields\n\n================================================================================\n-->\n",".claude/skills/add-reward/SKILL.md":"---\nname: add-reward\ndescription: Guide for adding a new reward function to AReaL. Use when user wants to create a reward function.\n---\n\n# Add Reward\n\nAdd a new reward function to AReaL.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a reward function?\"\n- User wants to implement custom rewards\n- User mentions reward computation\n\n## Step-by-Step Guide\n\n### Step 1: Create Reward File\n\nCreate `areal/reward/<name>.py`:\n\n```python\nfrom typing import Any\n\nfrom areal.utils import logging\n\nlogger = logging.getLogger(\"MyReward\")\n\n\ndef <name>_reward_fn(\n    prompt: str,\n    completions: str,\n    prompt_ids,\n    completion_ids,\n    answer: str | None = None,\n    **kwargs: Any,\n) -> float:\n    \"\"\"Compute reward for a single completion.\n\n    Args:\n        prompt: Prompt string\n        completions: Completion string (model output)\n        prompt_ids: Tokenized prompt IDs\n        completion_ids: Tokenized completion IDs\n        answer: Ground truth answer from dataset (optional)\n        **kwargs: Additional data from dataset\n\n    Returns:\n        Reward value (float), typically 0.0 or 1.0\n    \"\"\"\n    try:\n        # Extract answer from completion\n        extracted = _extract_answer(completions)\n\n        # Compare with ground truth\n        if answer is not None and extracted == str(answer):\n            return 1.0\n        return 0.0\n    except Exception:\n        logger.warning(\"Exception in reward computation\", exc_info=True)\n        return 0.0\n\n\ndef _extract_answer(completion: str) -> str:\n    \"\"\"Extract the answer from a completion string.\n\n    Implement your extraction logic here.\n    \"\"\"\n    # Example: Extract content from \\boxed{}\n    import re\n\n    match = re.search(r\"\\\\boxed\\{([^}]+)\\}\", completion)\n    if match:\n        return match.group(1).strip()\n    return completion.strip()\n```\n\n### Step 2: Register in __init__.py\n\nUpdate `areal/reward/__init__.py`:\n\n```python\n# Add to VALID_REWARD_FN\nVALID_REWARD_FN = [\n    # ... existing reward functions\n    \"<name>\",\n]\n\n# Add to get_reward_fn function\ndef get_reward_fn(name: str, **kwargs):\n    # ... existing code\n    elif name == \"<name>\":\n        from areal.reward.<name> import <name>_reward_fn\n        return <name>_reward_fn\n```\n\n### Step 3: Handle Blocking Operations\n\nIf your reward function uses blocking operations (e.g., API calls, model inference), the\nworkflow will wrap it with `AsyncRewardWrapper`:\n\n```python\n# In your workflow\nfrom areal.reward import AsyncRewardWrapper\n\nself.reward_fn = AsyncRewardWrapper(reward_fn)\n\n# Then call it asynchronously\nrewards = await self.reward_fn(prompt, completions, **data)\n```\n\n### Step 4: Add Tests\n\nCreate `tests/test_<name>_reward.py`:\n\n```python\nimport pytest\nfrom areal.reward.<name> import <name>_reward_fn\n\ndef test_reward_correct_answer():\n    reward = <name>_reward_fn(\n        prompt=\"What is 2+2?\",\n        completions=\"The answer is \\\\boxed{4}\",\n        prompt_ids=None,\n        completion_ids=None,\n        answer=\"4\",\n    )\n    assert reward == 1.0\n\ndef test_reward_wrong_answer():\n    reward = <name>_reward_fn(\n        prompt=\"What is 2+2?\",\n        completions=\"The answer is \\\\boxed{5}\",\n        prompt_ids=None,\n        completion_ids=None,\n        answer=\"4\",\n    )\n    assert reward == 0.0\n```\n\n## Reference Implementations\n\n| Reward     | File                              | Description                  |\n| ---------- | --------------------------------- | ---------------------------- |\n| GSM8K      | `areal/reward/gsm8k.py`           | Math answer verification     |\n| Geometry3K | `areal/reward/geometry3k.py`      | Geometry answer verification |\n| CLEVR      | `areal/reward/clevr_count_70k.py` | Counting verification        |\n| MathVerify | `areal/reward/math_verify.py`     | General math verification    |\n\n## Function Signature\n\nAll reward functions must follow this signature:\n\n```python\ndef reward_fn(\n    prompt: str,               # Input prompt string\n    completions: str,          # Model completion string\n    prompt_ids,                # Tokenized prompt\n    completion_ids,            # Tokenized completion\n    **kwargs: Any,             # Additional data from dataset (e.g., answer)\n) -> float:                    # Reward value (typically 0.0 or 1.0)\n```\n\n**Note**: The reward function is called once per sample. Batching is handled by\n`AsyncRewardWrapper` in the workflow.\n\n## Key Requirements\n\n1. **Deterministic**: Same inputs should produce same outputs\n1. **Return float**: Output is a single float value per sample\n1. **No blocking in async context**: Use `AsyncRewardWrapper` if needed\n1. **Logging**: Use `areal.utils.logging`, not `print`\n1. **Handle exceptions**: Return 0.0 on error, don't raise\n\n## Common Mistakes\n\n- ❌ Returning a tensor instead of a float\n- ❌ Expecting batched inputs (reward is called per sample)\n- ❌ Non-deterministic behavior\n- ❌ Blocking operations without `AsyncRewardWrapper`\n- ❌ Raising exceptions instead of returning 0.0\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/skills/add-reward/SKILL.md\nInvocation: /add-reward <name>\n\n## Purpose\n\nStep-by-step guide for adding new reward functions.\n\n## How to Update\n\n### When Reward API Changes\n1. Update the function signature section\n2. Update the code template\n3. Update key requirements\n\n### When New Reward Patterns Emerge\n1. Add to \"Reference Implementations\" table\n2. Add examples for new patterns\n\n================================================================================\n-->\n",".claude/skills/add-unit-tests/SKILL.md":"---\nname: add-unit-tests\ndescription: Guide for adding unit tests to AReaL. Use when user wants to add tests for new functionality or increase test coverage.\n---\n\n# Add Unit Tests\n\nAdd unit tests to AReaL following the project's testing conventions.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add tests?\"\n- User wants to increase test coverage\n- User needs to write tests for new functionality\n- User wants to understand AReaL testing patterns\n\n## Step-by-Step Guide\n\n### Step 1: Understand Test Types\n\nAReaL has two main test categories:\n\n| Test Type             | Purpose                            | Location Pattern                   | How It Runs                                |\n| --------------------- | ---------------------------------- | ---------------------------------- | ------------------------------------------ |\n| **Unit Tests**        | Test individual functions/modules  | `tests/test_<module>_<feature>.py` | Directly via pytest                        |\n| **Distributed Tests** | Test distributed/parallel behavior | `tests/torchrun/run_*.py`          | Via torchrun (called by pytest subprocess) |\n\n**Note**: All tests are invoked via pytest. Distributed tests use `torchrun` but are\nstill called from pytest test files.\n\n### Step 2: Create Test File Structure\n\nCreate test file with naming convention: `test_<module>_<feature>.py`\n\n```python\nimport pytest\nimport torch\n\n# Import the module to test\nfrom areal.dataset.gsm8k import get_gsm8k_sft_dataset\nfrom tests.utils import get_dataset_path  # Optional test utilities\n# For mocking tokenizer: from unittest.mock import MagicMock\n```\n\n### Step 3: Write Test Functions\n\nFollow Arrange-Act-Assert pattern:\n\n```python\ndef test_function_under_condition_returns_expected():\n    \"\"\"Test that function returns expected value under condition.\"\"\"\n    # Arrange\n    input_data = 5\n    expected_output = 10\n\n    # Act\n    result = function_under_test(input_data)\n\n    # Assert\n    assert result == expected_output\n```\n\n### Step 4: Add Pytest Markers and CI Strategy\n\nUse appropriate pytest markers:\n\n| Marker                                  | When to Use                                                  |\n| --------------------------------------- | ------------------------------------------------------------ |\n| `@pytest.mark.slow`                     | Test takes > 10 seconds (excluded from CI by default)        |\n| `@pytest.mark.ci`                       | Slow test that must run in CI (use with `@pytest.mark.slow`) |\n| `@pytest.mark.asyncio`                  | Async test functions                                         |\n| `@pytest.mark.skipif(cond, reason=...)` | Conditional skip                                             |\n| `@pytest.mark.parametrize(...)`         | Parameterized tests                                          |\n\n**CI Test Strategy**:\n\n- `@pytest.mark.slow`: Excluded from CI by default (CI runs `pytest -m \"not slow\"`)\n- `@pytest.mark.slow` + `@pytest.mark.ci`: Slow but must run in CI\n- No marker: Runs in CI (fast unit tests)\n\n```python\n@pytest.mark.asyncio\nasync def test_async_function():\n    result = await async_function()\n    assert result == expected\n\n@pytest.mark.skipif(not torch.cuda.is_available(), reason=\"CUDA not available\")\ndef test_gpu_feature():\n    tensor = torch.tensor([1, 2, 3], device=\"cuda\")\n    # ... assertions\n\n@pytest.mark.parametrize(\"batch_size\", [1, 4, 16])\ndef test_with_parameters(batch_size):\n    # Parameterized test\n\n@pytest.mark.slow\ndef test_slow_function():\n    # Excluded from CI by default\n\n@pytest.mark.slow\n@pytest.mark.ci\ndef test_slow_but_required_in_ci():\n    # Slow but must run in CI\n```\n\n### Step 5: Mock Distributed Environment\n\nFor unit tests that need distributed mocks:\n\n```python\nimport torch.distributed as dist\n\ndef test_distributed_function(monkeypatch):\n    monkeypatch.setattr(dist, \"get_rank\", lambda: 0)\n    monkeypatch.setattr(dist, \"get_world_size\", lambda: 2)\n    result = distributed_function()\n    assert result == expected\n```\n\n### Step 6: Handle GPU Dependencies\n\nAlways skip gracefully when GPU unavailable:\n\n```python\nCUDA_AVAILABLE = torch.cuda.is_available()\n\n@pytest.mark.skipif(not CUDA_AVAILABLE, reason=\"CUDA not available\")\ndef test_gpu_function():\n    tensor = torch.tensor([1, 2, 3], device=\"cuda\")\n    # ... assertions\n```\n\n## Key Requirements (Based on testing.md)\n\n### Mocking Distributed\n\n- Use `torch.distributed.fake_pg` for unit tests\n- Mock `dist.get_rank()` and `dist.get_world_size()` explicitly\n- Don't mock internals of FSDP/DTensor\n\n### GPU Test Constraints\n\n- **Always skip gracefully** when GPU unavailable\n- Clean up GPU memory: `torch.cuda.empty_cache()` in fixtures\n- Use smallest possible model/batch for unit tests\n\n### Assertions\n\n- Use `torch.testing.assert_close()` for tensor comparison\n- Specify `rtol`/`atol` explicitly for numerical tests\n- Avoid bare `assert tensor.equal()` - no useful error message\n\n## Reference Implementations\n\n| Test File                        | Description                            | Key Patterns                                      |\n| -------------------------------- | -------------------------------------- | ------------------------------------------------- |\n| `tests/test_utils.py`            | Utility function tests                 | Fixtures, parametrized tests                      |\n| `tests/test_examples.py`         | Integration tests with dataset loading | Dataset path resolution, success pattern matching |\n| `tests/test_fsdp_engine_nccl.py` | Distributed tests                      | Torchrun integration                              |\n\n## Common Mistakes\n\n- ❌ **Missing test file registration**: Ensure file follows `test_*.py` naming\n- ❌ **GPU dependency without skip**: Always use `@pytest.mark.skipif` for GPU tests\n- ❌ **Incorrect tensor comparisons**: Use `torch.testing.assert_close()` not\n  `assert tensor.equal()`\n- ❌ **Memory leaks in GPU tests**: Clean up with `torch.cuda.empty_cache()`\n- ❌ **Mocking too much**: Don't mock FSDP/DTensor internals\n- ❌ **Unclear test names**: Follow `test_<what>_<condition>_<expected>` pattern\n- ❌ **No docstrings**: Add descriptive docstrings to test functions\n\n## Integration with Other Skills\n\nThis skill complements other AReaL development skills:\n\n- **After `/add-dataset`**: Add tests for new dataset loaders\n- **After `/add-workflow`**: Add tests for new workflows\n- **After `/add-reward`**: Add tests for new reward functions\n- **With `planner` agent**: Reference this skill when planning test implementation\n\n## Running Tests\n\n```bash\n# First check GPU availability (many tests require GPU)\npython -c \"import torch; print('GPU available:', torch.cuda.is_available())\"\n\n# Run specific test file\nuv run pytest tests/test_<name>.py\n\n# Skip slow tests (CI default)\nuv run pytest -m \"not slow\"\n\n# Run with verbose output\nuv run pytest -v\n\n# Run distributed tests (requires torchrun and multi-GPU)\n# Note: Usually invoked via pytest test files\ntorchrun --nproc_per_node=2 tests/torchrun/run_<test>.py\n```\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/skills/add-unit-tests/SKILL.md\nInvocation: /add-unit-tests\n\n## Purpose\n\nStep-by-step guide for adding unit tests to AReaL.\n\n## How to Update\n\n### When Testing Conventions Change\n1. Update \"Key Requirements\" section based on `testing.md`\n2. Update test examples to match new patterns\n3. Update reference implementations\n\n### When Test Types Need Update\n1. Update \"Understand Test Types\" table (currently two main types)\n2. Add new examples if needed\n3. Update common mistakes\n\n### Integration with Other Skills\nEnsure references to other skills (`/add-dataset`, `/add-workflow`, `/add-reward`) remain accurate.\n\n================================================================================\n-->\n",".claude/skills/add-workflow/SKILL.md":"---\nname: add-workflow\ndescription: Guide for adding a new RolloutWorkflow to AReaL. Use when user wants to create a new workflow.\n---\n\n# Add Workflow\n\nAdd a new RolloutWorkflow implementation to AReaL.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a workflow?\"\n- User wants to create a new RolloutWorkflow\n- User mentions implementing a custom rollout\n\n## Prerequisites\n\nBefore starting, ensure you understand:\n\n- The workflow's purpose and requirements\n- Input/output data format\n- Reward function to use\n\n## Step-by-Step Guide\n\n### Step 1: Create Workflow File\n\nCreate `areal/workflow/<name>.py`:\n\n```python\nimport uuid\nfrom typing import Any, Callable\n\nimport torch\n\nfrom areal.api.cli_args import GenerationHyperparameters\nfrom areal.api.engine_api import InferenceEngine\nfrom areal.api.io_struct import ModelRequest, ModelResponse\nfrom areal.api.reward_api import AsyncRewardWrapper\nfrom areal.api.workflow_api import RolloutWorkflow\nfrom areal.utils import logging\n\nlogger = logging.getLogger(\"MyWorkflow\")\n\n\nclass MyWorkflow(RolloutWorkflow):\n    \"\"\"Description of your workflow.\"\"\"\n\n    def __init__(\n        self,\n        gconfig: GenerationHyperparameters,\n        tokenizer,\n        reward_fn: Callable,\n    ):\n        self.gconfig = gconfig.new_with_stop_and_pad_token_ids(tokenizer)\n        self.tokenizer = tokenizer\n        self.async_reward_fn = AsyncRewardWrapper(reward_fn)\n\n    async def arun_episode(\n        self,\n        engine: InferenceEngine,\n        data: dict[str, Any],\n    ) -> dict[str, torch.Tensor]:\n        \"\"\"Run a single episode. MUST be async and non-blocking.\"\"\"\n\n        # 1. Prepare input_ids from data\n        input_ids = self.tokenizer.apply_chat_template(\n            data[\"messages\"],\n            tokenize=True,\n            add_generation_prompt=True,\n        )\n\n        # 2. Build ModelRequest\n        req = ModelRequest(\n            rid=uuid.uuid4().hex,\n            input_ids=list(input_ids),\n            gconfig=self.gconfig.new(n_samples=1),\n            tokenizer=self.tokenizer,\n        )\n\n        # 3. Generate completion (async)\n        resp: ModelResponse = await engine.agenerate(req)\n\n        # 4. Compute reward (async)\n        prompt_str = self.tokenizer.decode(input_ids)\n        completion_str = self.tokenizer.decode(resp.output_tokens)\n        reward = await self.async_reward_fn(\n            prompt_str,\n            completion_str,\n            resp.input_tokens,\n            resp.output_tokens,\n            **data,\n        )\n\n        # 5. Return results in expected format\n        return {\n            \"input_ids\": torch.tensor(resp.input_tokens),\n            \"output_ids\": torch.tensor(resp.output_tokens),\n            \"reward\": torch.tensor(reward),\n        }\n```\n\n### Step 2: Register in __init__.py\n\nAdd to `areal/workflow/__init__.py`:\n\n```python\nfrom areal.workflow.<name> import MyWorkflow\n\n__all__ = [\n    # ... existing exports\n    \"MyWorkflow\",\n]\n```\n\n### Step 3: Update Entry Script\n\nUpdate your training script to use the new workflow:\n\n```python\ntrainer.train(\n    workflow=\"areal.workflow.<name>.MyWorkflow\",\n    # ... other args\n)\n```\n\n### Step 4: Add Tests\n\nCreate `tests/test_<name>_workflow.py`:\n\n```python\nimport pytest\nfrom areal.workflow.<name> import MyWorkflow\n\n@pytest.mark.asyncio\nasync def test_workflow_basic():\n    # Test basic functionality\n    pass\n```\n\n## Reference Implementations\n\n| Workflow           | File                            | Description                |\n| ------------------ | ------------------------------- | -------------------------- |\n| MultiTurnWorkflow  | `areal/workflow/multi_turn.py`  | Multi-turn conversation    |\n| RLVRWorkflow       | `areal/workflow/rlvr.py`        | RL with verifiable rewards |\n| VisionRLVRWorkflow | `areal/workflow/vision_rlvr.py` | Vision + RLVR              |\n\n## Key Requirements\n\n1. **Async**: `arun_episode` must be `async def` and non-blocking\n1. **No sync I/O**: Use `aiofiles` for file operations\n1. **Wrap rewards**: Use `AsyncRewardWrapper` for reward functions\n1. **Tensor format**: Output tensors should be `[batch, seq_len, ...]`\n1. **Use helpers**: `concat_padded_tensors` for combining outputs\n\n## Common Mistakes\n\n- ❌ Using `open()` instead of `aiofiles.open()`\n- ❌ Forgetting to `await` async calls\n- ❌ Not wrapping reward function with `AsyncRewardWrapper`\n- ❌ Wrong tensor shape conventions\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/skills/add-workflow/SKILL.md\nInvocation: /add-workflow <name>\n\n## Purpose\n\nStep-by-step guide for adding new RolloutWorkflow implementations.\n\n## How to Update\n\n### When Workflow API Changes\n1. Update the code template in Step 1\n2. Update the required imports\n3. Update the method signature if changed\n\n### When New Patterns Emerge\n1. Add to \"Reference Implementations\" table\n2. Update \"Key Requirements\" if new requirements added\n\n================================================================================\n-->\n",".claude/skills/commit-conventions/SKILL.md":"---\nname: commit-conventions\ndescription: AReaL commit message conventions. MUST load on every git commit -- provides Conventional Commits format with scope inference from file paths.\n---\n\n# Commit Conventions\n\nCommit message conventions and scope inference rules for the AReaL repository.\n\n## When to Use\n\n**ALWAYS load this skill when making any git commit in AReaL.** This includes:\n\n- Direct commits (`git commit`)\n- Commits during PR creation (`/create-pr`)\n- Commits delegated via Agent tool with `skills: [\"commit-conventions\"]`\n- Any agent workflow that produces a commit\n\n## Commit Message Format\n\n```\n<type>(<scope>): <subject>\n\n<body>\n\n[Optional sections:]\nKey changes:\n- change 1\n- change 2\n\nRefs: #123, #456\n```\n\n## Type Selection\n\n| Type       | When to Use                      |\n| ---------- | -------------------------------- |\n| `feat`     | New feature or capability        |\n| `fix`      | Bug fix                          |\n| `docs`     | Documentation only               |\n| `gov`      | Governance or maintainer changes |\n| `style`    | Formatting/style-only changes    |\n| `refactor` | Code change without feature/fix  |\n| `perf`     | Performance improvement          |\n| `test`     | Adding or fixing tests           |\n| `build`    | Build system or dependencies     |\n| `ci`       | CI pipeline or workflow changes  |\n| `chore`    | Build, deps, config changes      |\n| `revert`   | Revert a previous commit         |\n\n## Scope Inference\n\nInfer scope from the **primary** changed file paths:\n\n| File Path Pattern                                            | Scope                          |\n| ------------------------------------------------------------ | ------------------------------ |\n| `areal/workflow/`                                            | `workflow`                     |\n| `areal/engine/`                                              | `engine`                       |\n| `areal/reward/`                                              | `reward`                       |\n| `areal/dataset/`                                             | `dataset`                      |\n| `areal/api/`                                                 | `api`                          |\n| `areal/utils/`                                               | `utils`                        |\n| `areal/infra/`                                               | `infra`                        |\n| `areal/trainer/`                                             | `trainer`                      |\n| `areal/models/`                                              | `models`                       |\n| `areal/experimental/`                                        | `archon`                       |\n| `docs/`                                                      | `docs`                         |\n| `examples/`                                                  | `examples`                     |\n| `AGENTS.md`, `.agents/`, `.claude/`, `.codex/`, `.opencode/` | `agents`                       |\n| Multiple areas                                               | Omit scope or use broader term |\n\n## Rules\n\n- **Subject**: imperative mood, ~50-72 chars, no trailing period\n- **Body**: explain \"why\" not \"what\", wrap at 72 chars\n- **Key changes**: bullet list of main modifications (for complex commits with 3+ files)\n- **Refs**: reference issues/PRs if applicable\n\n## Examples\n\n**Single file fix:**\n\n```\nfix(reward): handle empty completion in gsm8k\n\nReturn 0 reward instead of raising exception when\ncompletion string is empty after extraction.\n```\n\n**Multi-file feature:**\n\n```\nfeat(engine): add CPU offload support to ArchonEngine\n\nEnable torch_memory_saver for model offloading during\nrollout phase to reduce GPU memory pressure.\n\nKey changes:\n- Add offload/onload methods to ArchonEngine\n- Integrate with weight update flow\n- Handle ROCm compatibility\n```\n\n**Docs only:**\n\n```\ndocs: update algorithm comparison table\n\nAdd SAPO and GSPO to the algorithm family documentation\nwith configuration examples.\n```\n\n**Agent/tooling changes:**\n\n```\nchore(agents): port review-pr command to OpenCode\n\nAdd OpenCode-native commands with task() category\ndelegation instead of hardcoded model names.\n\nKey changes:\n- Create .opencode/command/ with review-pr, create-pr\n- Replace hardcoded model routing with platform-native review routing\n- Add expert subagent consultation patterns\n```\n\n**Governance/maintainer changes:**\n\n```\ngov(agents): add maintainer ownership for service modules\n\nUpdate CODEOWNERS and maintainer references to reflect\ncurrent governance responsibilities.\n\nKey changes:\n- Add maintainers for agent_service and infra ownership\n- Align governance docs with updated reviewer responsibilities\n```\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/skills/commit-conventions/SKILL.md\nInvocation: Automatically loaded on every git commit via load_skills\n\n## Purpose\n\nProvides Conventional Commits format with AReaL-specific scope inference\nfrom file paths. Unlike other skills, this one is NOT user-triggered --\nit is loaded by the system/agent on every commit operation.\n\n## How to Update\n\n### When New Modules Are Added\n1. Add the file path pattern and scope to the \"Scope Inference\" table\n2. Keep table sorted by areal/ subpackages first, then top-level dirs\n\n### When Commit Types Change\n1. Update the \"Type Selection\" table\n2. Add/update examples to illustrate the new type\n\n### When Adding Examples\n1. Each example should demonstrate a distinct commit pattern\n2. Keep examples realistic -- use actual AReaL module names\n3. Show both subject-only and subject+body+key-changes variants\n\n### Important Design Decisions\n- This skill is ALWAYS loaded (not optional) -- keep it concise to\n  minimize token overhead on every commit\n- Scope inference is path-based, not content-based -- simpler and\n  more deterministic\n- \"Multiple areas\" -> omit scope rather than invent a new one\n\n================================================================================\n-->\n",".claude/skills/debug-distributed/SKILL.md":"---\nname: debug-distributed\ndescription: Guide for debugging distributed training issues in AReaL. Use when user encounters hangs, wrong results, OOM, or communication errors.\n---\n\n# Debug Distributed Training\n\nDebugging guide for distributed training issues in AReaL (FSDP2, TP, CP, EP).\n\n## When to Use\n\nThis skill is triggered when:\n\n- Training hangs or deadlocks\n- Results differ across ranks or are numerically wrong\n- OOM errors in distributed settings\n- NCCL/communication errors or device mesh issues\n\n## Debugging Principles\n\n### Minimal Reproduction\n\n**Always follow the minimal demo principle**: Reproduce with the least amount of code to\nnarrow down the issue faster.\n\n```python\n# Bad: Debug in full training loop\n# Good: Create minimal script\nimport torch\nimport torch.distributed as dist\n\ndist.init_process_group(\"nccl\")\nrank = dist.get_rank()\n\n# Reproduce the exact operation that fails\ntensor = torch.ones(10).cuda()\ndist.all_reduce(tensor)  # <-- Isolate the failing op\nprint(f\"Rank {rank}: {tensor}\")\n```\n\n**Reduction strategy:**\n\n1. Remove unrelated model components\n1. Use small tensor sizes\n1. Reduce world_size to minimum (e.g., 2 GPUs)\n1. Remove torch.compile if possible\n1. Disable activation checkpointing\n\n## Step-by-Step Debugging Guide\n\n### 1. Hang Debugging (Deadlocks, Synchronization)\n\n**Environment Variables for Debugging**:\n\n```bash\n# Full debug logging\nexport TORCH_DISTRIBUTED_DEBUG=DETAIL\nexport NCCL_DEBUG=INFO\nexport NCCL_DEBUG_SUBSYS=ALL\n\n# torch.compile debugging\nexport TORCH_LOGS=\"+dynamo,recompiles\"\nexport TORCHDYNAMO_VERBOSE=1\n```\n\n**Dump Call Stack with py-spy** (for hung processes):\n\n```bash\n# Find process IDs\nps aux | grep python\n\n# Dump call stack of specific rank\npy-spy dump --pid <PID>\n\n# Record flame graph for performance analysis\npy-spy record -o profile.svg --pid <PID> --duration 30\n```\n\n**Common Causes**:\n\n1. **Mismatched Collectives**: One rank calls `all_reduce`, another doesn't.\n1. **Wrong Process Group**: Using wrong group for collective.\n1. **Tensor Shape Mismatch**: Different shapes across ranks.\n\n**Debug Steps**:\n\n```python\n# Verify group membership\nmesh = parallel_dims.get_mesh(\"dp_shard_cp\")\ngroup = mesh.get_group()\nprint(f\"Rank {dist.get_rank()}: group size = {dist.get_world_size(group)}\")\n\n# Print shapes on all ranks\nprint(f\"Rank {dist.get_rank()}: tensor.shape = {tensor.shape}\")\ndist.barrier()\n```\n\n**Timeout Adjustment** (for debugging only):\n\n```python\nfrom areal.engine.core.distributed import patch_dist_group_timeout\nfrom datetime import timedelta\npatch_dist_group_timeout(timedelta(minutes=30))\n```\n\n### 2. Wrong Results (Gradient, Reduction Issues)\n\n**Check DTensor Placements**:\n\n```python\nfrom torch.distributed.tensor import DTensor\nif isinstance(param, DTensor):\n    print(f\"Param {name}: placements={param.placements}, mesh={param.device_mesh}\")\n```\n\n**Verify Gradient Reduction**:\n\n```python\nfor name, param in model.named_parameters():\n    if param.grad is not None:\n        print(f\"Rank {dist.get_rank()}: {name} grad_sum = {param.grad.sum().item()}\")\n```\n\n### 3. OOM Issues (Memory, Sharding)\n\n**Check Memory Usage**:\n\n```python\nprint(f\"Rank {dist.get_rank()}: \"\n      f\"allocated={torch.cuda.memory_allocated()/1e9:.2f}GB, \"\n      f\"reserved={torch.cuda.memory_reserved()/1e9:.2f}GB\")\n```\n\n**Check FSDP Coverage**:\n\n```python\nfor name, param in model.named_parameters():\n    is_dtensor = isinstance(param, DTensor)\n    print(f\"{name}: is_dtensor={is_dtensor}, shape={param.shape}\")\n```\n\n### 4. Communication Errors\n\n| Error                     | Cause                | Solution                           |\n| ------------------------- | -------------------- | ---------------------------------- |\n| `NCCL WARN Cuda failure`  | GPU communication    | Check NCCL version, GPU topology   |\n| `RuntimeError: Timed out` | Rank synchronization | Increase timeout, check code paths |\n| `Invalid device mesh`     | Mesh configuration   | Verify world_size = dp * tp * cp   |\n\n## Debugging Tools\n\n### Environment Variables Reference\n\n| Variable                          | Purpose                                |\n| --------------------------------- | -------------------------------------- |\n| `TORCH_DISTRIBUTED_DEBUG=DETAIL`  | Detailed distributed logging           |\n| `NCCL_DEBUG=INFO`                 | NCCL communication logging             |\n| `NCCL_DEBUG_SUBSYS=ALL`           | All NCCL subsystems                    |\n| `TORCH_LOGS=\"+dynamo,recompiles\"` | torch.compile logging                  |\n| `TORCHDYNAMO_VERBOSE=1`           | Dynamo verbose output                  |\n| `CUDA_LAUNCH_BLOCKING=1`          | Synchronous CUDA (slow, for debugging) |\n\n### py-spy for Call Stack Analysis\n\n```bash\n# Install\npip install py-spy\n\n# Dump call stack of hung process\npy-spy dump --pid <PID>\n\n# Dump all Python processes\npgrep -f python | xargs -I {} py-spy dump --pid {}\n\n# Record flame graph\npy-spy record -o profile.svg --pid <PID> --duration 30\n```\n\n### Rank-Conditional Printing\n\n```python\ndef print_all_ranks(msg):\n    for r in range(dist.get_world_size()):\n        if dist.get_rank() == r:\n            print(f\"[Rank {r}] {msg}\")\n        dist.barrier()\n```\n\n### Check Device Mesh\n\n```python\ndef debug_mesh(parallel_dims):\n    mesh = parallel_dims.world_mesh\n    for dim_name in mesh.mesh_dim_names:\n        submesh = parallel_dims.get_mesh(dim_name)\n        if submesh:\n            print(f\"Rank {dist.get_rank()}: {dim_name} size={submesh.size()}\")\n```\n\n### Validate Tensor Consistency\n\n```python\ndef check_tensor_consistency(tensor, name, group=None):\n    local_sum = tensor.sum().item()\n    tensor_sums = [None] * dist.get_world_size(group)\n    dist.all_gather_object(tensor_sums, local_sum, group=group)\n    if dist.get_rank() == 0 and len(set(tensor_sums)) > 1:\n        print(f\"WARNING: {name} inconsistent: {tensor_sums}\")\n```\n\n## Key Files Reference\n\n| Component       | File                                                          |\n| --------------- | ------------------------------------------------------------- |\n| Parallel Dims   | `areal/experimental/models/archon/parallel_dims.py`           |\n| Expert Parallel | `areal/experimental/models/archon/expert_parallel.py`         |\n| Ulysses (CP)    | `areal/experimental/models/archon/ulysses.py`                 |\n| FSDP/TP Apply   | `areal/experimental/models/archon/qwen2/infra/parallelize.py` |\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/skills/debug-distributed/SKILL.md\nInvocation: /debug-distributed\n\n## Purpose\n\nDebugging guide for distributed training issues.\nCovers FSDP2, Tensor Parallelism, Context Parallelism, and Expert Parallelism.\n\n## How to Update\n\n### When Adding New Parallelism Features\n1. Add section for the parallelism type\n2. Document common error patterns and debugging snippets\n\n### When PyTorch Distributed APIs Change\n1. Update DTensor/DeviceMesh examples\n2. Update environment variable references\n\n### When New Error Patterns Emerge\n1. Add to \"Common Errors and Solutions\" table\n2. Reference relevant source files\n\n================================================================================\n-->\n",".opencode/skills/add-archon-model/SKILL.md":"---\nname: add-archon-model\ndescription: Guide for adding a new model to the Archon engine. Use when user wants to add support for a new HuggingFace model architecture in ArchonEngine.\n---\n\n# Add Archon Model\n\nAdd support for a new HuggingFace model architecture in the Archon training engine.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a model to Archon?\"\n- User wants to support a new model family (e.g., Llama, Mistral, DeepSeek) in\n  ArchonEngine\n- User mentions adding a new `ModelSpec` or model type for Archon\n\n## Prerequisites\n\nBefore starting, ensure:\n\n- The target model is available on HuggingFace (has `config.json` with `model_type`)\n- You know the HuggingFace model ID (e.g., `meta-llama/Llama-3-8B`)\n- The model uses a standard transformer architecture (decoder-only)\n\n## Step-by-Step Guide\n\n### Step 1: Analyze the Target Model Architecture\n\nRead the HuggingFace model's source code to extract key architecture information.\n\n**Action**: Fetch and analyze the model's HuggingFace configuration and modeling files.\n\n1. Read the model's `config.json` (via `AutoConfig.from_pretrained`) to identify:\n\n   - `model_type` string (this is the key used for registry lookup)\n   - All architecture hyperparameters (hidden_size, num_layers, etc.)\n   - Any model-specific fields (e.g., `qk_norm`, `attention_bias`, MoE fields)\n\n1. Read the HuggingFace `modeling_*.py` source to identify:\n\n   - **Attention variant**: Does it have Q/K norm? Attention bias? Sliding window?\n     Multi-latent attention?\n   - **FFN variant**: SwiGLU (gate_proj + up_proj + down_proj)? GeGLU? Standard MLP?\n   - **MoE support**: Does it have MoE layers? What router type? Shared experts?\n   - **RoPE variant**: Standard RoPE? YaRN? NTK-aware scaling? What is the inv_freq\n     formula?\n   - **Normalization**: RMSNorm or LayerNorm? Pre-norm or post-norm? Elementwise affine?\n   - **Weight tying**: Does `tie_word_embeddings` appear in config?\n   - **State dict key names**: What are the HF weight key naming conventions?\n\n1. Summarize findings in a checklist like:\n\n```\nTarget model: <name>\nHF model_type: \"<model_type>\" (and variants like \"<model_type>_moe\" if applicable)\nAttention: [standard GQA / with QK norm / with bias / sliding window / ...]\nFFN: [SwiGLU / GeGLU / standard MLP / ...]\nMoE: [no / yes - num_experts, top_k, shared_experts]\nRoPE: [standard / YaRN / NTK-aware / ...]\nNorm: [RMSNorm / LayerNorm] with [pre-norm / post-norm]\nWeight tying: [yes / no]\n```\n\n### Step 2: Select the Reference Model\n\nChoose the closest existing implementation as a starting point:\n\n| Target characteristics               | Reference | Why                                     |\n| ------------------------------------ | --------- | --------------------------------------- |\n| Dense-only, standard GQA, no QK norm | `qwen2`   | Simplest baseline, pure dense           |\n| Has QK norm, or has MoE support      | `qwen3`   | Supports QK norm + MoE + shared experts |\n\n**Action**: Copy the reference model directory as the starting point:\n\n```\nareal/experimental/models/archon/<model>/\n  __init__.py\n  spec.py\n  model/\n    args.py\n    model.py\n    rope.py\n    state_dict_adapter.py\n  infra/\n    parallelize.py\n```\n\n### Step 3: Implement `args.py`\n\nAdapt `<Model>ModelArgs` to match the target model's HuggingFace config fields.\n\n**Key changes from reference**:\n\n1. Update the `@dataclass` fields to match the target model's hyperparameters:\n\n   - Field names should use Archon conventions (`dim`, `n_layers`, `n_heads`,\n     `n_kv_heads`, `vocab_size`, `head_dim`, `hidden_dim`, `norm_eps`, `rope_theta`,\n     etc.)\n   - Default values should match the smallest variant of the target model\n   - Add model-specific fields (e.g., `attention_bias`, `qk_norm`, `sliding_window`)\n\n1. Update `from_hf_config()` to correctly map HuggingFace config attributes:\n\n   - Use `getattr(hf_config, \"field_name\", default)` for optional fields\n   - Handle variant-specific fields (e.g., MoE fields only present in MoE variants)\n   - The method must return an instance of the model args class\n\n**Critical**: Verify every field mapping against the HF model's `config.json`. Incorrect\nmappings here cause silent errors downstream.\n\n**Base class contract** (`BaseModelArgs`):\n\n```python\n@dataclass\nclass <Model>ModelArgs(BaseModelArgs):\n    # ... model-specific fields ...\n\n    @classmethod\n    def from_hf_config(\n        cls,\n        hf_config: PretrainedConfig,\n        is_critic: bool = False,\n        **kwargs,\n    ) -> <Model>ModelArgs:\n        # Map HF config fields to Archon model args\n        ...\n```\n\n### Step 4: Implement `model.py`\n\nAdapt the model architecture to match the target model.\n\n**Key components to adapt**:\n\n1. **Normalization** (`RMSNorm` or similar):\n\n   - Check if `elementwise_affine` is configurable\n   - Check the epsilon default value\n   - If the model uses `LayerNorm`, implement accordingly\n\n1. **Attention** module:\n\n   - Q/K/V projection: Check bias presence (`nn.Linear(..., bias=True/False)`)\n   - QK norm: Add `q_norm`/`k_norm` if the model has them, remove if it doesn't\n   - GQA: `n_kv_heads` \\< `n_heads` for grouped-query attention\n   - Ulysses SP: Keep the `set_cp_group` / `_sp_enabled` pattern from the reference\n   - Output projection: Check bias presence\n\n1. **FeedForward** module:\n\n   - SwiGLU: `w2(silu(w1(x)) * w3(x))` -- most common for modern LLMs\n   - Check bias in linear layers\n   - For MoE models: `MoE` module replaces `FeedForward` on designated layers\n\n1. **TransformerBlock**: Pre-norm (most modern LLMs) vs post-norm\n\n   - MoE layer detection via `_is_moe_layer()` if applicable\n\n1. **Top-level Model** (`<Model>Model(BaseArchonModel)`):\n\n   - `tok_embeddings`, `layers` (as `ModuleDict`), `norm`, `output`/`score`\n   - `init_weights()`: Match initialization scheme from HF\n   - `init_buffers()`: RoPE cache + MoE buffers\n   - `forward()`: Must follow `BaseArchonModel` signature:\n     `(tokens, positions, cu_seqlens, max_seqlen, tree_attn_meta=None) -> Tensor`\n\n**Base class contract** (`BaseArchonModel`):\n\n```python\nclass <Model>Model(BaseArchonModel):\n    def forward(self, tokens, positions, cu_seqlens, max_seqlen, tree_attn_meta=None) -> torch.Tensor: ...\n    def init_weights(self) -> None: ...\n    def init_buffers(self, buffer_device) -> None: ...\n```\n\n### Step 5: Implement `rope.py`\n\nHandle the rotary position embedding variant.\n\n**Options**:\n\n1. **Standard RoPE** (same as qwen2/qwen3): Re-export from qwen2:\n\n   ```python\n   from areal.experimental.models.archon.qwen2.model.rope import (\n       apply_rotary_emb,\n       precompute_rope_cache,\n       repeat_kv,\n       reshape_for_broadcast,\n       rotate_half,\n   )\n   ```\n\n1. **Custom RoPE** (YaRN, NTK-aware, etc.): Implement custom `precompute_rope_cache()`\n   and `apply_rotary_emb()` functions. The key difference is usually in how `inv_freq`\n   is computed (scaling factors, interpolation, etc.).\n\n### Step 6: Implement `state_dict_adapter.py`\n\nMap between HuggingFace and Archon weight key names.\n\n**This is the most error-prone step.** The adapter must correctly handle:\n\n1. **Key name mapping** (`from_hf_map` dict):\n\n   - Embedding: `model.embed_tokens.weight` -> `tok_embeddings.weight`\n   - Attention: `model.layers.{}.self_attn.q_proj.weight` ->\n     `layers.{}.attention.wq.weight`\n   - FFN: `model.layers.{}.mlp.gate_proj.weight` -> `layers.{}.feed_forward.w1.weight`\n   - Norms: `model.layers.{}.input_layernorm.weight` ->\n     `layers.{}.attention_norm.weight`\n   - Output: `lm_head.weight` -> `output.weight`\n   - Skip keys (set to `None`): `rotary_emb.inv_freq` (computed at runtime)\n   - Model-specific keys: bias terms, QK norm weights, etc.\n\n1. **Reverse mapping** (`to_hf_map`): Auto-generated from `from_hf_map`\n\n1. **MoE expert weights** (if applicable): 3D\\<->2D conversion for expert weights. Copy\n   the MoE handling from qwen3 if the model has MoE.\n\n1. **Weight tying**: Skip `output.weight` during `to_hf()` if `tie_word_embeddings=True`\n\n**Verification approach**: After implementation, the adapter should satisfy:\n\n```python\n# Roundtrip: archon -> hf -> archon preserves all keys\nhf_sd = adapter.to_hf(archon_sd)\nroundtrip_sd = adapter.from_hf(hf_sd)\nassert set(roundtrip_sd.keys()) == set(archon_sd.keys())\n```\n\n**Base class contract** (`BaseStateDictAdapter`):\n\n```python\nclass <Model>StateDictAdapter(BaseStateDictAdapter):\n    def from_hf(self, hf_state_dict) -> dict[str, Any]: ...\n    def to_hf(self, archon_state_dict) -> dict[str, Any]: ...\n    def convert_single_to_hf(self, name, tensor) -> list[tuple[str, torch.Tensor]]: ...\n```\n\n### Step 7: Implement `parallelize.py`\n\nDefine the parallelization strategy for the model.\n\n**The parallelize function** applies parallelism in this order:\n\n1. TP (Tensor Parallelism) -- shard attention/FFN across devices\n1. EP (Expert Parallelism) -- for MoE models only\n1. CP (Context Parallelism / Ulysses SP) -- sequence parallelism\n1. AC (Activation Checkpointing) -- memory optimization\n1. torch.compile -- compilation optimization\n1. FSDP (Fully Sharded Data Parallelism) -- data parallelism\n\n**Key adaptations by model architecture**:\n\n- **Attention with QK norm**: wq/wk use `use_local_output=False` (DTensor output for\n  norm), add `SequenceParallel(sequence_dim=2)` for q_norm/k_norm\n- **Attention without QK norm**: wq/wk/wv all use `use_local_output=True`\n- **Attention with bias**: Bias terms follow the same parallel plan as their weights\n- **MoE layers**: Separate TP plan for MoE input/output, router gate, and expert\n  weights. Copy from qwen3's `apply_moe_ep_tp()` and `apply_non_moe_tp()`\n- **Dense-only models**: Simpler plan without MoE handling. Copy from qwen2\n\n**Function signature** (must match `ParallelizeFn` protocol):\n\n```python\ndef parallelize_<model>(\n    model: nn.Module,\n    parallel_dims: ArchonParallelDims,\n    param_dtype: torch.dtype = torch.bfloat16,\n    reduce_dtype: torch.dtype = torch.float32,\n    loss_parallel: bool = True,\n    cpu_offload: bool = False,\n    reshard_after_forward_policy: str = \"default\",\n    ac_config: ActivationCheckpointConfig | None = None,\n    enable_compile: bool = True,\n) -> nn.Module:\n```\n\n### Step 8: Create `spec.py` and Register\n\nAssemble the `ModelSpec` and register it.\n\n```python\nfrom areal.experimental.models.archon.model_spec import ModelSpec, register_model_spec\nfrom areal.experimental.models.archon.pipeline_parallel import pipeline_llm\nfrom areal.experimental.models.archon.<model>.infra.parallelize import parallelize_<model>\nfrom areal.experimental.models.archon.<model>.model.args import <Model>ModelArgs\nfrom areal.experimental.models.archon.<model>.model.model import <Model>Model\nfrom areal.experimental.models.archon.<model>.model.state_dict_adapter import (\n    <Model>StateDictAdapter,\n)\n\n<MODEL>_SPEC = ModelSpec(\n    name=\"<Model>\",\n    model_class=<Model>Model,\n    model_args_class=<Model>ModelArgs,\n    state_dict_adapter_class=<Model>StateDictAdapter,\n    parallelize_fn=parallelize_<model>,\n    supported_model_types=frozenset({\"<model_type>\"}),  # From HF config.json\n    pipelining_fn=pipeline_llm,\n)\n\n# Auto-register when module is imported\nregister_model_spec(<MODEL>_SPEC)\n\n__all__ = [\"<MODEL>_SPEC\"]\n```\n\n**Note**: `supported_model_types` should include all HF `model_type` strings that this\nimplementation handles (e.g., `{\"qwen3\", \"qwen3_moe\"}` for Qwen3).\n\n### Step 9: Register in `__init__.py`\n\nAdd the import to `areal/experimental/models/archon/__init__.py`:\n\n```python\nfrom areal.experimental.models.archon.<model> import spec as <model>_spec  # noqa: F401\n```\n\nThis triggers auto-registration when the module is imported.\n\n### Step 10: Verify and Test\n\nVerification should be done in stages, adapting based on available hardware and the test\npatterns in `tests/experimental/archon/`.\n\n**Before writing tests**, examine the existing test files to understand current\npatterns:\n\n```\ntests/experimental/archon/\n  conftest.py             -- Pytest configuration (version checks)\n  utils.py                -- Shared utilities (model loading, comparison)\n  test_qwen3_args.py      -- Args unit tests (CPU-only)\n  test_state_dict_adapter.py  -- State dict roundtrip tests\n  test_weight_sync.py     -- Weight completeness tests (meta device)\n  test_forward.py         -- Forward precision comparison (single GPU)\n  ...\n```\n\n**Test stages** (write tests appropriate for the model's complexity):\n\n#### Stage 1: Args Tests (CPU-only, always write these)\n\nTest `from_hf_config()` with mock HuggingFace configs:\n\n```python\n# Pattern: Create mock PretrainedConfig, verify args mapping\nfrom unittest.mock import MagicMock\n\ndef test_args_from_hf_config():\n    hf_config = MagicMock()\n    hf_config.hidden_size = 4096\n    hf_config.num_hidden_layers = 32\n    # ... set all required fields\n    args = <Model>ModelArgs.from_hf_config(hf_config)\n    assert args.dim == 4096\n    assert args.n_layers == 32\n```\n\n#### Stage 2: State Dict Adapter Tests (CPU-only)\n\nTest key mapping roundtrip:\n\n```python\ndef test_state_dict_roundtrip():\n    # Create adapter with mock config\n    adapter = <Model>StateDictAdapter(mock_config)\n    # Create fake archon state dict with expected keys\n    archon_sd = {\"tok_embeddings.weight\": torch.randn(vocab, dim), ...}\n    # Roundtrip\n    hf_sd = adapter.to_hf(archon_sd)\n    roundtrip = adapter.from_hf(hf_sd)\n    assert set(roundtrip.keys()) == set(archon_sd.keys())\n```\n\n#### Stage 3: Weight Completeness (meta device, CPU-only)\n\nVerify all model parameters have HF mappings:\n\n```python\ndef test_weight_completeness():\n    # Create model on meta device\n    with torch.device(\"meta\"):\n        model = <Model>Model(args)\n    adapter = <Model>StateDictAdapter(hf_config)\n    # Check every archon param has a HF mapping\n    for name, _ in model.named_parameters():\n        hf_pairs = adapter.convert_single_to_hf(name, torch.empty(0))\n        assert len(hf_pairs) > 0, f\"No HF mapping for {name}\"\n```\n\n#### Stage 4: Forward Precision (single GPU, if available)\n\nCompare Archon model output against HuggingFace reference:\n\n```python\n@pytest.mark.skipif(not torch.cuda.is_available(), reason=\"Requires CUDA\")\ndef test_forward_matches_hf():\n    # Load both HF and Archon models\n    # Run forward on same input\n    # Compare logits within tolerance\n```\n\n**Important**: Do NOT hardcode the test categories. Inspect the existing test files in\n`tests/experimental/archon/` and follow the same patterns, fixtures, and markers. Adapt\ntest scope to the model's specific features (e.g., add MoE-specific tests only if the\nmodel has MoE).\n\n## Reference Implementations\n\n| Model | Directory                                 | Features                                                |\n| ----- | ----------------------------------------- | ------------------------------------------------------- |\n| Qwen2 | `areal/experimental/models/archon/qwen2/` | Dense, attention bias, no QK norm                       |\n| Qwen3 | `areal/experimental/models/archon/qwen3/` | Dense + MoE, QK norm, no attention bias, shared experts |\n\n## Architecture Decision Map\n\n| Feature             | qwen2    | qwen3                      | What to check in target model                            |\n| ------------------- | -------- | -------------------------- | -------------------------------------------------------- |\n| Attention bias      | Yes      | No                         | `attention_bias` in HF config                            |\n| QK norm             | No       | Yes                        | `qk_norm` in HF config or QKNorm module in modeling file |\n| MoE                 | No       | Yes                        | `num_experts`/`num_local_experts` in HF config           |\n| Shared experts      | No       | Yes                        | `num_shared_experts` in HF config                        |\n| Decoder sparse step | No       | Yes                        | `decoder_sparse_step` in HF config                       |\n| Weight tying        | Both     | Both                       | `tie_word_embeddings` in HF config                       |\n| RoPE                | Standard | Standard (re-export qwen2) | Check inv_freq formula in HF modeling code               |\n\n## Common Mistakes\n\n- Not mapping all HF keys in `state_dict_adapter.py` (causes silent weight drops)\n- Wrong `from_hf_config()` field mapping (uses wrong HF config attribute name)\n- Forgetting to handle `None` keys in `from_hf_map` (keys to skip like\n  `rotary_emb.inv_freq`)\n- Missing MoE expert weight 3D\\<->2D conversion when model has MoE\n- Wrong TP plan for attention with/without QK norm (`use_local_output` must match)\n- Forgetting to add import line in `areal/experimental/models/archon/__init__.py`\n- Not including all `model_type` variants in `supported_model_types` frozenset\n- Using `print` instead of `areal.utils.logging.getLogger()`\n\n## File Checklist\n\nAfter completion, verify all files exist and are consistent:\n\n- [ ] `areal/experimental/models/archon/<model>/__init__.py`\n- [ ] `areal/experimental/models/archon/<model>/spec.py` -- ModelSpec + register\n- [ ] `areal/experimental/models/archon/<model>/model/args.py` -- ModelArgs +\n  from_hf_config\n- [ ] `areal/experimental/models/archon/<model>/model/model.py` -- Model + Attention +\n  FFN\n- [ ] `areal/experimental/models/archon/<model>/model/rope.py` -- RoPE (or re-export)\n- [ ] `areal/experimental/models/archon/<model>/model/state_dict_adapter.py` -- Key\n  mapping\n- [ ] `areal/experimental/models/archon/<model>/infra/parallelize.py` -- Parallel\n  strategy\n- [ ] `areal/experimental/models/archon/__init__.py` -- Import line added\n- [ ] `tests/experimental/archon/test_<model>_*.py` -- Tests\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .opencode/skills/add-archon-model/SKILL.md\nInvocation: /add-archon-model <model_name>\n\n## Purpose\n\nSemi-automated guide for adding new model architectures to the Archon training engine.\nUnlike simpler skills (add-reward, add-dataset), this skill actively guides Claude to:\n1. Analyze HuggingFace source code to extract architecture details\n2. Select the closest reference implementation (qwen2 or qwen3)\n3. Generate code skeletons adapted to the target architecture\n4. Create appropriate tests based on existing test patterns\n\n## How to Update\n\n### When New Reference Models Are Added\n1. Add to \"Reference Implementations\" table\n2. Update \"Architecture Decision Map\" with new feature columns\n3. Update Step 2 (reference selection) with new options\n\n### When Base Classes Change\n1. Update contract signatures in Steps 3, 4, 6, 7\n2. Update file checklist if new files are required\n\n### When ModelSpec Changes\n1. Update Step 8 with new ModelSpec fields\n2. Update spec.py template\n\n### When Test Patterns Change\n1. Update Step 10 with new test patterns\n2. Do NOT hardcode test categories -- keep it flexible\n\n### Important Design Decisions\n- This skill is SEMI-AUTOMATED: Claude should read HF source and generate code,\n  not just provide templates for the user to fill in manually\n- The skill references existing test files rather than hardcoding test categories,\n  ensuring it stays current as the test suite evolves\n- Reference model selection (qwen2 vs qwen3) is based on MoE and QK norm presence\n\n================================================================================\n-->\n",".opencode/skills/add-dataset/SKILL.md":"---\nname: add-dataset\ndescription: Guide for adding a new dataset loader to AReaL. Use when user wants to add a new dataset.\n---\n\n# Add Dataset\n\nAdd a new dataset loader to AReaL.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a dataset?\"\n- User wants to integrate a new dataset\n- User mentions creating a dataset loader\n\n## Step-by-Step Guide\n\n### Step 1: Create Dataset File\n\nCreate `areal/dataset/<name>.py`:\n\n```python\nfrom datasets import Dataset, load_dataset\n\n\ndef get_<name>_sft_dataset(\n    path: str,\n    split: str,\n    tokenizer,\n    max_length: int | None = None,\n) -> Dataset:\n    \"\"\"Load dataset for SFT training.\n\n    Args:\n        path: Path to dataset (HuggingFace hub or local path)\n        split: Dataset split (train/validation/test)\n        tokenizer: Tokenizer for processing\n        max_length: Maximum sequence length (optional)\n\n    Returns:\n        HuggingFace Dataset with processed samples\n    \"\"\"\n    dataset = load_dataset(path=path, split=split)\n\n    def process(sample):\n        # Tokenize the full sequence (prompt + response)\n        seq_token = tokenizer.encode(\n            sample[\"question\"] + sample[\"answer\"] + tokenizer.eos_token\n        )\n        prompt_token = tokenizer.encode(sample[\"question\"])\n        # Loss mask: 0 for prompt, 1 for response\n        loss_mask = [0] * len(prompt_token) + [1] * (len(seq_token) - len(prompt_token))\n        return {\"input_ids\": seq_token, \"loss_mask\": loss_mask}\n\n    dataset = dataset.map(process).remove_columns([\"question\", \"answer\"])\n\n    if max_length is not None:\n        dataset = dataset.filter(lambda x: len(x[\"input_ids\"]) <= max_length)\n\n    return dataset\n\n\ndef get_<name>_rl_dataset(\n    path: str,\n    split: str,\n    tokenizer,\n    max_length: int | None = None,\n) -> Dataset:\n    \"\"\"Load dataset for RL training.\n\n    Args:\n        path: Path to dataset\n        split: Dataset split\n        tokenizer: Tokenizer for length filtering\n        max_length: Maximum sequence length\n\n    Returns:\n        HuggingFace Dataset with prompts and answers for reward computation\n    \"\"\"\n    dataset = load_dataset(path=path, split=split)\n\n    def process(sample):\n        messages = [\n            {\n                \"role\": \"user\",\n                \"content\": sample[\"question\"],\n            }\n        ]\n        return {\"messages\": messages, \"answer\": sample[\"answer\"]}\n\n    dataset = dataset.map(process).remove_columns([\"question\"])\n\n    if max_length is not None:\n\n        def filter_length(sample):\n            content = sample[\"messages\"][0][\"content\"]\n            tokens = tokenizer.encode(content)\n            return len(tokens) <= max_length\n\n        dataset = dataset.filter(filter_length)\n\n    return dataset\n```\n\n### Step 2: Register in __init__.py\n\nUpdate `areal/dataset/__init__.py`:\n\n```python\n# Add to VALID_DATASETS\nVALID_DATASETS = [\n    # ... existing datasets\n    \"<name>\",\n]\n\n# Add to _get_custom_dataset function\ndef _get_custom_dataset(name: str, ...):\n    # ... existing code\n    elif name == \"<name>\":\n        from areal.dataset.<name> import get_<name>_sft_dataset, get_<name>_rl_dataset\n        if dataset_type == \"sft\":\n            return get_<name>_sft_dataset(path, split, max_length, tokenizer)\n        else:\n            return get_<name>_rl_dataset(path, split, max_length, tokenizer)\n```\n\n### Step 3: Add Config (Optional)\n\nIf the dataset needs special configuration, add to `areal/api/cli_args.py`:\n\n```python\n@dataclass\nclass TrainDatasetConfig:\n    # ... existing fields\n    <name>_specific_field: Optional[str] = None\n```\n\n### Step 4: Add Tests\n\nCreate `tests/test_<name>_dataset.py`:\n\n```python\nimport pytest\nfrom areal.dataset.<name> import get_<name>_sft_dataset, get_<name>_rl_dataset\n\ndef test_sft_dataset_loads(tokenizer):\n    dataset = get_<name>_sft_dataset(\"path/to/data\", split=\"train\", tokenizer=tokenizer)\n    assert len(dataset) > 0\n    assert \"input_ids\" in dataset.column_names\n    assert \"loss_mask\" in dataset.column_names\n\ndef test_rl_dataset_loads(tokenizer):\n    dataset = get_<name>_rl_dataset(\"path/to/data\", split=\"train\", tokenizer=tokenizer)\n    assert len(dataset) > 0\n    assert \"messages\" in dataset.column_names\n    assert \"answer\" in dataset.column_names\n```\n\n## Reference Implementations\n\n| Dataset    | File                               | Description              |\n| ---------- | ---------------------------------- | ------------------------ |\n| GSM8K      | `areal/dataset/gsm8k.py`           | Math word problems       |\n| Geometry3K | `areal/dataset/geometry3k.py`      | Geometry problems        |\n| CLEVR      | `areal/dataset/clevr_count_70k.py` | Visual counting          |\n| HH-RLHF    | `areal/dataset/hhrlhf.py`          | Helpfulness/Harmlessness |\n| TORL       | `areal/dataset/torl_data.py`       | Tool-use RL              |\n\n## Required Fields\n\n### SFT Dataset\n\n```python\n{\n    \"messages\": [\n        {\"role\": \"user\", \"content\": \"...\"},\n        {\"role\": \"assistant\", \"content\": \"...\"},\n    ]\n}\n```\n\n### RL Dataset\n\n```python\n{\n    \"messages\": [\n        {\"role\": \"user\", \"content\": \"...\"},\n    ],\n    \"answer\": \"ground_truth_for_reward\",\n    # Optional metadata for reward function\n}\n```\n\n## Common Mistakes\n\n- Returning `List[Dict]` instead of HuggingFace `Dataset`\n- Using Python loops instead of `dataset.map()`/`filter()`\n- Missing `\"messages\"` field for RL datasets\n- Wrong message format (should be list of dicts with `role` and `content`)\n- Not registering in `__init__.py`\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .opencode/skills/add-dataset/SKILL.md\nInvocation: /add-dataset <name>\n\n## Purpose\n\nStep-by-step guide for adding new dataset loaders.\n\n## How to Update\n\n### When Dataset API Changes\n1. Update the code templates\n2. Update required fields section\n3. Update registration example\n\n### When New Dataset Types Added\n1. Add to \"Reference Implementations\" table\n2. Add any new required fields\n\n================================================================================\n-->\n",".opencode/skills/add-reward/SKILL.md":"---\nname: add-reward\ndescription: Guide for adding a new reward function to AReaL. Use when user wants to create a reward function.\n---\n\n# Add Reward\n\nAdd a new reward function to AReaL.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a reward function?\"\n- User wants to implement custom rewards\n- User mentions reward computation\n\n## Step-by-Step Guide\n\n### Step 1: Create Reward File\n\nCreate `areal/reward/<name>.py`:\n\n```python\nfrom typing import Any\n\nfrom areal.utils import logging\n\nlogger = logging.getLogger(\"MyReward\")\n\n\ndef <name>_reward_fn(\n    prompt: str,\n    completions: str,\n    prompt_ids,\n    completion_ids,\n    answer: str | None = None,\n    **kwargs: Any,\n) -> float:\n    \"\"\"Compute reward for a single completion.\n\n    Args:\n        prompt: Prompt string\n        completions: Completion string (model output)\n        prompt_ids: Tokenized prompt IDs\n        completion_ids: Tokenized completion IDs\n        answer: Ground truth answer from dataset (optional)\n        **kwargs: Additional data from dataset\n\n    Returns:\n        Reward value (float), typically 0.0 or 1.0\n    \"\"\"\n    try:\n        # Extract answer from completion\n        extracted = _extract_answer(completions)\n\n        # Compare with ground truth\n        if answer is not None and extracted == str(answer):\n            return 1.0\n        return 0.0\n    except Exception:\n        logger.warning(\"Exception in reward computation\", exc_info=True)\n        return 0.0\n\n\ndef _extract_answer(completion: str) -> str:\n    \"\"\"Extract the answer from a completion string.\n\n    Implement your extraction logic here.\n    \"\"\"\n    # Example: Extract content from \\boxed{}\n    import re\n\n    match = re.search(r\"\\\\boxed\\{([^}]+)\\}\", completion)\n    if match:\n        return match.group(1).strip()\n    return completion.strip()\n```\n\n### Step 2: Register in __init__.py\n\nUpdate `areal/reward/__init__.py`:\n\n```python\n# Add to VALID_REWARD_FN\nVALID_REWARD_FN = [\n    # ... existing reward functions\n    \"<name>\",\n]\n\n# Add to get_reward_fn function\ndef get_reward_fn(name: str, **kwargs):\n    # ... existing code\n    elif name == \"<name>\":\n        from areal.reward.<name> import <name>_reward_fn\n        return <name>_reward_fn\n```\n\n### Step 3: Handle Blocking Operations\n\nIf your reward function uses blocking operations (e.g., API calls, model inference), the\nworkflow will wrap it with `AsyncRewardWrapper`:\n\n```python\n# In your workflow\nfrom areal.reward import AsyncRewardWrapper\n\nself.reward_fn = AsyncRewardWrapper(reward_fn)\n\n# Then call it asynchronously\nrewards = await self.reward_fn(prompt, completions, **data)\n```\n\n### Step 4: Add Tests\n\nCreate `tests/test_<name>_reward.py`:\n\n```python\nimport pytest\nfrom areal.reward.<name> import <name>_reward_fn\n\ndef test_reward_correct_answer():\n    reward = <name>_reward_fn(\n        prompt=\"What is 2+2?\",\n        completions=\"The answer is \\\\boxed{4}\",\n        prompt_ids=None,\n        completion_ids=None,\n        answer=\"4\",\n    )\n    assert reward == 1.0\n\ndef test_reward_wrong_answer():\n    reward = <name>_reward_fn(\n        prompt=\"What is 2+2?\",\n        completions=\"The answer is \\\\boxed{5}\",\n        prompt_ids=None,\n        completion_ids=None,\n        answer=\"4\",\n    )\n    assert reward == 0.0\n```\n\n## Reference Implementations\n\n| Reward     | File                              | Description                  |\n| ---------- | --------------------------------- | ---------------------------- |\n| GSM8K      | `areal/reward/gsm8k.py`           | Math answer verification     |\n| Geometry3K | `areal/reward/geometry3k.py`      | Geometry answer verification |\n| CLEVR      | `areal/reward/clevr_count_70k.py` | Counting verification        |\n| MathVerify | `areal/reward/math_verify.py`     | General math verification    |\n\n## Function Signature\n\nAll reward functions must follow this signature:\n\n```python\ndef reward_fn(\n    prompt: str,               # Input prompt string\n    completions: str,          # Model completion string\n    prompt_ids,                # Tokenized prompt\n    completion_ids,            # Tokenized completion\n    **kwargs: Any,             # Additional data from dataset (e.g., answer)\n) -> float:                    # Reward value (typically 0.0 or 1.0)\n```\n\n**Note**: The reward function is called once per sample. Batching is handled by\n`AsyncRewardWrapper` in the workflow.\n\n## Key Requirements\n\n1. **Deterministic**: Same inputs should produce same outputs\n1. **Return float**: Output is a single float value per sample\n1. **No blocking in async context**: Use `AsyncRewardWrapper` if needed\n1. **Logging**: Use `areal.utils.logging`, not `print`\n1. **Handle exceptions**: Return 0.0 on error, don't raise\n\n## Common Mistakes\n\n- Returning a tensor instead of a float\n- Expecting batched inputs (reward is called per sample)\n- Non-deterministic behavior\n- Blocking operations without `AsyncRewardWrapper`\n- Raising exceptions instead of returning 0.0\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .opencode/skills/add-reward/SKILL.md\nInvocation: /add-reward <name>\n\n## Purpose\n\nStep-by-step guide for adding new reward functions.\n\n## How to Update\n\n### When Reward API Changes\n1. Update the function signature section\n2. Update the code template\n3. Update key requirements\n\n### When New Reward Patterns Emerge\n1. Add to \"Reference Implementations\" table\n2. Add examples for new patterns\n\n================================================================================\n-->\n",".opencode/skills/add-unit-tests/SKILL.md":"---\nname: add-unit-tests\ndescription: Guide for adding unit tests to AReaL. Use when user wants to add tests for new functionality or increase test coverage.\n---\n\n# Add Unit Tests\n\nAdd unit tests to AReaL following the project's testing conventions.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add tests?\"\n- User wants to increase test coverage\n- User needs to write tests for new functionality\n- User wants to understand AReaL testing patterns\n\n## Step-by-Step Guide\n\n### Step 1: Understand Test Types\n\nAReaL has two main test categories:\n\n| Test Type             | Purpose                            | Location Pattern                   | How It Runs                                |\n| --------------------- | ---------------------------------- | ---------------------------------- | ------------------------------------------ |\n| **Unit Tests**        | Test individual functions/modules  | `tests/test_<module>_<feature>.py` | Directly via pytest                        |\n| **Distributed Tests** | Test distributed/parallel behavior | `tests/torchrun/run_*.py`          | Via torchrun (called by pytest subprocess) |\n\n**Note**: All tests are invoked via pytest. Distributed tests use `torchrun` but are\nstill called from pytest test files.\n\n### Step 2: Create Test File Structure\n\nCreate test file with naming convention: `test_<module>_<feature>.py`\n\n```python\nimport pytest\nimport torch\n\n# Import the module to test\nfrom areal.dataset.gsm8k import get_gsm8k_sft_dataset\nfrom tests.utils import get_dataset_path  # Optional test utilities\n# For mocking tokenizer: from unittest.mock import MagicMock\n```\n\n### Step 3: Write Test Functions\n\nFollow Arrange-Act-Assert pattern:\n\n```python\ndef test_function_under_condition_returns_expected():\n    \"\"\"Test that function returns expected value under condition.\"\"\"\n    # Arrange\n    input_data = 5\n    expected_output = 10\n\n    # Act\n    result = function_under_test(input_data)\n\n    # Assert\n    assert result == expected_output\n```\n\n### Step 4: Add Pytest Markers and CI Strategy\n\nUse appropriate pytest markers:\n\n| Marker                                  | When to Use                                                  |\n| --------------------------------------- | ------------------------------------------------------------ |\n| `@pytest.mark.slow`                     | Test takes > 10 seconds (excluded from CI by default)        |\n| `@pytest.mark.ci`                       | Slow test that must run in CI (use with `@pytest.mark.slow`) |\n| `@pytest.mark.asyncio`                  | Async test functions                                         |\n| `@pytest.mark.skipif(cond, reason=...)` | Conditional skip                                             |\n| `@pytest.mark.parametrize(...)`         | Parameterized tests                                          |\n\n**CI Test Strategy**:\n\n- `@pytest.mark.slow`: Excluded from CI by default (CI runs `pytest -m \"not slow\"`)\n- `@pytest.mark.slow` + `@pytest.mark.ci`: Slow but must run in CI\n- No marker: Runs in CI (fast unit tests)\n\n```python\n@pytest.mark.asyncio\nasync def test_async_function():\n    result = await async_function()\n    assert result == expected\n\n@pytest.mark.skipif(not torch.cuda.is_available(), reason=\"CUDA not available\")\ndef test_gpu_feature():\n    tensor = torch.tensor([1, 2, 3], device=\"cuda\")\n    # ... assertions\n\n@pytest.mark.parametrize(\"batch_size\", [1, 4, 16])\ndef test_with_parameters(batch_size):\n    # Parameterized test\n\n@pytest.mark.slow\ndef test_slow_function():\n    # Excluded from CI by default\n\n@pytest.mark.slow\n@pytest.mark.ci\ndef test_slow_but_required_in_ci():\n    # Slow but must run in CI\n```\n\n### Step 5: Mock Distributed Environment\n\nFor unit tests that need distributed mocks:\n\n```python\nimport torch.distributed as dist\n\ndef test_distributed_function(monkeypatch):\n    monkeypatch.setattr(dist, \"get_rank\", lambda: 0)\n    monkeypatch.setattr(dist, \"get_world_size\", lambda: 2)\n    result = distributed_function()\n    assert result == expected\n```\n\n### Step 6: Handle GPU Dependencies\n\nAlways skip gracefully when GPU unavailable:\n\n```python\nCUDA_AVAILABLE = torch.cuda.is_available()\n\n@pytest.mark.skipif(not CUDA_AVAILABLE, reason=\"CUDA not available\")\ndef test_gpu_function():\n    tensor = torch.tensor([1, 2, 3], device=\"cuda\")\n    # ... assertions\n```\n\n## Key Requirements (Based on testing.md)\n\n### Mocking Distributed\n\n- Use `torch.distributed.fake_pg` for unit tests\n- Mock `dist.get_rank()` and `dist.get_world_size()` explicitly\n- Don't mock internals of FSDP/DTensor\n\n### GPU Test Constraints\n\n- **Always skip gracefully** when GPU unavailable\n- Clean up GPU memory: `torch.cuda.empty_cache()` in fixtures\n- Use smallest possible model/batch for unit tests\n\n### Assertions\n\n- Use `torch.testing.assert_close()` for tensor comparison\n- Specify `rtol`/`atol` explicitly for numerical tests\n- Avoid bare `assert tensor.equal()` - no useful error message\n\n## Reference Implementations\n\n| Test File                        | Description                            | Key Patterns                                      |\n| -------------------------------- | -------------------------------------- | ------------------------------------------------- |\n| `tests/test_utils.py`            | Utility function tests                 | Fixtures, parametrized tests                      |\n| `tests/test_examples.py`         | Integration tests with dataset loading | Dataset path resolution, success pattern matching |\n| `tests/test_fsdp_engine_nccl.py` | Distributed tests                      | Torchrun integration                              |\n\n## Common Mistakes\n\n- **Missing test file registration**: Ensure file follows `test_*.py` naming\n- **GPU dependency without skip**: Always use `@pytest.mark.skipif` for GPU tests\n- **Incorrect tensor comparisons**: Use `torch.testing.assert_close()` not\n  `assert tensor.equal()`\n- **Memory leaks in GPU tests**: Clean up with `torch.cuda.empty_cache()`\n- **Mocking too much**: Don't mock FSDP/DTensor internals\n- **Unclear test names**: Follow `test_<what>_<condition>_<expected>` pattern\n- **No docstrings**: Add descriptive docstrings to test functions\n\n## Integration with Other Skills\n\nThis skill complements other AReaL development skills:\n\n- **After `/add-dataset`**: Add tests for new dataset loaders\n- **After `/add-workflow`**: Add tests for new workflows\n- **After `/add-reward`**: Add tests for new reward functions\n- **With expert agents**: Reference this skill when planning test implementation\n\n## Running Tests\n\n```bash\n# First check GPU availability (many tests require GPU)\npython -c \"import torch; print('GPU available:', torch.cuda.is_available())\"\n\n# Run specific test file\nuv run pytest tests/test_<name>.py\n\n# Skip slow tests (CI default)\nuv run pytest -m \"not slow\"\n\n# Run with verbose output\nuv run pytest -v\n\n# Run distributed tests (requires torchrun and multi-GPU)\n# Note: Usually invoked via pytest test files\ntorchrun --nproc_per_node=2 tests/torchrun/run_<test>.py\n```\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .opencode/skills/add-unit-tests/SKILL.md\nInvocation: /add-unit-tests\n\n## Purpose\n\nStep-by-step guide for adding unit tests to AReaL.\n\n## How to Update\n\n### When Testing Conventions Change\n1. Update \"Key Requirements\" section based on `testing.md`\n2. Update test examples to match new patterns\n3. Update reference implementations\n\n### When Test Types Need Update\n1. Update \"Understand Test Types\" table (currently two main types)\n2. Add new examples if needed\n3. Update common mistakes\n\n### Integration with Other Skills\nEnsure references to other skills (`/add-dataset`, `/add-workflow`, `/add-reward`) remain accurate.\n\n================================================================================\n-->\n",".opencode/skills/add-workflow/SKILL.md":"---\nname: add-workflow\ndescription: Guide for adding a new RolloutWorkflow to AReaL. Use when user wants to create a new workflow.\n---\n\n# Add Workflow\n\nAdd a new RolloutWorkflow implementation to AReaL.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a workflow?\"\n- User wants to create a new RolloutWorkflow\n- User mentions implementing a custom rollout\n\n## Prerequisites\n\nBefore starting, ensure you understand:\n\n- The workflow's purpose and requirements\n- Input/output data format\n- Reward function to use\n\n## Step-by-Step Guide\n\n### Step 1: Create Workflow File\n\nCreate `areal/workflow/<name>.py`:\n\n```python\nimport uuid\nfrom typing import Any, Callable\n\nimport torch\n\nfrom areal.api.cli_args import GenerationHyperparameters\nfrom areal.api.engine_api import InferenceEngine\nfrom areal.api.io_struct import ModelRequest, ModelResponse\nfrom areal.api.reward_api import AsyncRewardWrapper\nfrom areal.api.workflow_api import RolloutWorkflow\nfrom areal.utils import logging\n\nlogger = logging.getLogger(\"MyWorkflow\")\n\n\nclass MyWorkflow(RolloutWorkflow):\n    \"\"\"Description of your workflow.\"\"\"\n\n    def __init__(\n        self,\n        gconfig: GenerationHyperparameters,\n        tokenizer,\n        reward_fn: Callable,\n    ):\n        self.gconfig = gconfig.new_with_stop_and_pad_token_ids(tokenizer)\n        self.tokenizer = tokenizer\n        self.async_reward_fn = AsyncRewardWrapper(reward_fn)\n\n    async def arun_episode(\n        self,\n        engine: InferenceEngine,\n        data: dict[str, Any],\n    ) -> dict[str, Any] | None | dict[str, InteractionWithTokenLogpReward]:\n        \"\"\"Run a single episode. MUST be async and non-blocking.\"\"\"\n\n        # 1. Prepare input_ids from data\n        input_ids = self.tokenizer.apply_chat_template(\n            data[\"messages\"],\n            tokenize=True,\n            add_generation_prompt=True,\n        )\n\n        # 2. Build ModelRequest\n        req = ModelRequest(\n            rid=uuid.uuid4().hex,\n            input_ids=list(input_ids),\n            gconfig=self.gconfig.new(n_samples=1),\n            tokenizer=self.tokenizer,\n        )\n\n        # 3. Generate completion (async)\n        resp: ModelResponse = await engine.agenerate(req)\n\n        # 4. Compute reward (async)\n        prompt_str = self.tokenizer.decode(input_ids)\n        completion_str = self.tokenizer.decode(resp.output_tokens)\n        reward = await self.async_reward_fn(\n            prompt_str,\n            completion_str,\n            resp.input_tokens,\n            resp.output_tokens,\n            **data,\n        )\n\n        # 5. Return results in expected format\n        return {\n            \"input_ids\": torch.tensor(resp.input_tokens),\n            \"output_ids\": torch.tensor(resp.output_tokens),\n            \"reward\": torch.tensor(reward),\n        }\n```\n\n### Step 2: Register in __init__.py\n\nAdd to `areal/workflow/__init__.py`:\n\n```python\nfrom areal.workflow.<name> import MyWorkflow\n\n__all__ = [\n    # ... existing exports\n    \"MyWorkflow\",\n]\n```\n\n### Step 3: Update Entry Script\n\nUpdate your training script to use the new workflow:\n\n```python\ntrainer.train(\n    workflow=\"areal.workflow.<name>.MyWorkflow\",\n    # ... other args\n)\n```\n\n### Step 4: Add Tests\n\nCreate `tests/test_<name>_workflow.py`:\n\n```python\nimport pytest\nfrom areal.workflow.<name> import MyWorkflow\n\n@pytest.mark.asyncio\nasync def test_workflow_basic():\n    # Test basic functionality\n    pass\n```\n\n## Reference Implementations\n\n| Workflow           | File                            | Description                |\n| ------------------ | ------------------------------- | -------------------------- |\n| MultiTurnWorkflow  | `areal/workflow/multi_turn.py`  | Multi-turn conversation    |\n| RLVRWorkflow       | `areal/workflow/rlvr.py`        | RL with verifiable rewards |\n| VisionRLVRWorkflow | `areal/workflow/vision_rlvr.py` | Vision + RLVR              |\n\n## Key Requirements\n\n1. **Async**: `arun_episode` must be `async def` and non-blocking\n1. **No sync I/O**: Use `aiofiles` for file operations\n1. **Wrap rewards**: Use `AsyncRewardWrapper` for reward functions\n1. **Tensor format**: Output tensors should be `[batch, seq_len, ...]`\n1. **Use helpers**: `concat_padded_tensors` for combining outputs\n\n## Common Mistakes\n\n- Using `open()` instead of `aiofiles.open()`\n- Forgetting to `await` async calls\n- Not wrapping reward function with `AsyncRewardWrapper`\n- Wrong tensor shape conventions\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .opencode/skills/add-workflow/SKILL.md\nInvocation: /add-workflow <name>\n\n## Purpose\n\nStep-by-step guide for adding new RolloutWorkflow implementations.\n\n## How to Update\n\n### When Workflow API Changes\n1. Update the code template in Step 1\n2. Update the required imports\n3. Update the method signature if changed\n\n### When New Patterns Emerge\n1. Add to \"Reference Implementations\" table\n2. Update \"Key Requirements\" if new requirements added\n\n================================================================================\n-->\n",".opencode/skills/commit-conventions/SKILL.md":"---\nname: commit-conventions\ndescription: AReaL commit message conventions. MUST load on every git commit -- provides Conventional Commits format with scope inference from file paths.\n---\n\n# Commit Conventions\n\nCommit message conventions and scope inference rules for the AReaL repository.\n\n## When to Use\n\n**ALWAYS load this skill when making any git commit in AReaL.** This includes:\n\n- Direct commits (`git commit`)\n- Commits during PR creation (`/create-pr`)\n- Commits delegated via `task(load_skills=[\"commit-conventions\"], ...)`\n- Any agent workflow that produces a commit\n\n## Commit Message Format\n\n```\n<type>(<scope>): <subject>\n\n<body>\n\n[Optional sections:]\nKey changes:\n- change 1\n- change 2\n\nRefs: #123, #456\n```\n\n## Type Selection\n\n| Type       | When to Use                      |\n| ---------- | -------------------------------- |\n| `feat`     | New feature or capability        |\n| `fix`      | Bug fix                          |\n| `docs`     | Documentation only               |\n| `gov`      | Governance or maintainer changes |\n| `style`    | Formatting/style-only changes    |\n| `refactor` | Code change without feature/fix  |\n| `perf`     | Performance improvement          |\n| `test`     | Adding or fixing tests           |\n| `build`    | Build system or dependencies     |\n| `ci`       | CI pipeline or workflow changes  |\n| `chore`    | Build, deps, config changes      |\n| `revert`   | Revert a previous commit         |\n\n## Scope Inference\n\nInfer scope from the **primary** changed file paths:\n\n| File Path Pattern                                            | Scope                          |\n| ------------------------------------------------------------ | ------------------------------ |\n| `areal/workflow/`                                            | `workflow`                     |\n| `areal/engine/`                                              | `engine`                       |\n| `areal/reward/`                                              | `reward`                       |\n| `areal/dataset/`                                             | `dataset`                      |\n| `areal/api/`                                                 | `api`                          |\n| `areal/utils/`                                               | `utils`                        |\n| `areal/infra/`                                               | `infra`                        |\n| `areal/trainer/`                                             | `trainer`                      |\n| `areal/models/`                                              | `models`                       |\n| `areal/experimental/`                                        | `archon`                       |\n| `docs/`                                                      | `docs`                         |\n| `examples/`                                                  | `examples`                     |\n| `AGENTS.md`, `.agents/`, `.claude/`, `.codex/`, `.opencode/` | `agents`                       |\n| Multiple areas                                               | Omit scope or use broader term |\n\n## Rules\n\n- **Subject**: imperative mood, ~50-72 chars, no trailing period\n- **Body**: explain \"why\" not \"what\", wrap at 72 chars\n- **Key changes**: bullet list of main modifications (for complex commits with 3+ files)\n- **Refs**: reference issues/PRs if applicable\n\n## Examples\n\n**Single file fix:**\n\n```\nfix(reward): handle empty completion in gsm8k\n\nReturn 0 reward instead of raising exception when\ncompletion string is empty after extraction.\n```\n\n**Multi-file feature:**\n\n```\nfeat(engine): add CPU offload support to ArchonEngine\n\nEnable torch_memory_saver for model offloading during\nrollout phase to reduce GPU memory pressure.\n\nKey changes:\n- Add offload/onload methods to ArchonEngine\n- Integrate with weight update flow\n- Handle ROCm compatibility\n```\n\n**Docs only:**\n\n```\ndocs: update algorithm comparison table\n\nAdd SAPO and GSPO to the algorithm family documentation\nwith configuration examples.\n```\n\n**Agent/tooling changes:**\n\n```\nchore(agents): port review-pr command to OpenCode\n\nAdd OpenCode-native commands with task() category\ndelegation instead of hardcoded model names.\n\nKey changes:\n- Create .opencode/command/ with review-pr, create-pr\n- Replace hardcoded model routing with platform-native review routing\n- Add expert subagent consultation patterns\n```\n\n**Governance/maintainer changes:**\n\n```\ngov(agents): add maintainer ownership for service modules\n\nUpdate CODEOWNERS and maintainer references to reflect\ncurrent governance responsibilities.\n\nKey changes:\n- Add maintainers for agent_service and infra ownership\n- Align governance docs with updated reviewer responsibilities\n```\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .opencode/skills/commit-conventions/SKILL.md\nInvocation: Automatically loaded on every git commit via load_skills\n\n## Purpose\n\nProvides Conventional Commits format with AReaL-specific scope inference\nfrom file paths. Unlike other skills, this one is NOT user-triggered --\nit is loaded by the system/agent on every commit operation.\n\n## How to Update\n\n### When New Modules Are Added\n1. Add the file path pattern and scope to the \"Scope Inference\" table\n2. Keep table sorted by areal/ subpackages first, then top-level dirs\n\n### When Commit Types Change\n1. Update the \"Type Selection\" table\n2. Add/update examples to illustrate the new type\n\n### When Adding Examples\n1. Each example should demonstrate a distinct commit pattern\n2. Keep examples realistic -- use actual AReaL module names\n3. Show both subject-only and subject+body+key-changes variants\n\n### Important Design Decisions\n- This skill is ALWAYS loaded (not optional) -- keep it concise to\n  minimize token overhead on every commit\n- Scope inference is path-based, not content-based -- simpler and\n  more deterministic\n- \"Multiple areas\" -> omit scope rather than invent a new one\n\n================================================================================\n-->\n",".opencode/skills/debug-distributed/SKILL.md":"---\nname: debug-distributed\ndescription: Guide for debugging distributed training issues in AReaL. Use when user encounters hangs, wrong results, OOM, or communication errors.\n---\n\n# Debug Distributed Training\n\nDebugging guide for distributed training issues in AReaL (FSDP2, TP, CP, EP).\n\n## When to Use\n\nThis skill is triggered when:\n\n- Training hangs or deadlocks\n- Results differ across ranks or are numerically wrong\n- OOM errors in distributed settings\n- NCCL/communication errors or device mesh issues\n\n## Debugging Principles\n\n### Minimal Reproduction\n\n**Always follow the minimal demo principle**: Reproduce with the least amount of code to\nnarrow down the issue faster.\n\n```python\n# Bad: Debug in full training loop\n# Good: Create minimal script\nimport torch\nimport torch.distributed as dist\n\ndist.init_process_group(\"nccl\")\nrank = dist.get_rank()\n\n# Reproduce the exact operation that fails\ntensor = torch.ones(10).cuda()\ndist.all_reduce(tensor)  # <-- Isolate the failing op\nprint(f\"Rank {rank}: {tensor}\")\n```\n\n**Reduction strategy:**\n\n1. Remove unrelated model components\n1. Use small tensor sizes\n1. Reduce world_size to minimum (e.g., 2 GPUs)\n1. Remove torch.compile if possible\n1. Disable activation checkpointing\n\n## Step-by-Step Debugging Guide\n\n### 1. Hang Debugging (Deadlocks, Synchronization)\n\n**Environment Variables for Debugging**:\n\n```bash\n# Full debug logging\nexport TORCH_DISTRIBUTED_DEBUG=DETAIL\nexport NCCL_DEBUG=INFO\nexport NCCL_DEBUG_SUBSYS=ALL\n\n# torch.compile debugging\nexport TORCH_LOGS=\"+dynamo,recompiles\"\nexport TORCHDYNAMO_VERBOSE=1\n```\n\n**Dump Call Stack with py-spy** (for hung processes):\n\n```bash\n# Find process IDs\nps aux | grep python\n\n# Dump call stack of specific rank\npy-spy dump --pid <PID>\n\n# Record flame graph for performance analysis\npy-spy record -o profile.svg --pid <PID> --duration 30\n```\n\n**Common Causes**:\n\n1. **Mismatched Collectives**: One rank calls `all_reduce`, another doesn't.\n1. **Wrong Process Group**: Using wrong group for collective.\n1. **Tensor Shape Mismatch**: Different shapes across ranks.\n\n**Debug Steps**:\n\n```python\n# Verify group membership\nmesh = parallel_dims.get_mesh(\"dp_shard_cp\")\ngroup = mesh.get_group()\nprint(f\"Rank {dist.get_rank()}: group size = {dist.get_world_size(group)}\")\n\n# Print shapes on all ranks\nprint(f\"Rank {dist.get_rank()}: tensor.shape = {tensor.shape}\")\ndist.barrier()\n```\n\n**Timeout Adjustment** (for debugging only):\n\n```python\nfrom areal.engine.core.distributed import patch_dist_group_timeout\nfrom datetime import timedelta\npatch_dist_group_timeout(timedelta(minutes=30))\n```\n\n### 2. Wrong Results (Gradient, Reduction Issues)\n\n**Check DTensor Placements**:\n\n```python\nfrom torch.distributed.tensor import DTensor\nif isinstance(param, DTensor):\n    print(f\"Param {name}: placements={param.placements}, mesh={param.device_mesh}\")\n```\n\n**Verify Gradient Reduction**:\n\n```python\nfor name, param in model.named_parameters():\n    if param.grad is not None:\n        print(f\"Rank {dist.get_rank()}: {name} grad_sum = {param.grad.sum().item()}\")\n```\n\n### 3. OOM Issues (Memory, Sharding)\n\n**Check Memory Usage**:\n\n```python\nprint(f\"Rank {dist.get_rank()}: \"\n      f\"allocated={torch.cuda.memory_allocated()/1e9:.2f}GB, \"\n      f\"reserved={torch.cuda.memory_reserved()/1e9:.2f}GB\")\n```\n\n**Check FSDP Coverage**:\n\n```python\nfor name, param in model.named_parameters():\n    is_dtensor = isinstance(param, DTensor)\n    print(f\"{name}: is_dtensor={is_dtensor}, shape={param.shape}\")\n```\n\n### 4. Communication Errors\n\n| Error                     | Cause                | Solution                           |\n| ------------------------- | -------------------- | ---------------------------------- |\n| `NCCL WARN Cuda failure`  | GPU communication    | Check NCCL version, GPU topology   |\n| `RuntimeError: Timed out` | Rank synchronization | Increase timeout, check code paths |\n| `Invalid device mesh`     | Mesh configuration   | Verify world_size = dp * tp * cp   |\n\n## Debugging Tools\n\n### Environment Variables Reference\n\n| Variable                          | Purpose                                |\n| --------------------------------- | -------------------------------------- |\n| `TORCH_DISTRIBUTED_DEBUG=DETAIL`  | Detailed distributed logging           |\n| `NCCL_DEBUG=INFO`                 | NCCL communication logging             |\n| `NCCL_DEBUG_SUBSYS=ALL`           | All NCCL subsystems                    |\n| `TORCH_LOGS=\"+dynamo,recompiles\"` | torch.compile logging                  |\n| `TORCHDYNAMO_VERBOSE=1`           | Dynamo verbose output                  |\n| `CUDA_LAUNCH_BLOCKING=1`          | Synchronous CUDA (slow, for debugging) |\n\n### py-spy for Call Stack Analysis\n\n```bash\n# Install\npip install py-spy\n\n# Dump call stack of hung process\npy-spy dump --pid <PID>\n\n# Dump all Python processes\npgrep -f python | xargs -I {} py-spy dump --pid {}\n\n# Record flame graph\npy-spy record -o profile.svg --pid <PID> --duration 30\n```\n\n### Rank-Conditional Printing\n\n```python\ndef print_all_ranks(msg):\n    for r in range(dist.get_world_size()):\n        if dist.get_rank() == r:\n            print(f\"[Rank {r}] {msg}\")\n        dist.barrier()\n```\n\n### Check Device Mesh\n\n```python\ndef debug_mesh(parallel_dims):\n    mesh = parallel_dims.world_mesh\n    for dim_name in mesh.mesh_dim_names:\n        submesh = parallel_dims.get_mesh(dim_name)\n        if submesh:\n            print(f\"Rank {dist.get_rank()}: {dim_name} size={submesh.size()}\")\n```\n\n### Validate Tensor Consistency\n\n```python\ndef check_tensor_consistency(tensor, name, group=None):\n    local_sum = tensor.sum().item()\n    tensor_sums = [None] * dist.get_world_size(group)\n    dist.all_gather_object(tensor_sums, local_sum, group=group)\n    if dist.get_rank() == 0 and len(set(tensor_sums)) > 1:\n        print(f\"WARNING: {name} inconsistent: {tensor_sums}\")\n```\n\n## Key Files Reference\n\n| Component       | File                                                          |\n| --------------- | ------------------------------------------------------------- |\n| Parallel Dims   | `areal/experimental/models/archon/parallel_dims.py`           |\n| Expert Parallel | `areal/experimental/models/archon/expert_parallel.py`         |\n| Ulysses (CP)    | `areal/experimental/models/archon/ulysses.py`                 |\n| FSDP/TP Apply   | `areal/experimental/models/archon/qwen2/infra/parallelize.py` |\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .opencode/skills/debug-distributed/SKILL.md\nInvocation: /debug-distributed\n\n## Purpose\n\nDebugging guide for distributed training issues.\nCovers FSDP2, Tensor Parallelism, Context Parallelism, and Expert Parallelism.\n\n## How to Update\n\n### When Adding New Parallelism Features\n1. Add section for the parallelism type\n2. Document common error patterns and debugging snippets\n\n### When PyTorch Distributed APIs Change\n1. Update DTensor/DeviceMesh examples\n2. Update environment variable references\n\n### When New Error Patterns Emerge\n1. Add to \"Common Errors and Solutions\" table\n2. Reference relevant source files\n\n================================================================================\n-->\n","AGENTS.md":"<!-- Go-to brief for AI coding agents working on AReaL. -->\n\n# AGENTS.md -- AReaL Agent Operations Guide\n\n## Quick reference\n\n**Tech stack**: Python 3.12+ | PyTorch | FSDP2 / Megatron / Archon | SGLang / vLLM\n\n```bash\n# Environment\nuv sync --extra cuda            # CUDA + SGLang inference (default); for vLLM: cp pyproject.vllm.toml pyproject.toml && cp uv.vllm.lock uv.lock && uv sync --extra cuda\nuv sync --extra sandbox         # Daytona cloud sandbox backend (optional)\nsource .venv/bin/activate        # activate venv BEFORE pre-commit or git commit if venv exists\npre-commit install --install-hooks  # hooks: Ruff, clang-format, mdformat, nbstripout, conventional-commits\npre-commit run --all-files       # lint + format everything\n\n# Tests\nuv run pytest tests/test_<topic>.py\n\n# CLI docs\nuv run python docs/generate_cli_docs.py\n\n# Docs build (canonical, release-aligned)\n./docs/build_all.sh\n# Do NOT use `jupyter-book build docs/en|docs/zh` directly for final preview/release,\n# because it skips AReaL-specific static setup and output packaging.\n```\n\n**Hard rules** -- never violate:\n\n- No wildcard imports (`from x import *`).\n- No hardcoded secrets, paths, or endpoints.\n- No skipping pre-commit hooks.\n- No guessing cluster configs or rebuilding CUDA/driver stacks.\n- Integration tests require multi-node hardware -- explain skips explicitly.\n\n**Always do**:\n\n- Read relevant files before modifying code.\n- Run `pre-commit run --all-files` before committing.\n- Follow existing code patterns in the same module.\n- Add tests for new functionality.\n- Ask for decisions and clarifications with short, structured options instead of broad\n  open-ended questions. Use the platform's native question/clarification tool if\n  available.\n\n**Ask first** before:\n\n- Modifying config structures in `areal/api/cli_args.py`.\n- Adding new dependencies.\n- Changing launcher or scheduler logic.\n- Deleting or renaming public APIs.\n\nWhen unsure, leave a `TODO(agent)` comment and note the constraint in your response.\n\n______________________________________________________________________\n\n## Repository map\n\n```\nareal/                     Core Python package\n|-- api/                   Config dataclasses, contracts, IO structs\n|-- dataset/               Stateful dataset loaders (GSM8K, Geometry3K, CLEVR, ...)\n|-- engine/                Training backends (FSDP2, Megatron) + inference adapters\n|-- experimental/          Prototype engines/workflows (Archon MoE engine)\n|-- infra/                 Launchers (Local/Ray/Slurm), schedulers, utilities\n|-- models/                Model adapters (Megatron-Core, Transformers, custom heads)\n|-- reward/                Built-in reward functions + math parsers\n|-- tests/                 Unit/integration test suites\n|-- trainer/               High-level orchestrators (PPOTrainer, SFTTrainer)\n|-- utils/                 Cross-cutting helpers (logging, data, checkpoints, RL ops)\n+-- workflow/              RolloutWorkflow implementations (RLVR, multi-turn, vision)\n\ndocs/                      Jupyter Book docs (https://areal-project.github.io/AReaL/)\nexamples/                  Training scripts and launcher recipes\n```\n\n______________________________________________________________________\n\n## Code style & patterns\n\n- **Composition over inheritance** -- keep hierarchies \\<= 2 levels; prefer delegation.\n\n| Type             | Pattern         | Example                                   |\n| ---------------- | --------------- | ----------------------------------------- |\n| Config dataclass | `XxxConfig`     | `GRPOConfig`, `FSDPEngineConfig`          |\n| Engine class     | `XxxEngine`     | `FSDPEngine`, `ArchonEngine`              |\n| Workflow class   | `XxxWorkflow`   | `RLVRWorkflow`, `MultiTurnWorkflow`       |\n| Reward function  | `xxx_reward_fn` | `gsm8k_reward_fn`, `geometry3k_reward_fn` |\n\n**Logging**: `areal.utils.logging.getLogger(name)` with **PascalCase** names -- never\n`print` or `logging.__name__`. Per-rank format: `[{Component} Rank {N}]`. Register new\nloggers with color in `areal/utils/logging.py`.\n\n**Performance**:\n\n- No GPU-CPU sync in hot paths (`.item()`, `.tolist()`, `print(tensor)`).\n- Batch ops over Python loops on tensor elements.\n- Explicit `dtype`/`device`; `torch.Size` assertions for shape validation.\n\n**Typing & imports**: explicit type hints; reuse `areal/api/cli_args.py` dataclasses; no\nwildcard imports; heavy optional deps inside functions.\n\n**Async**: rollout workflows must stay non-blocking (`await` + `aiofiles`); no sync I/O\nin `arun_episode`.\n\n______________________________________________________________________\n\n## Domain experts & skills\n\nFire the appropriate **expert subagent** or **load a skill** based on what you're\nworking on. Experts are read-only consultants with deep domain knowledge; skills are\nstep-by-step implementation guides.\n\n| Working on...                | Fire subagent      | Load skill          |\n| ---------------------------- | ------------------ | ------------------- |\n| FSDP engine code             | `fsdp-expert`      | --                  |\n| Archon engine / new model    | `archon-expert`    | `add-archon-model`  |\n| Megatron engine code         | `megatron-expert`  | --                  |\n| RL algorithms / PPO / GRPO   | `algorithm-expert` | --                  |\n| Launcher / scheduler / infra | `launcher-expert`  | `debug-distributed` |\n| New reward function          | --                 | `add-reward`        |\n| New dataset loader           | --                 | `add-dataset`       |\n| New rollout workflow         | --                 | `add-workflow`      |\n| Unit tests                   | --                 | `add-unit-tests`    |\n| Distributed debugging        | --                 | `debug-distributed` |\n\n**How to invoke experts and skills** (platform-specific):\n\n| Platform | Fire expert subagent                                                               | Load skill                                         |\n| -------- | ---------------------------------------------------------------------------------- | -------------------------------------------------- |\n| OpenCode | `task(subagent_type=\"<name>\", load_skills=[], run_in_background=true, prompt=\"…\")` | `skill(name=\"<name>\")` or `load_skills=[\"<name>\"]` |\n| Codex    | Invoke registered subagent by canonical name (see `.codex/config.toml`)            | Reference `.agents/skills/<name>/SKILL.md`         |\n\n**Harness layout**:\n\n| Component         | OpenCode                                | Codex                                                  |\n| ----------------- | --------------------------------------- | ------------------------------------------------------ |\n| Root instructions | `AGENTS.md`                             | `AGENTS.md`                                            |\n| Agent configs     | `.opencode/agents/*.md` (frontmatter)   | `.codex/config.toml` + `.codex/agents/*.toml` + `*.md` |\n| Skills            | `.opencode/skills/` + `.agents/skills/` | `.agents/skills/<name>/SKILL.md`                       |\n\nDirectly executable workflows (both platforms): `add-workflow`, `review-pr`,\n`create-pr`, `translate-doc-zh`.\n\n______________________________________________________________________\n\n## Core concepts\n\n**Trainer** orchestrator (`areal/trainer/`, `PPOTrainer`, `SFTTrainer`): manages the\ntraining loop, dataset loading, and workflow execution. Entry point:\n`examples/math/gsm8k_rl.py`.\n\n**Rollout workflows** (`areal/workflow/`, `RolloutWorkflow.arun_episode`): define how\nepisodes are generated. Use `add-workflow` skill for step-by-step guide.\n\n**Engines**: *Inference engines* handle async generation via `engine.agenerate()` and\nmanage weight updates. *Training engines* consume rollout tensors, compute PPO/GRPO\nupdates, and broadcast weight versions (FSDP2, Megatron, or Archon).\n\n**Weight versioning**: async workflows require version alignment via `WeightUpdateMeta`\n(`areal/api/engine_api.py`). Critical for correctness across distributed training.\n\n**Observability**: emit metrics via `stats_tracker.get()`, persist artifacts under\n`dump_dir`, checkpoint via `areal/utils/saver.py` / `recover.py`.\n\n**Launcher / scheduler**: training requires cluster setup (local / Ray / Slurm) via\nconfigs in `areal/infra/launcher/`. See `launcher-expert` for deployment guidance.\n\n______________________________________________________________________\n\n## API & config rules\n\n*Applies to: `areal/api/**`*\n\n- **Field ordering**: required -> common optional -> rare optional -> internal (`_`\n  prefix).\n- **Validation**: `__post_init__` with `ValueError` and clear message.\n- **Backward compat**: add fields with defaults; deprecate before removing; avoid type\n  changes.\n- **CLI**: use `Literal` for enum choices; all public configs need docstrings with\n  constraints.\n\n______________________________________________________________________\n\n## Distributed code rules\n\n*Applies to: `areal/engine/**`, `areal/experimental/**`*\n\n- Never create global process groups at module level; always pass `process_group`\n  explicitly.\n- `dist.get_rank(group)` not `dist.get_rank()` when group matters.\n- DeviceMesh dimensions must match `ArchonParallelDims`: `dp_shard`, `tp`, `cp`, `ep`,\n  `etp`.\n- All-reduce: all ranks must call. Broadcast: explicit `src`. Barrier: debugging only.\n\n| Issue         | Cause                            | Fix                       |\n| ------------- | -------------------------------- | ------------------------- |\n| Hang          | Mismatched collective calls      | All ranks call same op    |\n| Wrong results | Incorrect `ReduceOp`             | Check SUM vs MEAN         |\n| OOM           | Unsharded tensor on wrong device | Verify DTensor placements |\n\nDebug env vars: `TORCH_DISTRIBUTED_DEBUG=DETAIL`, `NCCL_DEBUG=INFO`,\n`CUDA_LAUNCH_BLOCKING=1`. See the `debug-distributed` skill for the full workflow.\n\n______________________________________________________________________\n\n## Testing rules\n\n*Applies to: `**/tests/**`, `test_*.py`*\n\n| Marker                                  | When                             |\n| --------------------------------------- | -------------------------------- |\n| `@pytest.mark.slow`                     | > 10s (excluded from default CI) |\n| `@pytest.mark.slow` + `@pytest.mark.ci` | Slow but must run in CI          |\n| `@pytest.mark.asyncio`                  | Async tests                      |\n\n- Naming: `test_<what>_<condition>_<expected>()` with Arrange/Act/Assert.\n- GPU: skip gracefully (`@pytest.mark.skipif(not CUDA_AVAILABLE, reason=\"...\")`).\n- Distributed mocking: `torch.distributed.fake_pg`; don't mock FSDP/DTensor internals.\n- Assertions: `torch.testing.assert_close()` with explicit `rtol`/`atol`; prefer\n  `tmp_path`, `monkeypatch`.\n\n| Suite       | Command                       | GPU       |\n| ----------- | ----------------------------- | --------- |\n| Unit        | `pytest tests/test_*.py`      | No        |\n| GRPO        | `pytest tests/grpo/`          | Yes       |\n| FSDP        | `pytest tests/test_fsdp_*.py` | Yes       |\n| Distributed | `pytest tests/torchrun/`      | Multi-GPU |\n\n______________________________________________________________________\n\n## Collaboration & review\n\n- **Branches**: kebab-case (`feature/multi-turn-metrics`, `bugfix/fsdp-weight-sync`).\n- **Commits**: Conventional Commits (e.g., `feat:`, `fix:`, `docs:`, `gov:`), ~72 char\n  subject, imperative voice. Squash WIP before PR.\n- **Pre-merge**: full pre-commit stack; doc-only edits need at least `mdformat --check`.\n- **PRs**: tie to issue, highlight risk areas, list test commands executed, note skipped\n  suites with reasons.\n\n| Skill                | Purpose                                                |\n| -------------------- | ------------------------------------------------------ |\n| `create-pr`          | Rebase, squash, and create or update a PR              |\n| `commit-conventions` | Commit message conventions to load before `git commit` |\n| `review-pr`          | Dynamic PR review with targeted expert consultation    |\n| `translate-doc-zh`   | Translate English docs to Chinese                      |\n\n______________________________________________________________________\n\n## Reference material\n\n- **Docs portal**: <https://areal-project.github.io/AReaL/>\n- **Quickstart**: `docs/tutorial/quickstart.md`\n- **Architecture**: `docs/tutorial/gsm8k_grpo.md`\n- **Customization**: `docs/customization/*.md`\n- **Algorithms**: `docs/algorithms/*.md`\n- **Best practices**: `docs/best_practices/*.md`\n- **CLI reference**: `docs/cli_reference.md`\n- **Agent workflow**: `docs/customization/agent.md`\n","CLAUDE.md":"# CLAUDE.md - AReaL\n\n## WHAT: Project Overview\n\nAReaL is a distributed RL training framework for LLM alignment via reinforcement\nlearning.\n\n**Tech Stack**: Python 3.12+ | PyTorch | FSDP2/Megatron | SGLang/vLLM\n\n**Core Directories**:\n\n- `areal/` - Core package\n  - `api/` - Config dataclasses, workflow/engine contracts\n  - `engine/` - FSDP2, Megatron, SGLang/vLLM adapters\n    - `fsdp_utils/` - FSDP2-specific utilities (checkpoint, grad, optimizer, parallel)\n    - `megatron_utils/` - Megatron/FP8 utilities (checkpoint, pipeline, quantization)\n    - `core/` - Engine-shared utilities (distributed, lock, model, offload)\n  - `infra/` - Infrastructure (launcher, scheduler, RPC)\n    - `utils/` - Infrastructure utilities (launcher, proc, http, concurrent, slurm, ray)\n  - `workflow/` - RolloutWorkflow implementations\n  - `reward/` - Reward functions\n  - `dataset/` - Dataset loaders\n  - `utils/` - Cross-cutting utilities (logging, data, checkpoints, network, RL\n    functional)\n- `examples/` - Training scripts and configs\n- `docs/` - Jupyter Book source\n\n## WHY: Purpose\n\n- Enable efficient RL training for LLM alignment at scale\n- Async rollout + distributed training for high throughput\n- Modular design: workflows, engines, rewards, and datasets are independently extensible\n\n## HOW: Core Commands\n\n```bash\n# Check environment\npython --version              # Requires 3.12+\nuv --version                  # Install: https://docs.astral.sh/uv/\n\n# Sync dependencies\nuv sync --extra cuda          # CUDA + SGLang inference (default)\n# For vLLM: cp pyproject.vllm.toml pyproject.toml && cp uv.vllm.lock uv.lock && uv sync --extra cuda\nuv sync --group dev           # Include dev/test packages\nuv run python3 areal/tools/validate_installation.py  # Validate installation\n\n# Pre-commit hooks\npre-commit install --install-hooks  # Set up hooks (run once)\npre-commit run --all-files    # Format and lint\n\n# Run tests\n# First check GPU availability (many tests require GPU)\npython -c \"import torch; print('GPU available:', torch.cuda.is_available())\"\nuv run pytest tests/test_<topic>.py\n\n# Generate CLI docs\nuv run python docs/generate_cli_docs.py\n\n# Build docs (canonical, release-aligned)\n./docs/build_all.sh\n# Do NOT use `jupyter-book build docs/en|docs/zh` directly for final preview/release,\n# because it skips AReaL-specific static setup and output packaging.\n```\n\n## Boundaries\n\n### Constraints\n\n- Designed for distributed GPU clusters; assume containerized execution\n- Integration tests require multi-node hardware; explain skips when unavailable\n- Secrets and endpoints are managed outside the repo\n\n### Always Do\n\n- Read relevant files before modifying code\n- Run `pre-commit run --all-files` before committing\n- Follow existing code patterns in the same module\n- Add tests for new functionality\n\n### Ask First\n\n- Modifying config structures in `areal/api/cli_args.py`\n- Adding new dependencies\n- Changing launcher or scheduler logic\n- Deleting or renaming public APIs\n- Running GPU/distributed tests (check GPU first:\n  `python -c \"import torch; print('GPU available:', torch.cuda.is_available())\"`)\n\n### Never Do\n\n- Hardcode secrets, paths, or endpoints\n- Skip pre-commit hooks\n- Guess cluster configs or rebuild CUDA/driver stacks\n- Use wildcard imports (`from x import *`)\n\n## Progressive Disclosure: Detailed Guides\n\n| Task                   | Reference                                                     |\n| ---------------------- | ------------------------------------------------------------- |\n| Add Workflow           | `docs/customization/agent.md`, `areal/workflow/multi_turn.py` |\n| Add Dataset            | `docs/customization/`, `areal/dataset/gsm8k.py`               |\n| Add Reward             | `areal/api/reward_api.py`, `areal/reward/geometry3k.py`       |\n| Add Archon Model       | `areal/experimental/models/archon/qwen2/`, `qwen3/`           |\n| Algorithm Details      | `docs/algorithms/*.md`                                        |\n| R3 Router Replay       | `r3/README.md`                                                |\n| Quickstart             | `docs/tutorial/quickstart.md`                                 |\n| Architecture Deep Dive | `docs/tutorial/gsm8k_grpo.md`                                 |\n| CLI Reference          | `docs/cli_reference.md`                                       |\n\n## Git Workflow\n\n- **Commits**: Conventional Commits (e.g., `feat:`, `fix:`, `docs:`, `gov:`), ~72 chars\n  subject, imperative voice, reasoning in body\n- **Squash**: Squash WIP commits before opening PR\n- **PR requirements**: Run pre-commit, document test coverage, note hardware limitations\n\n## Extended Configuration\n\nSee `.claude/agents/`, `.claude/skills/`, `.claude/commands/`, and `.claude/rules/` for\nspecialized instructions.\n\n### Agents\n\n| Agent                       | Purpose                                   | Activation Trigger                                                  |\n| --------------------------- | ----------------------------------------- | ------------------------------------------------------------------- |\n| `planner`                   | Implementation planning                   | Before multi-file changes, new features, or architectural decisions |\n| `simple-code-reviewer`      | Quick code quality checks                 | After code changes, before committing                               |\n| `code-verifier`             | Formatting/linting/tests                  | After code changes, before committing                               |\n| `fsdp-engine-expert`        | FSDPEngine implementation                 | FSDPEngine code changes or questions                                |\n| `archon-engine-expert`      | ArchonEngine implementation               | ArchonEngine code changes or questions                              |\n| `megatron-engine-expert`    | MegatronEngine implementation             | MegatronEngine code changes or questions                            |\n| `algorithm-expert`          | RL algorithms                             | GRPO/PPO/DAPO questions                                             |\n| `launcher-scheduler-expert` | Cluster launching and resource scheduling | Launcher/scheduler code changes or configuration questions          |\n\n**Stage-by-Stage Agent Guidance**:\n\n1. **Planning Stage** (Before coding): Use `planner` for architecture design and\n   implementation planning\n1. **Code Formatting & Linting** (After coding): Use `code-verifier` to automatically\n   run formatting, linting, and tests, catching syntax errors and style issues quickly\n1. **Code Quality Check** (After formatting): Use `simple-code-reviewer` for quick code\n   quality checks, focusing on logic issues and code smells\n\n### Skills (Guided Development Workflows)\n\nSkills provide step-by-step guides for common development tasks:\n\n- `/add-dataset` - Dataset loader creation guide\n- `/add-workflow` - Workflow implementation guide\n- `/add-reward` - Reward function guide\n- `/add-archon-model` - Archon engine model architecture guide\n- `/debug-distributed` - Distributed debugging guide\n- `/add-unit-tests` - Test development guide (NEW)\n\n### Commands (User-invoked Actions)\n\nCommands perform specific actions when invoked:\n\n- `/create-pr` - Rebase, squash commits, and create/update PR with intelligent messages\n- `/gen-commit-msg` - Generate commit messages from staged changes\n- `/review-pr` - Intelligent PR code review with dynamic agent allocation\n- `/translate-doc-zh` - Translate English documentation to Chinese\n\n### Rules (Code Quality Standards)\n\nProject-wide standards enforced across all code changes:\n\n- `api-config.md` - Configuration dataclass design patterns\n- `code-style.md` - Coding conventions beyond pre-commit hooks\n- `distributed.md` - Distributed training patterns and constraints\n- `testing.md` - Testing strategy and coverage requirements\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"<!-- Go-to brief for AI coding agents working on AReaL. -->\n\n# AGENTS.md -- AReaL Agent Operations Guide\n\n## Quick reference\n\n**Tech stack**: Python 3.12+ | PyTorch | FSDP2 / Megatron / Archon | SGLang / vLLM\n\n```bash\n# Environment\nuv sync --extra cuda            # CUDA + SGLang inference (default); for vLLM: cp pyproject.vllm.toml pyproject.toml && cp uv.vllm.lock uv.lock && uv sync --extra cuda\nuv sync --extra sandbox         # Daytona cloud sandbox backend (optional)\nsource .venv/bin/activate        # activate venv BEFORE pre-commit or git commit if venv exists\npre-commit install --install-hooks  # hooks: Ruff, clang-format, mdformat, nbstripout, conventional-commits\npre-commit run --all-files       # lint + format everything\n\n# Tests\nuv run pytest tests/test_<topic>.py\n\n# CLI docs\nuv run python docs/generate_cli_docs.py\n\n# Docs build (canonical, release-aligned)\n./docs/build_all.sh\n# Do NOT use `jupyter-book build docs/en|docs/zh` directly for final preview/release,\n# because it skips AReaL-specific static setup and output packaging.\n```\n\n**Hard rules** -- never violate:\n\n- No wildcard imports (`from x import *`).\n- No hardcoded secrets, paths, or endpoints.\n- No skipping pre-commit hooks.\n- No guessing cluster configs or rebuilding CUDA/driver stacks.\n- Integration tests require multi-node hardware -- explain skips explicitly.\n\n**Always do**:\n\n- Read relevant files before modifying code.\n- Run `pre-commit run --all-files` before committing.\n- Follow existing code patterns in the same module.\n- Add tests for new functionality.\n- Ask for decisions and clarifications with short, structured options instead of broad\n  open-ended questions. Use the platform's native question/clarification tool if\n  available.\n\n**Ask first** before:\n\n- Modifying config structures in `areal/api/cli_args.py`.\n- Adding new dependencies.\n- Changing launcher or scheduler logic.\n- Deleting or renaming public APIs.\n\nWhen unsure, leave a `TODO(agent)` comment and note the constraint in your response.\n\n______________________________________________________________________\n\n## Repository map\n\n```\nareal/                     Core Python package\n|-- api/                   Config dataclasses, contracts, IO structs\n|-- dataset/               Stateful dataset loaders (GSM8K, Geometry3K, CLEVR, ...)\n|-- engine/                Training backends (FSDP2, Megatron) + inference adapters\n|-- experimental/          Prototype engines/workflows (Archon MoE engine)\n|-- infra/                 Launchers (Local/Ray/Slurm), schedulers, utilities\n|-- models/                Model adapters (Megatron-Core, Transformers, custom heads)\n|-- reward/                Built-in reward functions + math parsers\n|-- tests/                 Unit/integration test suites\n|-- trainer/               High-level orchestrators (PPOTrainer, SFTTrainer)\n|-- utils/                 Cross-cutting helpers (logging, data, checkpoints, RL ops)\n+-- workflow/              RolloutWorkflow implementations (RLVR, multi-turn, vision)\n\ndocs/                      Jupyter Book docs (https://areal-project.github.io/AReaL/)\nexamples/                  Training scripts and launcher recipes\n```\n\n______________________________________________________________________\n\n## Code style & patterns\n\n- **Composition over inheritance** -- keep hierarchies \\<= 2 levels; prefer delegation.\n\n| Type             | Pattern         | Example                                   |\n| ---------------- | --------------- | ----------------------------------------- |\n| Config dataclass | `XxxConfig`     | `GRPOConfig`, `FSDPEngineConfig`          |\n| Engine class     | `XxxEngine`     | `FSDPEngine`, `ArchonEngine`              |\n| Workflow class   | `XxxWorkflow`   | `RLVRWorkflow`, `MultiTurnWorkflow`       |\n| Reward function  | `xxx_reward_fn` | `gsm8k_reward_fn`, `geometry3k_reward_fn` |\n\n**Logging**: `areal.utils.logging.getLogger(name)` with **PascalCase** names -- never\n`print` or `logging.__name__`. Per-rank format: `[{Component} Rank {N}]`. Register new\nloggers with color in `areal/utils/logging.py`.\n\n**Performance**:\n\n- No GPU-CPU sync in hot paths (`.item()`, `.tolist()`, `print(tensor)`).\n- Batch ops over Python loops on tensor elements.\n- Explicit `dtype`/`device`; `torch.Size` assertions for shape validation.\n\n**Typing & imports**: explicit type hints; reuse `areal/api/cli_args.py` dataclasses; no\nwildcard imports; heavy optional deps inside functions.\n\n**Async**: rollout workflows must stay non-blocking (`await` + `aiofiles`); no sync I/O\nin `arun_episode`.\n\n______________________________________________________________________\n\n## Domain experts & skills\n\nFire the appropriate **expert subagent** or **load a skill** based on what you're\nworking on. Experts are read-only consultants with deep domain knowledge; skills are\nstep-by-step implementation guides.\n\n| Working on...                | Fire subagent      | Load skill          |\n| ---------------------------- | ------------------ | ------------------- |\n| FSDP engine code             | `fsdp-expert`      | --                  |\n| Archon engine / new model    | `archon-expert`    | `add-archon-model`  |\n| Megatron engine code         | `megatron-expert`  | --                  |\n| RL algorithms / PPO / GRPO   | `algorithm-expert` | --                  |\n| Launcher / scheduler / infra | `launcher-expert`  | `debug-distributed` |\n| New reward function          | --                 | `add-reward`        |\n| New dataset loader           | --                 | `add-dataset`       |\n| New rollout workflow         | --                 | `add-workflow`      |\n| Unit tests                   | --                 | `add-unit-tests`    |\n| Distributed debugging        | --                 | `debug-distributed` |\n\n**How to invoke experts and skills** (platform-specific):\n\n| Platform | Fire expert subagent                                                               | Load skill                                         |\n| -------- | ---------------------------------------------------------------------------------- | -------------------------------------------------- |\n| OpenCode | `task(subagent_type=\"<name>\", load_skills=[], run_in_background=true, prompt=\"…\")` | `skill(name=\"<name>\")` or `load_skills=[\"<name>\"]` |\n| Codex    | Invoke registered subagent by canonical name (see `.codex/config.toml`)            | Reference `.agents/skills/<name>/SKILL.md`         |\n\n**Harness layout**:\n\n| Component         | OpenCode                                | Codex                                                  |\n| ----------------- | --------------------------------------- | ------------------------------------------------------ |\n| Root instructions | `AGENTS.md`                             | `AGENTS.md`                                            |\n| Agent configs     | `.opencode/agents/*.md` (frontmatter)   | `.codex/config.toml` + `.codex/agents/*.toml` + `*.md` |\n| Skills            | `.opencode/skills/` + `.agents/skills/` | `.agents/skills/<name>/SKILL.md`                       |\n\nDirectly executable workflows (both platforms): `add-workflow`, `review-pr`,\n`create-pr`, `translate-doc-zh`.\n\n______________________________________________________________________\n\n## Core concepts\n\n**Trainer** orchestrator (`areal/trainer/`, `PPOTrainer`, `SFTTrainer`): manages the\ntraining loop, dataset loading, and workflow execution. Entry point:\n`examples/math/gsm8k_rl.py`.\n\n**Rollout workflows** (`areal/workflow/`, `RolloutWorkflow.arun_episode`): define how\nepisodes are generated. Use `add-workflow` skill for step-by-step guide.\n\n**Engines**: *Inference engines* handle async generation via `engine.agenerate()` and\nmanage weight updates. *Training engines* consume rollout tensors, compute PPO/GRPO\nupdates, and broadcast weight versions (FSDP2, Megatron, or Archon).\n\n**Weight versioning**: async workflows require version alignment via `WeightUpdateMeta`\n(`areal/api/engine_api.py`). Critical for correctness across distributed training.\n\n**Observability**: emit metrics via `stats_tracker.get()`, persist artifacts under\n`dump_dir`, checkpoint via `areal/utils/saver.py` / `recover.py`.\n\n**Launcher / scheduler**: training requires cluster setup (local / Ray / Slurm) via\nconfigs in `areal/infra/launcher/`. See `launcher-expert` for deployment guidance.\n\n______________________________________________________________________\n\n## API & config rules\n\n*Applies to: `areal/api/**`*\n\n- **Field ordering**: required -> common optional -> rare optional -> internal (`_`\n  prefix).\n- **Validation**: `__post_init__` with `ValueError` and clear message.\n- **Backward compat**: add fields with defaults; deprecate before removing; avoid type\n  changes.\n- **CLI**: use `Literal` for enum choices; all public configs need docstrings with\n  constraints.\n\n______________________________________________________________________\n\n## Distributed code rules\n\n*Applies to: `areal/engine/**`, `areal/experimental/**`*\n\n- Never create global process groups at module level; always pass `process_group`\n  explicitly.\n- `dist.get_rank(group)` not `dist.get_rank()` when group matters.\n- DeviceMesh dimensions must match `ArchonParallelDims`: `dp_shard`, `tp`, `cp`, `ep`,\n  `etp`.\n- All-reduce: all ranks must call. Broadcast: explicit `src`. Barrier: debugging only.\n\n| Issue         | Cause                            | Fix                       |\n| ------------- | -------------------------------- | ------------------------- |\n| Hang          | Mismatched collective calls      | All ranks call same op    |\n| Wrong results | Incorrect `ReduceOp`             | Check SUM vs MEAN         |\n| OOM           | Unsharded tensor on wrong device | Verify DTensor placements |\n\nDebug env vars: `TORCH_DISTRIBUTED_DEBUG=DETAIL`, `NCCL_DEBUG=INFO`,\n`CUDA_LAUNCH_BLOCKING=1`. See the `debug-distributed` skill for the full workflow.\n\n______________________________________________________________________\n\n## Testing rules\n\n*Applies to: `**/tests/**`, `test_*.py`*\n\n| Marker                                  | When                             |\n| --------------------------------------- | -------------------------------- |\n| `@pytest.mark.slow`                     | > 10s (excluded from default CI) |\n| `@pytest.mark.slow` + `@pytest.mark.ci` | Slow but must run in CI          |\n| `@pytest.mark.asyncio`                  | Async tests                      |\n\n- Naming: `test_<what>_<condition>_<expected>()` with Arrange/Act/Assert.\n- GPU: skip gracefully (`@pytest.mark.skipif(not CUDA_AVAILABLE, reason=\"...\")`).\n- Distributed mocking: `torch.distributed.fake_pg`; don't mock FSDP/DTensor internals.\n- Assertions: `torch.testing.assert_close()` with explicit `rtol`/`atol`; prefer\n  `tmp_path`, `monkeypatch`.\n\n| Suite       | Command                       | GPU       |\n| ----------- | ----------------------------- | --------- |\n| Unit        | `pytest tests/test_*.py`      | No        |\n| GRPO        | `pytest tests/grpo/`          | Yes       |\n| FSDP        | `pytest tests/test_fsdp_*.py` | Yes       |\n| Distributed | `pytest tests/torchrun/`      | Multi-GPU |\n\n______________________________________________________________________\n\n## Collaboration & review\n\n- **Branches**: kebab-case (`feature/multi-turn-metrics`, `bugfix/fsdp-weight-sync`).\n- **Commits**: Conventional Commits (e.g., `feat:`, `fix:`, `docs:`, `gov:`), ~72 char\n  subject, imperative voice. Squash WIP before PR.\n- **Pre-merge**: full pre-commit stack; doc-only edits need at least `mdformat --check`.\n- **PRs**: tie to issue, highlight risk areas, list test commands executed, note skipped\n  suites with reasons.\n\n| Skill                | Purpose                                                |\n| -------------------- | ------------------------------------------------------ |\n| `create-pr`          | Rebase, squash, and create or update a PR              |\n| `commit-conventions` | Commit message conventions to load before `git commit` |\n| `review-pr`          | Dynamic PR review with targeted expert consultation    |\n| `translate-doc-zh`   | Translate English docs to Chinese                      |\n\n______________________________________________________________________\n\n## Reference material\n\n- **Docs portal**: <https://areal-project.github.io/AReaL/>\n- **Quickstart**: `docs/tutorial/quickstart.md`\n- **Architecture**: `docs/tutorial/gsm8k_grpo.md`\n- **Customization**: `docs/customization/*.md`\n- **Algorithms**: `docs/algorithms/*.md`\n- **Best practices**: `docs/best_practices/*.md`\n- **CLI reference**: `docs/cli_reference.md`\n- **Agent workflow**: `docs/customization/agent.md`\n","isInternal":false,"tokens":2764,"sizeBytes":12622},{"name":"CLAUDE.md","path":"CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/CLAUDE.md","title":"Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"# CLAUDE.md - AReaL\n\n## WHAT: Project Overview\n\nAReaL is a distributed RL training framework for LLM alignment via reinforcement\nlearning.\n\n**Tech Stack**: Python 3.12+ | PyTorch | FSDP2/Megatron | SGLang/vLLM\n\n**Core Directories**:\n\n- `areal/` - Core package\n  - `api/` - Config dataclasses, workflow/engine contracts\n  - `engine/` - FSDP2, Megatron, SGLang/vLLM adapters\n    - `fsdp_utils/` - FSDP2-specific utilities (checkpoint, grad, optimizer, parallel)\n    - `megatron_utils/` - Megatron/FP8 utilities (checkpoint, pipeline, quantization)\n    - `core/` - Engine-shared utilities (distributed, lock, model, offload)\n  - `infra/` - Infrastructure (launcher, scheduler, RPC)\n    - `utils/` - Infrastructure utilities (launcher, proc, http, concurrent, slurm, ray)\n  - `workflow/` - RolloutWorkflow implementations\n  - `reward/` - Reward functions\n  - `dataset/` - Dataset loaders\n  - `utils/` - Cross-cutting utilities (logging, data, checkpoints, network, RL\n    functional)\n- `examples/` - Training scripts and configs\n- `docs/` - Jupyter Book source\n\n## WHY: Purpose\n\n- Enable efficient RL training for LLM alignment at scale\n- Async rollout + distributed training for high throughput\n- Modular design: workflows, engines, rewards, and datasets are independently extensible\n\n## HOW: Core Commands\n\n```bash\n# Check environment\npython --version              # Requires 3.12+\nuv --version                  # Install: https://docs.astral.sh/uv/\n\n# Sync dependencies\nuv sync --extra cuda          # CUDA + SGLang inference (default)\n# For vLLM: cp pyproject.vllm.toml pyproject.toml && cp uv.vllm.lock uv.lock && uv sync --extra cuda\nuv sync --group dev           # Include dev/test packages\nuv run python3 areal/tools/validate_installation.py  # Validate installation\n\n# Pre-commit hooks\npre-commit install --install-hooks  # Set up hooks (run once)\npre-commit run --all-files    # Format and lint\n\n# Run tests\n# First check GPU availability (many tests require GPU)\npython -c \"import torch; print('GPU available:', torch.cuda.is_available())\"\nuv run pytest tests/test_<topic>.py\n\n# Generate CLI docs\nuv run python docs/generate_cli_docs.py\n\n# Build docs (canonical, release-aligned)\n./docs/build_all.sh\n# Do NOT use `jupyter-book build docs/en|docs/zh` directly for final preview/release,\n# because it skips AReaL-specific static setup and output packaging.\n```\n\n## Boundaries\n\n### Constraints\n\n- Designed for distributed GPU clusters; assume containerized execution\n- Integration tests require multi-node hardware; explain skips when unavailable\n- Secrets and endpoints are managed outside the repo\n\n### Always Do\n\n- Read relevant files before modifying code\n- Run `pre-commit run --all-files` before committing\n- Follow existing code patterns in the same module\n- Add tests for new functionality\n\n### Ask First\n\n- Modifying config structures in `areal/api/cli_args.py`\n- Adding new dependencies\n- Changing launcher or scheduler logic\n- Deleting or renaming public APIs\n- Running GPU/distributed tests (check GPU first:\n  `python -c \"import torch; print('GPU available:', torch.cuda.is_available())\"`)\n\n### Never Do\n\n- Hardcode secrets, paths, or endpoints\n- Skip pre-commit hooks\n- Guess cluster configs or rebuild CUDA/driver stacks\n- Use wildcard imports (`from x import *`)\n\n## Progressive Disclosure: Detailed Guides\n\n| Task                   | Reference                                                     |\n| ---------------------- | ------------------------------------------------------------- |\n| Add Workflow           | `docs/customization/agent.md`, `areal/workflow/multi_turn.py` |\n| Add Dataset            | `docs/customization/`, `areal/dataset/gsm8k.py`               |\n| Add Reward             | `areal/api/reward_api.py`, `areal/reward/geometry3k.py`       |\n| Add Archon Model       | `areal/experimental/models/archon/qwen2/`, `qwen3/`           |\n| Algorithm Details      | `docs/algorithms/*.md`                                        |\n| R3 Router Replay       | `r3/README.md`                                                |\n| Quickstart             | `docs/tutorial/quickstart.md`                                 |\n| Architecture Deep Dive | `docs/tutorial/gsm8k_grpo.md`                                 |\n| CLI Reference          | `docs/cli_reference.md`                                       |\n\n## Git Workflow\n\n- **Commits**: Conventional Commits (e.g., `feat:`, `fix:`, `docs:`, `gov:`), ~72 chars\n  subject, imperative voice, reasoning in body\n- **Squash**: Squash WIP commits before opening PR\n- **PR requirements**: Run pre-commit, document test coverage, note hardware limitations\n\n## Extended Configuration\n\nSee `.claude/agents/`, `.claude/skills/`, `.claude/commands/`, and `.claude/rules/` for\nspecialized instructions.\n\n### Agents\n\n| Agent                       | Purpose                                   | Activation Trigger                                                  |\n| --------------------------- | ----------------------------------------- | ------------------------------------------------------------------- |\n| `planner`                   | Implementation planning                   | Before multi-file changes, new features, or architectural decisions |\n| `simple-code-reviewer`      | Quick code quality checks                 | After code changes, before committing                               |\n| `code-verifier`             | Formatting/linting/tests                  | After code changes, before committing                               |\n| `fsdp-engine-expert`        | FSDPEngine implementation                 | FSDPEngine code changes or questions                                |\n| `archon-engine-expert`      | ArchonEngine implementation               | ArchonEngine code changes or questions                              |\n| `megatron-engine-expert`    | MegatronEngine implementation             | MegatronEngine code changes or questions                            |\n| `algorithm-expert`          | RL algorithms                             | GRPO/PPO/DAPO questions                                             |\n| `launcher-scheduler-expert` | Cluster launching and resource scheduling | Launcher/scheduler code changes or configuration questions          |\n\n**Stage-by-Stage Agent Guidance**:\n\n1. **Planning Stage** (Before coding): Use `planner` for architecture design and\n   implementation planning\n1. **Code Formatting & Linting** (After coding): Use `code-verifier` to automatically\n   run formatting, linting, and tests, catching syntax errors and style issues quickly\n1. **Code Quality Check** (After formatting): Use `simple-code-reviewer` for quick code\n   quality checks, focusing on logic issues and code smells\n\n### Skills (Guided Development Workflows)\n\nSkills provide step-by-step guides for common development tasks:\n\n- `/add-dataset` - Dataset loader creation guide\n- `/add-workflow` - Workflow implementation guide\n- `/add-reward` - Reward function guide\n- `/add-archon-model` - Archon engine model architecture guide\n- `/debug-distributed` - Distributed debugging guide\n- `/add-unit-tests` - Test development guide (NEW)\n\n### Commands (User-invoked Actions)\n\nCommands perform specific actions when invoked:\n\n- `/create-pr` - Rebase, squash commits, and create/update PR with intelligent messages\n- `/gen-commit-msg` - Generate commit messages from staged changes\n- `/review-pr` - Intelligent PR code review with dynamic agent allocation\n- `/translate-doc-zh` - Translate English documentation to Chinese\n\n### Rules (Code Quality Standards)\n\nProject-wide standards enforced across all code changes:\n\n- `api-config.md` - Configuration dataclass design patterns\n- `code-style.md` - Coding conventions beyond pre-commit hooks\n- `distributed.md` - Distributed training patterns and constraints\n- `testing.md` - Testing strategy and coverage requirements\n","isInternal":false,"tokens":1692,"sizeBytes":7847},{"name":"SKILL.md","path":".agents/skills/add-archon-model/SKILL.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.agents/skills/add-archon-model/SKILL.md","title":"add-archon-model","category":"anthropic-skill","format":"markdown","content":"---\nname: add-archon-model\ndescription: Guide for adding a new model to the Archon engine. Use when user wants to add support for a new HuggingFace model architecture in ArchonEngine.\n---\n\n# Add Archon Model\n\nAdd support for a new HuggingFace model architecture in the Archon training engine.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a model to Archon?\"\n- User wants to support a new model family (e.g., Llama, Mistral, DeepSeek) in\n  ArchonEngine\n- User mentions adding a new `ModelSpec` or model type for Archon\n\n## Prerequisites\n\nBefore starting, ensure:\n\n- The target model is available on HuggingFace (has `config.json` with `model_type`)\n- You know the HuggingFace model ID (e.g., `meta-llama/Llama-3-8B`)\n- The model uses a standard transformer architecture (decoder-only)\n\n## Step-by-Step Guide\n\n### Step 1: Analyze the Target Model Architecture\n\nRead the HuggingFace model's source code to extract key architecture information.\n\n**Action**: Fetch and analyze the model's HuggingFace configuration and modeling files.\n\n1. Read the model's `config.json` (via `AutoConfig.from_pretrained`) to identify:\n\n   - `model_type` string (this is the key used for registry lookup)\n   - All architecture hyperparameters (hidden_size, num_layers, etc.)\n   - Any model-specific fields (e.g., `qk_norm`, `attention_bias`, MoE fields)\n\n1. Read the HuggingFace `modeling_*.py` source to identify:\n\n   - **Attention variant**: Does it have Q/K norm? Attention bias? Sliding window?\n     Multi-latent attention?\n   - **FFN variant**: SwiGLU (gate_proj + up_proj + down_proj)? GeGLU? Standard MLP?\n   - **MoE support**: Does it have MoE layers? What router type? Shared experts?\n   - **RoPE variant**: Standard RoPE? YaRN? NTK-aware scaling? What is the inv_freq\n     formula?\n   - **Normalization**: RMSNorm or LayerNorm? Pre-norm or post-norm? Elementwise affine?\n   - **Weight tying**: Does `tie_word_embeddings` appear in config?\n   - **State dict key names**: What are the HF weight key naming conventions?\n\n1. Summarize findings in a checklist like:\n\n```\nTarget model: <name>\nHF model_type: \"<model_type>\" (and variants like \"<model_type>_moe\" if applicable)\nAttention: [standard GQA / with QK norm / with bias / sliding window / ...]\nFFN: [SwiGLU / GeGLU / standard MLP / ...]\nMoE: [no / yes - num_experts, top_k, shared_experts]\nRoPE: [standard / YaRN / NTK-aware / ...]\nNorm: [RMSNorm / LayerNorm] with [pre-norm / post-norm]\nWeight tying: [yes / no]\n```\n\n### Step 2: Select the Reference Model\n\nChoose the closest existing implementation as a starting point:\n\n| Target characteristics               | Reference | Why                                     |\n| ------------------------------------ | --------- | --------------------------------------- |\n| Dense-only, standard GQA, no QK norm | `qwen2`   | Simplest baseline, pure dense           |\n| Has QK norm, or has MoE support      | `qwen3`   | Supports QK norm + MoE + shared experts |\n\n**Action**: Copy the reference model directory as the starting point:\n\n```\nareal/experimental/models/archon/<model>/\n  __init__.py\n  spec.py\n  model/\n    args.py\n    model.py\n    rope.py\n    state_dict_adapter.py\n  infra/\n    parallelize.py\n```\n\n### Step 3: Implement `args.py`\n\nAdapt `<Model>ModelArgs` to match the target model's HuggingFace config fields.\n\n**Key changes from reference**:\n\n1. Update the `@dataclass` fields to match the target model's hyperparameters:\n\n   - Field names should use Archon conventions (`dim`, `n_layers`, `n_heads`,\n     `n_kv_heads`, `vocab_size`, `head_dim`, `hidden_dim`, `norm_eps`, `rope_theta`,\n     etc.)\n   - Default values should match the smallest variant of the target model\n   - Add model-specific fields (e.g., `attention_bias`, `qk_norm`, `sliding_window`)\n\n1. Update `from_hf_config()` to correctly map HuggingFace config attributes:\n\n   - Use `getattr(hf_config, \"field_name\", default)` for optional fields\n   - Handle variant-specific fields (e.g., MoE fields only present in MoE variants)\n   - The method must return an instance of the model args class\n\n**Critical**: Verify every field mapping against the HF model's `config.json`. Incorrect\nmappings here cause silent errors downstream.\n\n**Base class contract** (`BaseModelArgs`):\n\n```python\n@dataclass\nclass <Model>ModelArgs(BaseModelArgs):\n    # ... model-specific fields ...\n\n    @classmethod\n    def from_hf_config(\n        cls,\n        hf_config: PretrainedConfig,\n        is_critic: bool = False,\n        **kwargs,\n    ) -> <Model>ModelArgs:\n        # Map HF config fields to Archon model args\n        ...\n```\n\n### Step 4: Implement `model.py`\n\nAdapt the model architecture to match the target model.\n\n**Key components to adapt**:\n\n1. **Normalization** (`RMSNorm` or similar):\n\n   - Check if `elementwise_affine` is configurable\n   - Check the epsilon default value\n   - If the model uses `LayerNorm`, implement accordingly\n\n1. **Attention** module:\n\n   - Q/K/V projection: Check bias presence (`nn.Linear(..., bias=True/False)`)\n   - QK norm: Add `q_norm`/`k_norm` if the model has them, remove if it doesn't\n   - GQA: `n_kv_heads` \\< `n_heads` for grouped-query attention\n   - Ulysses SP: Keep the `set_cp_group` / `_sp_enabled` pattern from the reference\n   - Output projection: Check bias presence\n\n1. **FeedForward** module:\n\n   - SwiGLU: `w2(silu(w1(x)) * w3(x))` -- most common for modern LLMs\n   - Check bias in linear layers\n   - For MoE models: `MoE` module replaces `FeedForward` on designated layers\n\n1. **TransformerBlock**: Pre-norm (most modern LLMs) vs post-norm\n\n   - MoE layer detection via `_is_moe_layer()` if applicable\n\n1. **Top-level Model** (`<Model>Model(BaseArchonModel)`):\n\n   - `tok_embeddings`, `layers` (as `ModuleDict`), `norm`, `output`/`score`\n   - `init_weights()`: Match initialization scheme from HF\n   - `init_buffers()`: RoPE cache + MoE buffers\n   - `forward()`: Must follow `BaseArchonModel` signature:\n     `(tokens, positions, cu_seqlens, max_seqlen, tree_attn_meta=None) -> Tensor`\n\n**Base class contract** (`BaseArchonModel`):\n\n```python\nclass <Model>Model(BaseArchonModel):\n    def forward(self, tokens, positions, cu_seqlens, max_seqlen, tree_attn_meta=None) -> torch.Tensor: ...\n    def init_weights(self) -> None: ...\n    def init_buffers(self, buffer_device) -> None: ...\n```\n\n### Step 5: Implement `rope.py`\n\nHandle the rotary position embedding variant.\n\n**Options**:\n\n1. **Standard RoPE** (same as qwen2/qwen3): Re-export from qwen2:\n\n   ```python\n   from areal.experimental.models.archon.qwen2.model.rope import (\n       apply_rotary_emb,\n       precompute_rope_cache,\n       repeat_kv,\n       reshape_for_broadcast,\n       rotate_half,\n   )\n   ```\n\n1. **Custom RoPE** (YaRN, NTK-aware, etc.): Implement custom `precompute_rope_cache()`\n   and `apply_rotary_emb()` functions. The key difference is usually in how `inv_freq`\n   is computed (scaling factors, interpolation, etc.).\n\n### Step 6: Implement `state_dict_adapter.py`\n\nMap between HuggingFace and Archon weight key names.\n\n**This is the most error-prone step.** The adapter must correctly handle:\n\n1. **Key name mapping** (`from_hf_map` dict):\n\n   - Embedding: `model.embed_tokens.weight` -> `tok_embeddings.weight`\n   - Attention: `model.layers.{}.self_attn.q_proj.weight` ->\n     `layers.{}.attention.wq.weight`\n   - FFN: `model.layers.{}.mlp.gate_proj.weight` -> `layers.{}.feed_forward.w1.weight`\n   - Norms: `model.layers.{}.input_layernorm.weight` ->\n     `layers.{}.attention_norm.weight`\n   - Output: `lm_head.weight` -> `output.weight`\n   - Skip keys (set to `None`): `rotary_emb.inv_freq` (computed at runtime)\n   - Model-specific keys: bias terms, QK norm weights, etc.\n\n1. **Reverse mapping** (`to_hf_map`): Auto-generated from `from_hf_map`\n\n1. **MoE expert weights** (if applicable): 3D\\<->2D conversion for expert weights. Copy\n   the MoE handling from qwen3 if the model has MoE.\n\n1. **Weight tying**: Skip `output.weight` during `to_hf()` if `tie_word_embeddings=True`\n\n**Verification approach**: After implementation, the adapter should satisfy:\n\n```python\n# Roundtrip: archon -> hf -> archon preserves all keys\nhf_sd = adapter.to_hf(archon_sd)\nroundtrip_sd = adapter.from_hf(hf_sd)\nassert set(roundtrip_sd.keys()) == set(archon_sd.keys())\n```\n\n**Base class contract** (`BaseStateDictAdapter`):\n\n```python\nclass <Model>StateDictAdapter(BaseStateDictAdapter):\n    def from_hf(self, hf_state_dict) -> dict[str, Any]: ...\n    def to_hf(self, archon_state_dict) -> dict[str, Any]: ...\n    def convert_single_to_hf(self, name, tensor) -> list[tuple[str, torch.Tensor]]: ...\n```\n\n### Step 7: Implement `parallelize.py`\n\nDefine the parallelization strategy for the model.\n\n**The parallelize function** applies parallelism in this order:\n\n1. TP (Tensor Parallelism) -- shard attention/FFN across devices\n1. EP (Expert Parallelism) -- for MoE models only\n1. CP (Context Parallelism / Ulysses SP) -- sequence parallelism\n1. AC (Activation Checkpointing) -- memory optimization\n1. torch.compile -- compilation optimization\n1. FSDP (Fully Sharded Data Parallelism) -- data parallelism\n\n**Key adaptations by model architecture**:\n\n- **Attention with QK norm**: wq/wk use `use_local_output=False` (DTensor output for\n  norm), add `SequenceParallel(sequence_dim=2)` for q_norm/k_norm\n- **Attention without QK norm**: wq/wk/wv all use `use_local_output=True`\n- **Attention with bias**: Bias terms follow the same parallel plan as their weights\n- **MoE layers**: Separate TP plan for MoE input/output, router gate, and expert\n  weights. Copy from qwen3's `apply_moe_ep_tp()` and `apply_non_moe_tp()`\n- **Dense-only models**: Simpler plan without MoE handling. Copy from qwen2\n\n**Function signature** (must match `ParallelizeFn` protocol):\n\n```python\ndef parallelize_<model>(\n    model: nn.Module,\n    parallel_dims: ArchonParallelDims,\n    param_dtype: torch.dtype = torch.bfloat16,\n    reduce_dtype: torch.dtype = torch.float32,\n    loss_parallel: bool = True,\n    cpu_offload: bool = False,\n    reshard_after_forward_policy: str = \"default\",\n    ac_config: ActivationCheckpointConfig | None = None,\n    enable_compile: bool = True,\n) -> nn.Module:\n```\n\n### Step 8: Create `spec.py` and Register\n\nAssemble the `ModelSpec` and register it.\n\n```python\nfrom areal.experimental.models.archon.model_spec import ModelSpec, register_model_spec\nfrom areal.experimental.models.archon.pipeline_parallel import pipeline_llm\nfrom areal.experimental.models.archon.<model>.infra.parallelize import parallelize_<model>\nfrom areal.experimental.models.archon.<model>.model.args import <Model>ModelArgs\nfrom areal.experimental.models.archon.<model>.model.model import <Model>Model\nfrom areal.experimental.models.archon.<model>.model.state_dict_adapter import (\n    <Model>StateDictAdapter,\n)\n\n<MODEL>_SPEC = ModelSpec(\n    name=\"<Model>\",\n    model_class=<Model>Model,\n    model_args_class=<Model>ModelArgs,\n    state_dict_adapter_class=<Model>StateDictAdapter,\n    parallelize_fn=parallelize_<model>,\n    supported_model_types=frozenset({\"<model_type>\"}),  # From HF config.json\n    pipelining_fn=pipeline_llm,\n)\n\n# Auto-register when module is imported\nregister_model_spec(<MODEL>_SPEC)\n\n__all__ = [\"<MODEL>_SPEC\"]\n```\n\n**Note**: `supported_model_types` should include all HF `model_type` strings that this\nimplementation handles (e.g., `{\"qwen3\", \"qwen3_moe\"}` for Qwen3).\n\n### Step 9: Register in `__init__.py`\n\nAdd the import to `areal/experimental/models/archon/__init__.py`:\n\n```python\nfrom areal.experimental.models.archon.<model> import spec as <model>_spec  # noqa: F401\n```\n\nThis triggers auto-registration when the module is imported.\n\n### Step 10: Verify and Test\n\nVerification should be done in stages, adapting based on available hardware and the test\npatterns in `tests/experimental/archon/`.\n\n**Before writing tests**, examine the existing test files to understand current\npatterns:\n\n```\ntests/experimental/archon/\n  conftest.py             -- Pytest configuration (version checks)\n  utils.py                -- Shared utilities (model loading, comparison)\n  test_qwen3_args.py      -- Args unit tests (CPU-only)\n  test_state_dict_adapter.py  -- State dict roundtrip tests\n  test_weight_sync.py     -- Weight completeness tests (meta device)\n  test_forward.py         -- Forward precision comparison (single GPU)\n  ...\n```\n\n**Test stages** (write tests appropriate for the model's complexity):\n\n#### Stage 1: Args Tests (CPU-only, always write these)\n\nTest `from_hf_config()` with mock HuggingFace configs:\n\n```python\n# Pattern: Create mock PretrainedConfig, verify args mapping\nfrom unittest.mock import MagicMock\n\ndef test_args_from_hf_config():\n    hf_config = MagicMock()\n    hf_config.hidden_size = 4096\n    hf_config.num_hidden_layers = 32\n    # ... set all required fields\n    args = <Model>ModelArgs.from_hf_config(hf_config)\n    assert args.dim == 4096\n    assert args.n_layers == 32\n```\n\n#### Stage 2: State Dict Adapter Tests (CPU-only)\n\nTest key mapping roundtrip:\n\n```python\ndef test_state_dict_roundtrip():\n    # Create adapter with mock config\n    adapter = <Model>StateDictAdapter(mock_config)\n    # Create fake archon state dict with expected keys\n    archon_sd = {\"tok_embeddings.weight\": torch.randn(vocab, dim), ...}\n    # Roundtrip\n    hf_sd = adapter.to_hf(archon_sd)\n    roundtrip = adapter.from_hf(hf_sd)\n    assert set(roundtrip.keys()) == set(archon_sd.keys())\n```\n\n#### Stage 3: Weight Completeness (meta device, CPU-only)\n\nVerify all model parameters have HF mappings:\n\n```python\ndef test_weight_completeness():\n    # Create model on meta device\n    with torch.device(\"meta\"):\n        model = <Model>Model(args)\n    adapter = <Model>StateDictAdapter(hf_config)\n    # Check every archon param has a HF mapping\n    for name, _ in model.named_parameters():\n        hf_pairs = adapter.convert_single_to_hf(name, torch.empty(0))\n        assert len(hf_pairs) > 0, f\"No HF mapping for {name}\"\n```\n\n#### Stage 4: Forward Precision (single GPU, if available)\n\nCompare Archon model output against HuggingFace reference:\n\n```python\n@pytest.mark.skipif(not torch.cuda.is_available(), reason=\"Requires CUDA\")\ndef test_forward_matches_hf():\n    # Load both HF and Archon models\n    # Run forward on same input\n    # Compare logits within tolerance\n```\n\n**Important**: Do NOT hardcode the test categories. Inspect the existing test files in\n`tests/experimental/archon/` and follow the same patterns, fixtures, and markers. Adapt\ntest scope to the model's specific features (e.g., add MoE-specific tests only if the\nmodel has MoE).\n\n## Reference Implementations\n\n| Model | Directory                                 | Features                                                |\n| ----- | ----------------------------------------- | ------------------------------------------------------- |\n| Qwen2 | `areal/experimental/models/archon/qwen2/` | Dense, attention bias, no QK norm                       |\n| Qwen3 | `areal/experimental/models/archon/qwen3/` | Dense + MoE, QK norm, no attention bias, shared experts |\n\n## Architecture Decision Map\n\n| Feature             | qwen2    | qwen3                      | What to check in target model                            |\n| ------------------- | -------- | -------------------------- | -------------------------------------------------------- |\n| Attention bias      | Yes      | No                         | `attention_bias` in HF config                            |\n| QK norm             | No       | Yes                        | `qk_norm` in HF config or QKNorm module in modeling file |\n| MoE                 | No       | Yes                        | `num_experts`/`num_local_experts` in HF config           |\n| Shared experts      | No       | Yes                        | `num_shared_experts` in HF config                        |\n| Decoder sparse step | No       | Yes                        | `decoder_sparse_step` in HF config                       |\n| Weight tying        | Both     | Both                       | `tie_word_embeddings` in HF config                       |\n| RoPE                | Standard | Standard (re-export qwen2) | Check inv_freq formula in HF modeling code               |\n\n## Common Mistakes\n\n- Not mapping all HF keys in `state_dict_adapter.py` (causes silent weight drops)\n- Wrong `from_hf_config()` field mapping (uses wrong HF config attribute name)\n- Forgetting to handle `None` keys in `from_hf_map` (keys to skip like\n  `rotary_emb.inv_freq`)\n- Missing MoE expert weight 3D\\<->2D conversion when model has MoE\n- Wrong TP plan for attention with/without QK norm (`use_local_output` must match)\n- Forgetting to add import line in `areal/experimental/models/archon/__init__.py`\n- Not including all `model_type` variants in `supported_model_types` frozenset\n- Using `print` instead of `areal.utils.logging.getLogger()`\n\n## File Checklist\n\nAfter completion, verify all files exist and are consistent:\n\n- [ ] `areal/experimental/models/archon/<model>/__init__.py`\n- [ ] `areal/experimental/models/archon/<model>/spec.py` -- ModelSpec + register\n- [ ] `areal/experimental/models/archon/<model>/model/args.py` -- ModelArgs +\n  from_hf_config\n- [ ] `areal/experimental/models/archon/<model>/model/model.py` -- Model + Attention +\n  FFN\n- [ ] `areal/experimental/models/archon/<model>/model/rope.py` -- RoPE (or re-export)\n- [ ] `areal/experimental/models/archon/<model>/model/state_dict_adapter.py` -- Key\n  mapping\n- [ ] `areal/experimental/models/archon/<model>/infra/parallelize.py` -- Parallel\n  strategy\n- [ ] `areal/experimental/models/archon/__init__.py` -- Import line added\n- [ ] `tests/experimental/archon/test_<model>_*.py` -- Tests\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nCanonical location: .agents/skills/add-archon-model/SKILL.md\nMirrors: .opencode/skills/add-archon-model/SKILL.md, .claude/skills/add-archon-model/SKILL.md\nInvocation: $add-archon-model (Codex) / /add-archon-model (OpenCode, Claude Code)\n\n## Purpose\n\nSemi-automated guide for adding new model architectures to the Archon training engine.\nUnlike simpler skills (add-reward, add-dataset), this skill actively guides the agent to:\n1. Analyze HuggingFace source code to extract architecture details\n2. Select the closest reference implementation (qwen2 or qwen3)\n3. Generate code skeletons adapted to the target architecture\n4. Create appropriate tests based on existing test patterns\n\n## How to Update\n\n### When New Reference Models Are Added\n1. Add to \"Reference Implementations\" table\n2. Update \"Architecture Decision Map\" with new feature columns\n3. Update Step 2 (reference selection) with new options\n\n### When Base Classes Change\n1. Update contract signatures in Steps 3, 4, 6, 7\n2. Update file checklist if new files are required\n\n### When ModelSpec Changes\n1. Update Step 8 with new ModelSpec fields\n2. Update spec.py template\n\n### When Test Patterns Change\n1. Update Step 10 with new test patterns\n2. Do NOT hardcode test categories -- keep it flexible\n\n### Important Design Decisions\n- This skill is SEMI-AUTOMATED: Claude should read HF source and generate code,\n  not just provide templates for the user to fill in manually\n- The skill references existing test files rather than hardcoding test categories,\n  ensuring it stays current as the test suite evolves\n- Reference model selection (qwen2 vs qwen3) is based on MoE and QK norm presence\n\n================================================================================\n-->\n","frontmatter":{"name":"add-archon-model","description":"Guide for adding a new model to the Archon engine. Use when user wants to add support for a new HuggingFace model architecture in ArchonEngine."},"isInternal":false,"tokens":4820,"sizeBytes":19648},{"name":"SKILL.md","path":".agents/skills/add-dataset/SKILL.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.agents/skills/add-dataset/SKILL.md","title":"add-dataset","category":"anthropic-skill","format":"markdown","content":"---\nname: add-dataset\ndescription: Guide for adding a new dataset loader to AReaL. Use when user wants to add a new dataset.\n---\n\n# Add Dataset\n\nAdd a new dataset loader to AReaL.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a dataset?\"\n- User wants to integrate a new dataset\n- User mentions creating a dataset loader\n\n## Step-by-Step Guide\n\n### Step 1: Create Dataset File\n\nCreate `areal/dataset/<name>.py`:\n\n```python\nfrom datasets import Dataset, load_dataset\n\n\ndef get_<name>_sft_dataset(\n    path: str,\n    split: str,\n    tokenizer,\n    max_length: int | None = None,\n) -> Dataset:\n    \"\"\"Load dataset for SFT training.\n\n    Args:\n        path: Path to dataset (HuggingFace hub or local path)\n        split: Dataset split (train/validation/test)\n        tokenizer: Tokenizer for processing\n        max_length: Maximum sequence length (optional)\n\n    Returns:\n        HuggingFace Dataset with processed samples\n    \"\"\"\n    dataset = load_dataset(path=path, split=split)\n\n    def process(sample):\n        # Tokenize the full sequence (prompt + response)\n        seq_token = tokenizer.encode(\n            sample[\"question\"] + sample[\"answer\"] + tokenizer.eos_token\n        )\n        prompt_token = tokenizer.encode(sample[\"question\"])\n        # Loss mask: 0 for prompt, 1 for response\n        loss_mask = [0] * len(prompt_token) + [1] * (len(seq_token) - len(prompt_token))\n        return {\"input_ids\": seq_token, \"loss_mask\": loss_mask}\n\n    dataset = dataset.map(process).remove_columns([\"question\", \"answer\"])\n\n    if max_length is not None:\n        dataset = dataset.filter(lambda x: len(x[\"input_ids\"]) <= max_length)\n\n    return dataset\n\n\ndef get_<name>_rl_dataset(\n    path: str,\n    split: str,\n    tokenizer,\n    max_length: int | None = None,\n) -> Dataset:\n    \"\"\"Load dataset for RL training.\n\n    Args:\n        path: Path to dataset\n        split: Dataset split\n        tokenizer: Tokenizer for length filtering\n        max_length: Maximum sequence length\n\n    Returns:\n        HuggingFace Dataset with prompts and answers for reward computation\n    \"\"\"\n    dataset = load_dataset(path=path, split=split)\n\n    def process(sample):\n        messages = [\n            {\n                \"role\": \"user\",\n                \"content\": sample[\"question\"],\n            }\n        ]\n        return {\"messages\": messages, \"answer\": sample[\"answer\"]}\n\n    dataset = dataset.map(process).remove_columns([\"question\"])\n\n    if max_length is not None:\n\n        def filter_length(sample):\n            content = sample[\"messages\"][0][\"content\"]\n            tokens = tokenizer.encode(content)\n            return len(tokens) <= max_length\n\n        dataset = dataset.filter(filter_length)\n\n    return dataset\n```\n\n### Step 2: Register in __init__.py\n\nUpdate `areal/dataset/__init__.py`:\n\n```python\n# Add to VALID_DATASETS\nVALID_DATASETS = [\n    # ... existing datasets\n    \"<name>\",\n]\n\n# Add to _get_custom_dataset function\ndef _get_custom_dataset(name: str, ...):\n    # ... existing code\n    elif name == \"<name>\":\n        from areal.dataset.<name> import get_<name>_sft_dataset, get_<name>_rl_dataset\n        if dataset_type == \"sft\":\n            return get_<name>_sft_dataset(path, split, max_length, tokenizer)\n        else:\n            return get_<name>_rl_dataset(path, split, max_length, tokenizer)\n```\n\n### Step 3: Add Config (Optional)\n\nIf the dataset needs special configuration, add to `areal/api/cli_args.py`:\n\n```python\n@dataclass\nclass TrainDatasetConfig:\n    # ... existing fields\n    <name>_specific_field: Optional[str] = None\n```\n\n### Step 4: Add Tests\n\nCreate `tests/test_<name>_dataset.py`:\n\n```python\nimport pytest\nfrom areal.dataset.<name> import get_<name>_sft_dataset, get_<name>_rl_dataset\n\ndef test_sft_dataset_loads(tokenizer):\n    dataset = get_<name>_sft_dataset(\"path/to/data\", split=\"train\", tokenizer=tokenizer)\n    assert len(dataset) > 0\n    assert \"input_ids\" in dataset.column_names\n    assert \"loss_mask\" in dataset.column_names\n\ndef test_rl_dataset_loads(tokenizer):\n    dataset = get_<name>_rl_dataset(\"path/to/data\", split=\"train\", tokenizer=tokenizer)\n    assert len(dataset) > 0\n    assert \"messages\" in dataset.column_names\n    assert \"answer\" in dataset.column_names\n```\n\n## Reference Implementations\n\n| Dataset    | File                               | Description              |\n| ---------- | ---------------------------------- | ------------------------ |\n| GSM8K      | `areal/dataset/gsm8k.py`           | Math word problems       |\n| Geometry3K | `areal/dataset/geometry3k.py`      | Geometry problems        |\n| CLEVR      | `areal/dataset/clevr_count_70k.py` | Visual counting          |\n| HH-RLHF    | `areal/dataset/hhrlhf.py`          | Helpfulness/Harmlessness |\n| TORL       | `areal/dataset/torl_data.py`       | Tool-use RL              |\n\n## Required Fields\n\n### SFT Dataset\n\n```python\n{\n    \"messages\": [\n        {\"role\": \"user\", \"content\": \"...\"},\n        {\"role\": \"assistant\", \"content\": \"...\"},\n    ]\n}\n```\n\n### RL Dataset\n\n```python\n{\n    \"messages\": [\n        {\"role\": \"user\", \"content\": \"...\"},\n    ],\n    \"answer\": \"ground_truth_for_reward\",\n    # Optional metadata for reward function\n}\n```\n\n## Common Mistakes\n\n- Returning `List[Dict]` instead of HuggingFace `Dataset`\n- Using Python loops instead of `dataset.map()`/`filter()`\n- Missing `\"messages\"` field for RL datasets\n- Wrong message format (should be list of dicts with `role` and `content`)\n- Not registering in `__init__.py`\n","frontmatter":{"name":"add-dataset","description":"Guide for adding a new dataset loader to AReaL. Use when user wants to add a new dataset."},"isInternal":false,"tokens":1323,"sizeBytes":5474},{"name":"SKILL.md","path":".agents/skills/add-reward/SKILL.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.agents/skills/add-reward/SKILL.md","title":"add-reward","category":"anthropic-skill","format":"markdown","content":"---\nname: add-reward\ndescription: Guide for adding a new reward function to AReaL. Use when user wants to create a reward function.\n---\n\n# Add Reward\n\nAdd a new reward function to AReaL.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a reward function?\"\n- User wants to implement custom rewards\n- User mentions reward computation\n\n## Step-by-Step Guide\n\n### Step 1: Create Reward File\n\nCreate `areal/reward/<name>.py`:\n\n```python\nfrom typing import Any\n\nfrom areal.utils import logging\n\nlogger = logging.getLogger(\"MyReward\")\n\n\ndef <name>_reward_fn(\n    prompt: str,\n    completions: str,\n    prompt_ids,\n    completion_ids,\n    answer: str | None = None,\n    **kwargs: Any,\n) -> float:\n    \"\"\"Compute reward for a single completion.\n\n    Args:\n        prompt: Prompt string\n        completions: Completion string (model output)\n        prompt_ids: Tokenized prompt IDs\n        completion_ids: Tokenized completion IDs\n        answer: Ground truth answer from dataset (optional)\n        **kwargs: Additional data from dataset\n\n    Returns:\n        Reward value (float), typically 0.0 or 1.0\n    \"\"\"\n    try:\n        # Extract answer from completion\n        extracted = _extract_answer(completions)\n\n        # Compare with ground truth\n        if answer is not None and extracted == str(answer):\n            return 1.0\n        return 0.0\n    except Exception:\n        logger.warning(\"Exception in reward computation\", exc_info=True)\n        return 0.0\n\n\ndef _extract_answer(completion: str) -> str:\n    \"\"\"Extract the answer from a completion string.\n\n    Implement your extraction logic here.\n    \"\"\"\n    # Example: Extract content from \\boxed{}\n    import re\n\n    match = re.search(r\"\\\\boxed\\{([^}]+)\\}\", completion)\n    if match:\n        return match.group(1).strip()\n    return completion.strip()\n```\n\n### Step 2: Register in __init__.py\n\nUpdate `areal/reward/__init__.py`:\n\n```python\n# Add to VALID_REWARD_FN\nVALID_REWARD_FN = [\n    # ... existing reward functions\n    \"<name>\",\n]\n\n# Add to get_reward_fn function\ndef get_reward_fn(name: str, **kwargs):\n    # ... existing code\n    elif name == \"<name>\":\n        from areal.reward.<name> import <name>_reward_fn\n        return <name>_reward_fn\n```\n\n### Step 3: Handle Blocking Operations\n\nIf your reward function uses blocking operations (e.g., API calls, model inference), the\nworkflow will wrap it with `AsyncRewardWrapper`:\n\n```python\n# In your workflow\nfrom areal.reward import AsyncRewardWrapper\n\nself.reward_fn = AsyncRewardWrapper(reward_fn)\n\n# Then call it asynchronously\nrewards = await self.reward_fn(prompt, completions, **data)\n```\n\n### Step 4: Add Tests\n\nCreate `tests/test_<name>_reward.py`:\n\n```python\nimport pytest\nfrom areal.reward.<name> import <name>_reward_fn\n\ndef test_reward_correct_answer():\n    reward = <name>_reward_fn(\n        prompt=\"What is 2+2?\",\n        completions=\"The answer is \\\\boxed{4}\",\n        prompt_ids=None,\n        completion_ids=None,\n        answer=\"4\",\n    )\n    assert reward == 1.0\n\ndef test_reward_wrong_answer():\n    reward = <name>_reward_fn(\n        prompt=\"What is 2+2?\",\n        completions=\"The answer is \\\\boxed{5}\",\n        prompt_ids=None,\n        completion_ids=None,\n        answer=\"4\",\n    )\n    assert reward == 0.0\n```\n\n## Reference Implementations\n\n| Reward     | File                              | Description                  |\n| ---------- | --------------------------------- | ---------------------------- |\n| GSM8K      | `areal/reward/gsm8k.py`           | Math answer verification     |\n| Geometry3K | `areal/reward/geometry3k.py`      | Geometry answer verification |\n| CLEVR      | `areal/reward/clevr_count_70k.py` | Counting verification        |\n| MathVerify | `areal/reward/math_verify.py`     | General math verification    |\n\n## Function Signature\n\nAll reward functions must follow this signature:\n\n```python\ndef reward_fn(\n    prompt: str,               # Input prompt string\n    completions: str,          # Model completion string\n    prompt_ids,                # Tokenized prompt\n    completion_ids,            # Tokenized completion\n    **kwargs: Any,             # Additional data from dataset (e.g., answer)\n) -> float:                    # Reward value (typically 0.0 or 1.0)\n```\n\n**Note**: The reward function is called once per sample. Batching is handled by\n`AsyncRewardWrapper` in the workflow.\n\n## Key Requirements\n\n1. **Deterministic**: Same inputs should produce same outputs\n1. **Return float**: Output is a single float value per sample\n1. **No blocking in async context**: Use `AsyncRewardWrapper` if needed\n1. **Logging**: Use `areal.utils.logging`, not `print`\n1. **Handle exceptions**: Return 0.0 on error, don't raise\n\n## Common Mistakes\n\n- Returning a tensor instead of a float\n- Expecting batched inputs (reward is called per sample)\n- Non-deterministic behavior\n- Blocking operations without `AsyncRewardWrapper`\n- Raising exceptions instead of returning 0.0\n","frontmatter":{"name":"add-reward","description":"Guide for adding a new reward function to AReaL. Use when user wants to create a reward function."},"isInternal":false,"tokens":1180,"sizeBytes":4945},{"name":"SKILL.md","path":".agents/skills/add-unit-tests/SKILL.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.agents/skills/add-unit-tests/SKILL.md","title":"add-unit-tests","category":"anthropic-skill","format":"markdown","content":"---\nname: add-unit-tests\ndescription: Guide for adding unit tests to AReaL. Use when user wants to add tests for new functionality or increase test coverage.\n---\n\n# Add Unit Tests\n\nAdd unit tests to AReaL following the project's testing conventions.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add tests?\"\n- User wants to increase test coverage\n- User needs to write tests for new functionality\n- User wants to understand AReaL testing patterns\n\n## Step-by-Step Guide\n\n### Step 1: Understand Test Types\n\nAReaL has two main test categories:\n\n| Test Type             | Purpose                            | Location Pattern                   | How It Runs                                |\n| --------------------- | ---------------------------------- | ---------------------------------- | ------------------------------------------ |\n| **Unit Tests**        | Test individual functions/modules  | `tests/test_<module>_<feature>.py` | Directly via pytest                        |\n| **Distributed Tests** | Test distributed/parallel behavior | `tests/torchrun/run_*.py`          | Via torchrun (called by pytest subprocess) |\n\n**Note**: All tests are invoked via pytest. Distributed tests use `torchrun` but are\nstill called from pytest test files.\n\n### Step 2: Create Test File Structure\n\nCreate test file with naming convention: `test_<module>_<feature>.py`\n\n```python\nimport pytest\nimport torch\n\n# Import the module to test\nfrom areal.dataset.gsm8k import get_gsm8k_sft_dataset\nfrom tests.utils import get_dataset_path  # Optional test utilities\n# For mocking tokenizer: from unittest.mock import MagicMock\n```\n\n### Step 3: Write Test Functions\n\nFollow Arrange-Act-Assert pattern:\n\n```python\ndef test_function_under_condition_returns_expected():\n    \"\"\"Test that function returns expected value under condition.\"\"\"\n    # Arrange\n    input_data = 5\n    expected_output = 10\n\n    # Act\n    result = function_under_test(input_data)\n\n    # Assert\n    assert result == expected_output\n```\n\n### Step 4: Add Pytest Markers and CI Strategy\n\nUse appropriate pytest markers:\n\n| Marker                                  | When to Use                                                  |\n| --------------------------------------- | ------------------------------------------------------------ |\n| `@pytest.mark.slow`                     | Test takes > 10 seconds (excluded from CI by default)        |\n| `@pytest.mark.ci`                       | Slow test that must run in CI (use with `@pytest.mark.slow`) |\n| `@pytest.mark.asyncio`                  | Async test functions                                         |\n| `@pytest.mark.skipif(cond, reason=...)` | Conditional skip                                             |\n| `@pytest.mark.parametrize(...)`         | Parameterized tests                                          |\n\n**CI Test Strategy**:\n\n- `@pytest.mark.slow`: Excluded from CI by default (CI runs `pytest -m \"not slow\"`)\n- `@pytest.mark.slow` + `@pytest.mark.ci`: Slow but must run in CI\n- No marker: Runs in CI (fast unit tests)\n\n```python\n@pytest.mark.asyncio\nasync def test_async_function():\n    result = await async_function()\n    assert result == expected\n\n@pytest.mark.skipif(not torch.cuda.is_available(), reason=\"CUDA not available\")\ndef test_gpu_feature():\n    tensor = torch.tensor([1, 2, 3], device=\"cuda\")\n    # ... assertions\n\n@pytest.mark.parametrize(\"batch_size\", [1, 4, 16])\ndef test_with_parameters(batch_size):\n    # Parameterized test\n\n@pytest.mark.slow\ndef test_slow_function():\n    # Excluded from CI by default\n\n@pytest.mark.slow\n@pytest.mark.ci\ndef test_slow_but_required_in_ci():\n    # Slow but must run in CI\n```\n\n### Step 5: Mock Distributed Environment\n\nFor unit tests that need distributed mocks:\n\n```python\nimport torch.distributed as dist\n\ndef test_distributed_function(monkeypatch):\n    monkeypatch.setattr(dist, \"get_rank\", lambda: 0)\n    monkeypatch.setattr(dist, \"get_world_size\", lambda: 2)\n    result = distributed_function()\n    assert result == expected\n```\n\n### Step 6: Handle GPU Dependencies\n\nAlways skip gracefully when GPU unavailable:\n\n```python\nCUDA_AVAILABLE = torch.cuda.is_available()\n\n@pytest.mark.skipif(not CUDA_AVAILABLE, reason=\"CUDA not available\")\ndef test_gpu_function():\n    tensor = torch.tensor([1, 2, 3], device=\"cuda\")\n    # ... assertions\n```\n\n## Key Requirements (Based on testing.md)\n\n### Mocking Distributed\n\n- Use `torch.distributed.fake_pg` for unit tests\n- Mock `dist.get_rank()` and `dist.get_world_size()` explicitly\n- Don't mock internals of FSDP/DTensor\n\n### GPU Test Constraints\n\n- **Always skip gracefully** when GPU unavailable\n- Clean up GPU memory: `torch.cuda.empty_cache()` in fixtures\n- Use smallest possible model/batch for unit tests\n\n### Assertions\n\n- Use `torch.testing.assert_close()` for tensor comparison\n- Specify `rtol`/`atol` explicitly for numerical tests\n- Avoid bare `assert tensor.equal()` - no useful error message\n\n## Reference Implementations\n\n| Test File                        | Description                            | Key Patterns                                      |\n| -------------------------------- | -------------------------------------- | ------------------------------------------------- |\n| `tests/test_utils.py`            | Utility function tests                 | Fixtures, parametrized tests                      |\n| `tests/test_examples.py`         | Integration tests with dataset loading | Dataset path resolution, success pattern matching |\n| `tests/test_fsdp_engine_nccl.py` | Distributed tests                      | Torchrun integration                              |\n\n## Common Mistakes\n\n- **Missing test file registration**: Ensure file follows `test_*.py` naming\n- **GPU dependency without skip**: Always use `@pytest.mark.skipif` for GPU tests\n- **Incorrect tensor comparisons**: Use `torch.testing.assert_close()` not\n  `assert tensor.equal()`\n- **Memory leaks in GPU tests**: Clean up with `torch.cuda.empty_cache()`\n- **Mocking too much**: Don't mock FSDP/DTensor internals\n- **Unclear test names**: Follow `test_<what>_<condition>_<expected>` pattern\n- **No docstrings**: Add descriptive docstrings to test functions\n\n## Integration with Other Skills\n\nThis skill complements other AReaL development skills:\n\n- **After `/add-dataset`**: Add tests for new dataset loaders\n- **After `/add-workflow`**: Add tests for new workflows\n- **After `/add-reward`**: Add tests for new reward functions\n- **With expert agents**: Reference this skill when planning test implementation\n\n## Running Tests\n\n```bash\n# First check GPU availability (many tests require GPU)\npython -c \"import torch; print('GPU available:', torch.cuda.is_available())\"\n\n# Run specific test file\nuv run pytest tests/test_<name>.py\n\n# Skip slow tests (CI default)\nuv run pytest -m \"not slow\"\n\n# Run with verbose output\nuv run pytest -v\n\n# Run distributed tests (requires torchrun and multi-GPU)\n# Note: Usually invoked via pytest test files\ntorchrun --nproc_per_node=2 tests/torchrun/run_<test>.py\n```\n","frontmatter":{"name":"add-unit-tests","description":"Guide for adding unit tests to AReaL. Use when user wants to add tests for new functionality or increase test coverage."},"isInternal":false,"tokens":1503,"sizeBytes":7018},{"name":"SKILL.md","path":".agents/skills/add-workflow/SKILL.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.agents/skills/add-workflow/SKILL.md","title":"add-workflow","category":"anthropic-skill","format":"markdown","content":"---\nname: add-workflow\ndescription: Guide for adding a new RolloutWorkflow to AReaL. Use when user wants to create a new workflow.\n---\n\n# Add Workflow\n\nAdd a new RolloutWorkflow implementation to AReaL.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a workflow?\"\n- User wants to create a new RolloutWorkflow\n- User mentions implementing a custom rollout\n\n## Prerequisites\n\nBefore starting, ensure you understand:\n\n- The workflow's purpose and requirements\n- Input/output data format\n- Reward function to use\n\n## Step-by-Step Guide\n\n### Step 1: Create Workflow File\n\nCreate `areal/workflow/<name>.py`:\n\n```python\nimport uuid\nfrom typing import Any, Callable\n\nimport torch\n\nfrom areal.api.cli_args import GenerationHyperparameters\nfrom areal.api.engine_api import InferenceEngine\nfrom areal.api.io_struct import ModelRequest, ModelResponse\nfrom areal.api.reward_api import AsyncRewardWrapper\nfrom areal.api.workflow_api import RolloutWorkflow\nfrom areal.utils import logging\n\nlogger = logging.getLogger(\"MyWorkflow\")\n\n\nclass MyWorkflow(RolloutWorkflow):\n    \"\"\"Description of your workflow.\"\"\"\n\n    def __init__(\n        self,\n        gconfig: GenerationHyperparameters,\n        tokenizer,\n        reward_fn: Callable,\n    ):\n        self.gconfig = gconfig.new_with_stop_and_pad_token_ids(tokenizer)\n        self.tokenizer = tokenizer\n        self.async_reward_fn = AsyncRewardWrapper(reward_fn)\n\n    async def arun_episode(\n        self,\n        engine: InferenceEngine,\n        data: dict[str, Any],\n    ) -> dict[str, Any] | None | dict[str, InteractionWithTokenLogpReward]:\n        \"\"\"Run a single episode. MUST be async and non-blocking.\"\"\"\n\n        # 1. Prepare input_ids from data\n        input_ids = self.tokenizer.apply_chat_template(\n            data[\"messages\"],\n            tokenize=True,\n            add_generation_prompt=True,\n        )\n\n        # 2. Build ModelRequest\n        req = ModelRequest(\n            rid=uuid.uuid4().hex,\n            input_ids=list(input_ids),\n            gconfig=self.gconfig.new(n_samples=1),\n            tokenizer=self.tokenizer,\n        )\n\n        # 3. Generate completion (async)\n        resp: ModelResponse = await engine.agenerate(req)\n\n        # 4. Compute reward (async)\n        prompt_str = self.tokenizer.decode(input_ids)\n        completion_str = self.tokenizer.decode(resp.output_tokens)\n        reward = await self.async_reward_fn(\n            prompt_str,\n            completion_str,\n            resp.input_tokens,\n            resp.output_tokens,\n            **data,\n        )\n\n        # 5. Return results in expected format\n        return {\n            \"input_ids\": torch.tensor(resp.input_tokens),\n            \"output_ids\": torch.tensor(resp.output_tokens),\n            \"reward\": torch.tensor(reward),\n        }\n```\n\n### Step 2: Register in __init__.py\n\nAdd to `areal/workflow/__init__.py`:\n\n```python\nfrom areal.workflow.<name> import MyWorkflow\n\n__all__ = [\n    # ... existing exports\n    \"MyWorkflow\",\n]\n```\n\n### Step 3: Update Entry Script\n\nUpdate your training script to use the new workflow:\n\n```python\ntrainer.train(\n    workflow=\"areal.workflow.<name>.MyWorkflow\",\n    # ... other args\n)\n```\n\n### Step 4: Add Tests\n\nCreate `tests/test_<name>_workflow.py`:\n\n```python\nimport pytest\nfrom areal.workflow.<name> import MyWorkflow\n\n@pytest.mark.asyncio\nasync def test_workflow_basic():\n    # Test basic functionality\n    pass\n```\n\n## Reference Implementations\n\n| Workflow           | File                            | Description                |\n| ------------------ | ------------------------------- | -------------------------- |\n| MultiTurnWorkflow  | `areal/workflow/multi_turn.py`  | Multi-turn conversation    |\n| RLVRWorkflow       | `areal/workflow/rlvr.py`        | RL with verifiable rewards |\n| VisionRLVRWorkflow | `areal/workflow/vision_rlvr.py` | Vision + RLVR              |\n\n## Key Requirements\n\n1. **Async**: `arun_episode` must be `async def` and non-blocking\n1. **No sync I/O**: Use `aiofiles` for file operations\n1. **Wrap rewards**: Use `AsyncRewardWrapper` for reward functions\n1. **Tensor format**: Output tensors should be `[batch, seq_len, ...]`\n1. **Use helpers**: `concat_padded_tensors` for combining outputs\n\n## Common Mistakes\n\n- Using `open()` instead of `aiofiles.open()`\n- Forgetting to `await` async calls\n- Not wrapping reward function with `AsyncRewardWrapper`\n- Wrong tensor shape conventions\n","frontmatter":{"name":"add-workflow","description":"Guide for adding a new RolloutWorkflow to AReaL. Use when user wants to create a new workflow."},"isInternal":false,"tokens":1002,"sizeBytes":4416},{"name":"SKILL.md","path":".agents/skills/commit-conventions/SKILL.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.agents/skills/commit-conventions/SKILL.md","title":"commit-conventions","category":"anthropic-skill","format":"markdown","content":"---\nname: commit-conventions\ndescription: AReaL commit message conventions. MUST load on every git commit -- provides Conventional Commits format with scope inference from file paths.\n---\n\n# Commit Conventions\n\nCommit message conventions and scope inference rules for the AReaL repository.\n\n## When to Use\n\n**ALWAYS load this skill when making any git commit in AReaL.** This includes:\n\n- Direct commits (`git commit`)\n- Commits during PR creation (`$create-pr` / `/create-pr`)\n- Commits delegated to sub-agents with this skill loaded\n- Any agent workflow that produces a commit\n\n## Commit Message Format\n\n```\n<type>(<scope>): <subject>\n\n<body>\n\n[Optional sections:]\nKey changes:\n- change 1\n- change 2\n\nRefs: #123, #456\n```\n\n## Type Selection\n\n| Type       | When to Use                      |\n| ---------- | -------------------------------- |\n| `feat`     | New feature or capability        |\n| `fix`      | Bug fix                          |\n| `docs`     | Documentation only               |\n| `gov`      | Governance or maintainer changes |\n| `style`    | Formatting/style-only changes    |\n| `refactor` | Code change without feature/fix  |\n| `perf`     | Performance improvement          |\n| `test`     | Adding or fixing tests           |\n| `build`    | Build system or dependencies     |\n| `ci`       | CI pipeline or workflow changes  |\n| `chore`    | Build, deps, config changes      |\n| `revert`   | Revert a previous commit         |\n\n## Scope Inference\n\nInfer scope from the **primary** changed file paths:\n\n| File Path Pattern                                            | Scope                          |\n| ------------------------------------------------------------ | ------------------------------ |\n| `areal/workflow/`                                            | `workflow`                     |\n| `areal/engine/`                                              | `engine`                       |\n| `areal/reward/`                                              | `reward`                       |\n| `areal/dataset/`                                             | `dataset`                      |\n| `areal/api/`                                                 | `api`                          |\n| `areal/utils/`                                               | `utils`                        |\n| `areal/infra/`                                               | `infra`                        |\n| `areal/trainer/`                                             | `trainer`                      |\n| `areal/models/`                                              | `models`                       |\n| `areal/experimental/`                                        | `experimental`                 |\n| `docs/`                                                      | `docs`                         |\n| `examples/`                                                  | `examples`                     |\n| `AGENTS.md`, `.agents/`, `.claude/`, `.codex/`, `.opencode/` | `agents`                       |\n| Multiple areas                                               | Omit scope or use broader term |\n\n## Rules\n\n- **Subject**: imperative mood, ~50-72 chars, no trailing period\n- **Body**: explain \"why\" not \"what\", wrap at 72 chars\n- **Key changes**: bullet list of main modifications (for complex commits with 3+ files)\n- **Refs**: reference issues/PRs if applicable\n\n## Examples\n\n**Single file fix:**\n\n```\nfix(reward): handle empty completion in gsm8k\n\nReturn 0 reward instead of raising exception when\ncompletion string is empty after extraction.\n```\n\n**Multi-file feature:**\n\n```\nfeat(engine): add CPU offload support to ArchonEngine\n\nEnable torch_memory_saver for model offloading during\nrollout phase to reduce GPU memory pressure.\n\nKey changes:\n- Add offload/onload methods to ArchonEngine\n- Integrate with weight update flow\n- Handle ROCm compatibility\n```\n\n**Docs only:**\n\n```\ndocs: update algorithm comparison table\n\nAdd SAPO and GSPO to the algorithm family documentation\nwith configuration examples.\n```\n\n**Agent/tooling changes:**\n\n```\nchore(agents): port review-pr command to OpenCode\n\nAdd OpenCode-native commands with task() category\ndelegation instead of hardcoded model names.\n\nKey changes:\n- Create .opencode/command/ with review-pr, create-pr\n- Replace hardcoded model routing with platform-native review routing\n- Add expert subagent consultation patterns\n```\n\n**Governance/maintainer changes:**\n\n```\ngov(agents): add maintainer ownership for service modules\n\nUpdate CODEOWNERS and maintainer references to reflect\ncurrent governance responsibilities.\n\nKey changes:\n- Add maintainers for agent_service and infra ownership\n- Align governance docs with updated reviewer responsibilities\n```\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nCanonical location: .agents/skills/commit-conventions/SKILL.md\nMirrors: .opencode/skills/commit-conventions/SKILL.md, .claude/skills/commit-conventions/SKILL.md\nInvocation: Automatically loaded on every git commit (all platforms)\n\n## Purpose\n\nProvides Conventional Commits format with AReaL-specific scope inference\nfrom file paths. Unlike other skills, this one is NOT user-triggered --\nit is loaded by the system/agent on every commit operation.\n\n## How to Update\n\n### When New Modules Are Added\n1. Add the file path pattern and scope to the \"Scope Inference\" table\n2. Keep table sorted by areal/ subpackages first, then top-level dirs\n\n### When Commit Types Change\n1. Update the \"Type Selection\" table\n2. Add/update examples to illustrate the new type\n\n### When Adding Examples\n1. Each example should demonstrate a distinct commit pattern\n2. Keep examples realistic -- use actual AReaL module names\n3. Show both subject-only and subject+body+key-changes variants\n\n### Important Design Decisions\n- This skill is ALWAYS loaded (not optional) -- keep it concise to\n  minimize token overhead on every commit\n- Scope inference is path-based, not content-based -- simpler and\n  more deterministic\n- \"Multiple areas\" -> omit scope rather than invent a new one\n\n================================================================================\n-->\n","frontmatter":{"name":"commit-conventions","description":"AReaL commit message conventions. MUST load on every git commit -- provides Conventional Commits format with scope inference from file paths."},"isInternal":false,"tokens":1250,"sizeBytes":6324},{"name":"SKILL.md","path":".agents/skills/create-pr/SKILL.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.agents/skills/create-pr/SKILL.md","title":"create-pr","category":"anthropic-skill","format":"markdown","content":"---\nname: create-pr\ndescription: Rebase the current branch onto the latest base branch, squash local commits, generate a Conventional Commit message, and create or update the GitHub pull request.\n---\n\n# Create Pull Request\n\nUse this skill when the user asks to create or update a PR for the current branch.\n\n## Inputs\n\n- Optional `--draft`\n- Optional `--base <branch>` (default: `main`)\n\n## Preconditions\n\n1. Verify the current branch is not `main` or `master`.\n1. Check for uncommitted changes with `git status --short`.\n1. Ensure `gh` is available.\n1. If there are uncommitted changes, stop and ask the user whether to commit or stash\n   them first.\n\n## Workflow\n\n### Step 1: Check for an existing PR\n\n- Run `gh pr view --json number,title,url,state,isDraft`.\n- If a PR already exists, tell the user before rewriting history or force-pushing.\n\n### Step 2: Fetch and rebase\n\n```bash\ngit fetch origin <base>\ngit rebase origin/<base>\n```\n\n- If rebase conflicts occur, abort the rebase and stop.\n- Tell the user which files conflicted and ask them to resolve manually.\n\n### Step 3: Squash into one commit\n\n```bash\ngit reset --soft origin/<base>\n```\n\n- Load the `commit-conventions` skill before generating the commit message.\n- Infer `type` and `scope` from the staged diff.\n- Keep the commit subject imperative and under about 72 characters.\n\n### Step 4: Generate PR title and body\n\n- Use the squashed commit message style for the PR title.\n- Follow the repository PR template at `.github/PULL_REQUEST_TEMPLATE.md`.\n- Summarize user-facing changes, risk areas, test commands run, and skipped suites with\n  reasons.\n\n### Step 5: Push and create or update the PR\n\n- Push the branch.\n- If history was rewritten, confirm before force-pushing.\n- Create or update the PR with `gh pr create` or `gh pr edit`.\n- Respect `--draft` when requested.\n\n## Guardrails\n\n- Never create a PR from `main` or `master`.\n- Never silently force-push over an existing PR branch.\n- Never bypass `commit-conventions` for the squashed commit.\n- If `gh` authentication or remote permissions fail, stop and report the exact blocker.\n\n## Output\n\nReport:\n\n- Base branch used\n- Final commit message\n- PR title\n- PR URL, if creation or update succeeded\n- Any steps that were skipped or require user follow-up\n","frontmatter":{"name":"create-pr","description":"Rebase the current branch onto the latest base branch, squash local commits, generate a Conventional Commit message, and create or update the GitHub pull request."},"isInternal":false,"tokens":542,"sizeBytes":2275},{"name":"SKILL.md","path":".agents/skills/debug-distributed/SKILL.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.agents/skills/debug-distributed/SKILL.md","title":"debug-distributed","category":"anthropic-skill","format":"markdown","content":"---\nname: debug-distributed\ndescription: Guide for debugging distributed training issues in AReaL. Use when user encounters hangs, wrong results, OOM, or communication errors.\n---\n\n# Debug Distributed Training\n\nDebugging guide for distributed training issues in AReaL (FSDP2, TP, CP, EP).\n\n## When to Use\n\nThis skill is triggered when:\n\n- Training hangs or deadlocks\n- Results differ across ranks or are numerically wrong\n- OOM errors in distributed settings\n- NCCL/communication errors or device mesh issues\n\n## Debugging Principles\n\n### Minimal Reproduction\n\n**Always follow the minimal demo principle**: Reproduce with the least amount of code to\nnarrow down the issue faster.\n\n```python\n# Bad: Debug in full training loop\n# Good: Create minimal script\nimport torch\nimport torch.distributed as dist\n\ndist.init_process_group(\"nccl\")\nrank = dist.get_rank()\n\n# Reproduce the exact operation that fails\ntensor = torch.ones(10).cuda()\ndist.all_reduce(tensor)  # <-- Isolate the failing op\nprint(f\"Rank {rank}: {tensor}\")\n```\n\n**Reduction strategy:**\n\n1. Remove unrelated model components\n1. Use small tensor sizes\n1. Reduce world_size to minimum (e.g., 2 GPUs)\n1. Remove torch.compile if possible\n1. Disable activation checkpointing\n\n## Step-by-Step Debugging Guide\n\n### 1. Hang Debugging (Deadlocks, Synchronization)\n\n**Environment Variables for Debugging**:\n\n```bash\n# Full debug logging\nexport TORCH_DISTRIBUTED_DEBUG=DETAIL\nexport NCCL_DEBUG=INFO\nexport NCCL_DEBUG_SUBSYS=ALL\n\n# torch.compile debugging\nexport TORCH_LOGS=\"+dynamo,recompiles\"\nexport TORCHDYNAMO_VERBOSE=1\n```\n\n**Dump Call Stack with py-spy** (for hung processes):\n\n```bash\n# Find process IDs\nps aux | grep python\n\n# Dump call stack of specific rank\npy-spy dump --pid <PID>\n\n# Record flame graph for performance analysis\npy-spy record -o profile.svg --pid <PID> --duration 30\n```\n\n**Common Causes**:\n\n1. **Mismatched Collectives**: One rank calls `all_reduce`, another doesn't.\n1. **Wrong Process Group**: Using wrong group for collective.\n1. **Tensor Shape Mismatch**: Different shapes across ranks.\n\n**Debug Steps**:\n\n```python\n# Verify group membership\nmesh = parallel_dims.get_mesh(\"dp_shard_cp\")\ngroup = mesh.get_group()\nprint(f\"Rank {dist.get_rank()}: group size = {dist.get_world_size(group)}\")\n\n# Print shapes on all ranks\nprint(f\"Rank {dist.get_rank()}: tensor.shape = {tensor.shape}\")\ndist.barrier()\n```\n\n**Timeout Adjustment** (for debugging only):\n\n```python\nfrom areal.engine.core.distributed import patch_dist_group_timeout\nfrom datetime import timedelta\npatch_dist_group_timeout(timedelta(minutes=30))\n```\n\n### 2. Wrong Results (Gradient, Reduction Issues)\n\n**Check DTensor Placements**:\n\n```python\nfrom torch.distributed.tensor import DTensor\nif isinstance(param, DTensor):\n    print(f\"Param {name}: placements={param.placements}, mesh={param.device_mesh}\")\n```\n\n**Verify Gradient Reduction**:\n\n```python\nfor name, param in model.named_parameters():\n    if param.grad is not None:\n        print(f\"Rank {dist.get_rank()}: {name} grad_sum = {param.grad.sum().item()}\")\n```\n\n### 3. OOM Issues (Memory, Sharding)\n\n**Check Memory Usage**:\n\n```python\nprint(f\"Rank {dist.get_rank()}: \"\n      f\"allocated={torch.cuda.memory_allocated()/1e9:.2f}GB, \"\n      f\"reserved={torch.cuda.memory_reserved()/1e9:.2f}GB\")\n```\n\n**Check FSDP Coverage**:\n\n```python\nfor name, param in model.named_parameters():\n    is_dtensor = isinstance(param, DTensor)\n    print(f\"{name}: is_dtensor={is_dtensor}, shape={param.shape}\")\n```\n\n### 4. Communication Errors\n\n| Error                     | Cause                | Solution                           |\n| ------------------------- | -------------------- | ---------------------------------- |\n| `NCCL WARN Cuda failure`  | GPU communication    | Check NCCL version, GPU topology   |\n| `RuntimeError: Timed out` | Rank synchronization | Increase timeout, check code paths |\n| `Invalid device mesh`     | Mesh configuration   | Verify world_size = dp * tp * cp   |\n\n## Debugging Tools\n\n### Environment Variables Reference\n\n| Variable                          | Purpose                                |\n| --------------------------------- | -------------------------------------- |\n| `TORCH_DISTRIBUTED_DEBUG=DETAIL`  | Detailed distributed logging           |\n| `NCCL_DEBUG=INFO`                 | NCCL communication logging             |\n| `NCCL_DEBUG_SUBSYS=ALL`           | All NCCL subsystems                    |\n| `TORCH_LOGS=\"+dynamo,recompiles\"` | torch.compile logging                  |\n| `TORCHDYNAMO_VERBOSE=1`           | Dynamo verbose output                  |\n| `CUDA_LAUNCH_BLOCKING=1`          | Synchronous CUDA (slow, for debugging) |\n\n### py-spy for Call Stack Analysis\n\n```bash\n# Install\npip install py-spy\n\n# Dump call stack of hung process\npy-spy dump --pid <PID>\n\n# Dump all Python processes\npgrep -f python | xargs -I {} py-spy dump --pid {}\n\n# Record flame graph\npy-spy record -o profile.svg --pid <PID> --duration 30\n```\n\n### Rank-Conditional Printing\n\n```python\ndef print_all_ranks(msg):\n    for r in range(dist.get_world_size()):\n        if dist.get_rank() == r:\n            print(f\"[Rank {r}] {msg}\")\n        dist.barrier()\n```\n\n### Check Device Mesh\n\n```python\ndef debug_mesh(parallel_dims):\n    mesh = parallel_dims.world_mesh\n    for dim_name in mesh.mesh_dim_names:\n        submesh = parallel_dims.get_mesh(dim_name)\n        if submesh:\n            print(f\"Rank {dist.get_rank()}: {dim_name} size={submesh.size()}\")\n```\n\n### Validate Tensor Consistency\n\n```python\ndef check_tensor_consistency(tensor, name, group=None):\n    local_sum = tensor.sum().item()\n    tensor_sums = [None] * dist.get_world_size(group)\n    dist.all_gather_object(tensor_sums, local_sum, group=group)\n    if dist.get_rank() == 0 and len(set(tensor_sums)) > 1:\n        print(f\"WARNING: {name} inconsistent: {tensor_sums}\")\n```\n\n## Key Files Reference\n\n| Component       | File                                                          |\n| --------------- | ------------------------------------------------------------- |\n| Parallel Dims   | `areal/experimental/models/archon/parallel_dims.py`           |\n| Expert Parallel | `areal/experimental/models/archon/expert_parallel.py`         |\n| Ulysses (CP)    | `areal/experimental/models/archon/ulysses.py`                 |\n| FSDP/TP Apply   | `areal/experimental/models/archon/qwen2/infra/parallelize.py` |\n","frontmatter":{"name":"debug-distributed","description":"Guide for debugging distributed training issues in AReaL. Use when user encounters hangs, wrong results, OOM, or communication errors."},"isInternal":false,"tokens":1524,"sizeBytes":6364},{"name":"MIGRATION.md","path":".agents/skills/review-pr/MIGRATION.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.agents/skills/review-pr/MIGRATION.md","title":"Review-pr Skill","category":"anthropic-skill","format":"markdown","content":"# `/review-pr` Refactor & Update Plan (Codex / OpenCode / Claude)\n\nLast updated: 2026-03-31 Owner: AI workflow maintainers Scope:\n`.agents/skills/review-pr/*`, `.opencode/{command,data}/review-pr*`,\n`.claude/{commands,data}/review-pr*`\n\n______________________________________________________________________\n\n## 1. Goals and Non-Goals\n\n### Goals\n\n1. Rebuild `/review-pr` taxonomy and templates for better coverage of new AReaL modules.\n1. Keep template count manageable while preserving depth for high-risk PRs.\n1. Eliminate cross-platform drift through a single semantic source of truth.\n1. Reduce false positives by prioritizing path-based triggers.\n\n### Non-Goals\n\n1. Changing `/review-pr` into a file-mutating workflow (must remain read-only).\n1. Adding one-off niche templates for every micro-feature.\n1. Rewriting all platform command orchestration logic in one iteration.\n\n______________________________________________________________________\n\n## 2. Current State Summary (Problem Statement)\n\n### What works\n\n1. Existing workflow phases are clear (context -> analysis -> planning -> delegated\n   review -> summary).\n1. Platform-specific command wrappers already exist and are functional.\n1. Risk-first review behavior (CRITICAL/HIGH/MEDIUM/LOW) is already enforced.\n\n### What is broken or stale\n\n1. Domain/signal matrix lags behind repository evolution (`tree_attn`, `vllm_ext`,\n   service stack, `archon_weight_sync`).\n1. Taxonomy is overly fragmented in some areas and missing in others.\n1. Three-platform data files are near-duplicates and easy to drift.\n1. Trigger rules rely heavily on generic keywords in places where path triggers are\n   safer.\n\n______________________________________________________________________\n\n## 3. Target Information Architecture\n\nUse a **two-layer taxonomy**:\n\n- **L1 Domain (12 domains)**: template-level planning unit.\n- **L2 Signals (2-9 per domain)**: detection and checklist specialization unit.\n\nThis avoids both extremes: too many tiny templates or too coarse single-pass review.\n\n### 3.1 L1 Domains and L2 Signals\n\n1. **Distributed Runtime**\n   - L2: `archon_core`, `archon_parallel`, `process_group`, `fsdp_core`,\n     `megatron_core`, `collectives`, `mesh_dtensor`, `activation_checkpointing`,\n     `weight_sync`\n1. **Model Compute & Attention**\n   - L2: `tree_attn`, `sdpa_varlen`, `sp_cp_attention_mask`, `triton_kernel`,\n     `archon_model_family`, `archon_attention_stack`, `archon_moe_modeling`\n1. **Inference Backend & Serving**\n   - L2: `vllm_ext`, `remote_inference_backend`, `request_lifecycle`\n1. **Service Orchestration**\n   - L2: `service_routing_dataflow`, `session_consistency`\n1. **Workflow & Trainer Contract**\n   - L2: `workflow_engine_boundary`, `dataset_surface`, `async_contract`,\n     `weight_version_contract`\n1. **API & Config Compatibility**\n   - L2: `dataclass_schema`, `cli_compat`, `backward_compat`,\n     `project_dependency_config`\n1. **Numerics & Tensor Semantics**\n   - L2: `shape_dtype`, `numerical_stability`, `reward_surface`, `compile_dynamo`,\n     `mixed_precision_fp8`\n1. **Checkpoint & Recovery**\n   - L2: `dcp_consistency`, `optimizer_state`, `resume_compat`\n1. **Launcher & Infrastructure**\n   - L2: `launcher_resource_match`, `scheduler_contract`, `rpc_transport`,\n     `runtime_image_config`\n1. **Low-Risk Hygiene**\n   - L2: `tests_docs_config`, `logging_import_security`, `project_docs_metadata`\n1. **Harness & Agent Infrastructure**\n   - L2: `skill_definition`, `platform_command_data`, `agent_registry_config`\n1. **CI/CD & Release Automation**\n   - L2: `github_workflow_jobs`, `runner_provisioning`, `release_delivery`\n\n______________________________________________________________________\n\n## 4. Detection Strategy (Path-first)\n\n### 4.1 Trigger priority\n\n1. **Path trigger (primary)**\n1. **Keyword trigger (secondary, only to refine existing path hits)**\n1. **Linkage trigger (auto-add dependent checks)**\n\n### 4.2 Canonical path mapping (v1)\n\n1. `areal/models/tree_attn/**` -> Model Compute & Attention (HIGH/MEDIUM by touched\n   files)\n1. `areal/engine/vllm_ext/**` -> Inference Backend & Serving (HIGH)\n1. `areal/experimental/models/archon/**` + `areal/experimental/engine/archon_engine.py`\n   \\+ `areal/experimental/engine/archon_checkpoint.py` -> Distributed Runtime + Model\n   Compute & Attention (CRITICAL/HIGH)\n1. `areal/v2/agent_service/**` -> Service Orchestration (HIGH)\n1. `areal/v2/inference_service/**` -> Service Orchestration (HIGH)\n1. `areal/experimental/engine/archon_weight_sync.py` -> Distributed Runtime (CRITICAL)\n1. `areal/infra/rpc/**` -> Launcher & Infrastructure + Distributed Runtime (HIGH)\n1. `areal/workflow/**`, `areal/trainer/**` -> Workflow & Trainer Contract (HIGH/MEDIUM)\n1. `areal/api/**` -> API & Config Compatibility (MEDIUM)\n1. `areal/utils/{saver.py,recover.py,async_checkpoint.py}` + `*checkpoint*.py` ->\n   Checkpoint & Recovery (CRITICAL/HIGH)\n1. `areal/experimental/models/archon/activation_checkpoint.py` -> Distributed Runtime\n   (MEDIUM/HIGH)\n1. `areal/experimental/models/archon/compile.py` -> Numerics & Tensor Semantics +\n   Distributed Runtime (MEDIUM)\n1. `pyproject.toml`, `uv.lock` -> API & Config Compatibility (MEDIUM)\n1. `Dockerfile`, `.dockerignore` -> Launcher & Infrastructure (HIGH)\n1. `.agents/**`, `.claude/**`, `.opencode/**`, `.codex/**`, `AGENTS.md`, `CLAUDE.md` ->\n   Harness & Agent Infrastructure (MEDIUM/HIGH)\n1. `.github/workflows/**` -> CI/CD & Release Automation (HIGH/CRITICAL)\n1. `docs/build_all.sh`, `docs/generate_cli_docs.py`, `.github/PULL_REQUEST_TEMPLATE.md`,\n   `README.md`, `CONTRIBUTING.md` -> Low-Risk Hygiene (LOW/MEDIUM)\n\n### 4.3 Must-not-regress coverage (from current review-pr)\n\nThe migration must preserve the existing high-risk framework coverage already present\ntoday:\n\n1. `areal/experimental/models/archon/**` + `areal/experimental/engine/archon_engine.py`\n   - `areal/experimental/engine/archon_checkpoint.py` -> Distributed Runtime + Model\n     Compute & Attention\n1. `areal/engine/fsdp_utils/**` + `areal/engine/fsdp_engine.py` -> Distributed Runtime\n1. `areal/engine/megatron_engine.py` + `areal/engine/megatron_utils/**` -> Distributed\n   Runtime\n1. `areal/trainer/**` -> Workflow & Trainer Contract\n1. `areal/reward/**` -> Numerics & Tensor Semantics + Workflow & Trainer Contract\n1. `areal/dataset/**` -> Workflow & Trainer Contract + API & Config Compatibility\n\n### 4.4 Noise control rules\n\n1. No repo-wide standalone triggering for `current_platform`, `RTensor`, or `fp8`.\n1. These keywords only refine severity/checklists after domain has been path-selected.\n1. Cap task fan-out: max one primary template per domain, plus one general logic pass.\n\n______________________________________________________________________\n\n## 5. Template Strategy\n\nMaintain **12 domain templates** (one per L1), each with signal-specific checklists,\nplus **1 universal logic pass**.\n\n### Template inventory (v1)\n\n1. Distributed Runtime Review\n1. Model Compute & Attention Review\n1. Inference Backend & Serving Review\n1. Service Orchestration Review\n1. Workflow & Trainer Contract Review\n1. API & Config Compatibility Review\n1. Numerics & Tensor Semantics Review\n1. Checkpoint & Recovery Review\n1. Launcher & Infrastructure Review\n1. Low-Risk Hygiene Review\n1. Harness & Agent Infrastructure Review\n1. CI/CD & Release Automation Review\n\n### Mandatory universal pass\n\n- Always run one lightweight **General Logic & Boundary** pass for non-doc PRs.\n\n______________________________________________________________________\n\n## 6. Severity Mapping\n\n1. **CRITICAL**: distributed invariants, checkpoint correctness, core weight sync\n   safety.\n1. **HIGH**: service orchestration, inference backend runtime, trainer/workflow\n   contracts.\n1. **MEDIUM**: API compatibility, numerics/tensor semantics in bounded scope.\n1. **LOW**: docs/tests/config-only hygiene.\n\nRule: domain default severity can be escalated by L2 signal combinations (e.g.,\n`mesh_dtensor` + `weight_sync`).\n\n______________________________________________________________________\n\n## 7. Cross-Domain Linkage Rules (Auto-appended checks)\n\n1. `tree_attn` -> also append Numerics & Tensor Semantics checks.\n1. `archon_core` or `archon_parallel` -> also append Model Compute & Attention checks.\n1. `archon_model_family` or `archon_moe_modeling` -> also append Numerics & Tensor\n   Semantics checks.\n1. `reward_surface` -> also append Workflow & Trainer Contract checks.\n1. `compile_dynamo` -> also append Distributed Runtime checks.\n1. `vllm_ext` -> also append Launcher & Infrastructure checks.\n1. Service Orchestration changes -> also append Workflow & Trainer async-contract\n   checks.\n1. `archon_weight_sync` -> also append DTensor + process-group + checkpoint interaction\n   checks.\n1. RPC transport changes -> also append Distributed Runtime synchronization checks.\n1. `mixed_precision_fp8` + Distributed Runtime -> also append mesh + weight-sync\n   compatibility checks.\n1. `runtime_image_config` -> also append Inference Backend & Serving checks.\n1. `project_dependency_config` -> also append API & Config Compatibility checks.\n1. `github_workflow_jobs` or `release_delivery` -> also append Launcher & Infrastructure\n   checks.\n1. `skill_definition` or `platform_command_data` -> also append Low-Risk Hygiene checks.\n\n______________________________________________________________________\n\n## 8. Three-Platform Synchronization Model\n\n## 8.1 Source-of-truth\n\nSemantic content is authored only in:\n\n1. `.agents/skills/review-pr/references/review-pr-domains-and-signals.md`\n1. `.agents/skills/review-pr/references/review-pr-templates.md`\n\nCanonical semantic scope includes:\n\n1. taxonomy (L1/L2), path/linkage rules, and severity rules\n1. checklist bodies and change-analysis vocabulary\n\nWrapper-specific scope (non-canonical) includes:\n\n1. command syntax and frontmatter fields\n1. shell snippets, import/include syntax, and runtime-specific routing policies\n1. OpenCode-only and Claude-only execution options\n\n## 8.2 Derived outputs\n\nGenerated/derived files:\n\n1. `.opencode/data/review-pr-domains-and-signals.md`\n1. `.opencode/data/review-pr-templates.md`\n1. `.claude/data/review-pr-domains-and-signals.md`\n1. `.claude/data/review-pr-templates.md`\n\n## 8.3 Mechanical sync (definition)\n\n\"Mechanical sync\" means deterministic conversion, not manual copy/paste:\n\n1. Read canonical `.agents` files.\n1. Emit OpenCode and Claude data copies with the same generic review-depth vocabulary\n   (`comprehensive`, `targeted`, `basic`).\n1. Keep all runtime routing and platform execution choices in the wrapper command files\n   only.\n1. Preserve section order and checklist content exactly.\n\n______________________________________________________________________\n\n## 9. Implementation Plan\n\n### Phase A (Foundation)\n\n1. Replace the old review-pr taxonomy with L1/L2 domain/signal references in canonical\n   `.agents` references.\n1. Rebuild template file into 12 domain templates + universal logic pass.\n1. Add linkage rules and fan-out caps.\n\n### Phase B (Platform sync)\n\n1. Add sync script at `.agents/skills/review-pr/sync_review_pr_refs.py`.\n1. Regenerate `.opencode/data/*` and `.claude/data/*`.\n1. Add CI check: fail if derived files differ from regeneration output.\n\n### Phase C (Command layer alignment)\n\n1. Update `.opencode/command/review-pr.md` wording to reflect new domains/signals.\n1. Update `.claude/commands/review-pr.md` wording similarly.\n1. Keep orchestration differences platform-specific (task categories vs model routing).\n\n### Phase D (Validation)\n\n1. Run **classification lane** with `/review-pr --quick` against representative PRs\n   from:\n   - `tree_attn`\n   - `vllm_ext`\n   - `agent_service/inference_service`\n   - `archon_weight_sync`\n1. Measure in classification lane:\n   - expected detected domains/signals\n   - expected severity\n   - false positive rate\n   - missing high-risk findings\n1. Run **delegation lane** with full `/review-pr` (non-quick) on the same fixtures.\n1. Measure in delegation lane:\n   - false positive rate\n   - missing high-risk findings\n   - number of spawned review tasks per PR\n\n______________________________________________________________________\n\n## 10. Plan Review (Critical self-review)\n\n### Strengths\n\n1. Improves coverage for newly introduced high-risk modules.\n1. Shrinks maintenance overhead by moving to domain templates.\n1. Prevents cross-platform drift via deterministic derivation.\n\n### Risks\n\n1. Over-broad domains can dilute checklist quality.\n1. First migration may temporarily shift severity distributions.\n1. Command docs can lag behind taxonomy if sync discipline is not enforced.\n\n### Mitigations\n\n1. Keep L2 signals explicit and path-anchored.\n1. Use golden PR cases for pre/post comparison.\n1. Add CI consistency gate for derived platform data files.\n1. Require wrapper wording updates in the same migration PR as taxonomy changes.\n\n______________________________________________________________________\n\n## 11. Acceptance Criteria\n\n1. Canonical references express all 12 domains + L2 signals and linkage rules.\n1. Derived OpenCode/Claude data files regenerate with zero manual edits.\n1. No mixed old/new label vocabulary remains after regeneration.\n1. Representative PRs trigger expected domains:\n   - `tree_attn` PR -> Model Compute & Attention (+ Numerics linkage)\n   - `vllm_ext` PR -> Inference Backend & Serving (+ Launcher linkage)\n   - service-stack PR -> Service Orchestration (+ Workflow linkage)\n   - `archon_weight_sync` PR -> Distributed Runtime (CRITICAL)\n1. Legacy high-risk coverage remains intact (Archon/FSDP/Megatron/Reward/Dataset).\n1. Task fan-out remains bounded (no uncontrolled template explosion).\n\n______________________________________________________________________\n\n## 12. Out of Scope for v1 (defer)\n\n1. Fully unifying command orchestration syntax across OpenCode and Claude.\n1. Creating standalone domain types for every backend keyword.\n1. Automatically posting review comments to GitHub.\n\n______________________________________________________________________\n\n## 13. Recommended Next Action\n\nLand **Phase A + B + C** in one migration PR (taxonomy + sync tooling + wrapper\nalignment), then run **Phase D** validation immediately using fixed fixtures.\n","isInternal":false,"tokens":3277,"sizeBytes":14187},{"name":"review-pr-domains-and-signals.md","path":".agents/skills/review-pr/references/review-pr-domains-and-signals.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.agents/skills/review-pr/references/review-pr-domains-and-signals.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# PR Review: Domain & Signal Detection Reference\n\nThis file contains the canonical change-domain and signal detection tables for PR\nreview. Referenced by: `.agents/skills/review-pr/SKILL.md`\n\n______________________________________________________________________\n\n## Severity-to-Review-Depth Mapping\n\n- **CRITICAL**: use `comprehensive` review depth\n- **HIGH**: use `comprehensive` review depth\n- **MEDIUM**: use `targeted` review depth\n- **LOW**: use `basic` review depth\n\n______________________________________________________________________\n\n## L1 Domains and L2 Signals\n\n## Domain 1: Distributed Runtime (CRITICAL/HIGH)\n\n| L2 Signal                  | File Path Pattern                                                                              | Code Pattern                                                                                                                                                              |\n| -------------------------- | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `archon_core`              | `areal/experimental/engine/archon_engine.py`, `areal/experimental/engine/archon_checkpoint.py` | `ArchonEngine`, `ArchonCheckpointManager`, `archon`                                                                                                                       |\n| `archon_parallel`          | `areal/experimental/models/archon/`, `parallel_dims.py`, `parallelize.py`                      | `ArchonParallelDims`, `_build_mesh`, `apply_moe_ep_tp`, `apply_tp`, `apply_cp`, `ExpertTensorParallel`, `etp`, `parallelize_module`, `ColwiseParallel`, `RowwiseParallel` |\n| `process_group`            | `areal/engine/fsdp_utils/`, `areal/engine/megatron_utils/`, `areal/experimental/engine/`       | `new_group`, `ProcessGroup`, `dist.get_rank(`                                                                                                                             |\n| `fsdp_core`                | `areal/engine/fsdp_engine.py`, `areal/engine/fsdp_utils/`                                      | `FSDP`, `fully_shard`, `FullyShardedDataParallel`                                                                                                                         |\n| `megatron_core`            | `areal/engine/megatron_engine.py`, `areal/engine/megatron_utils/`                              | `MegatronEngine`, `pipeline`, `micro-batch`                                                                                                                               |\n| `collectives`              | `areal/engine/`, `areal/infra/rpc/`                                                            | `all_reduce`, `all_gather`, `reduce_scatter`, `all_to_all`, `broadcast`, `barrier`                                                                                        |\n| `mesh_dtensor`             | `areal/experimental/models/archon/`, `areal/engine/fsdp_utils/`                                | `DeviceMesh`, `DTensor`, `Shard(`, `Replicate(`, `distribute_tensor`                                                                                                      |\n| `activation_checkpointing` | `areal/experimental/models/archon/activation_checkpoint.py`, `areal/models/`, `areal/engine/`  | `activation_checkpoint`, `checkpoint_wrapper`, `selective_checkpoint`                                                                                                     |\n| `weight_sync`              | `areal/experimental/engine/archon_weight_sync.py`, `areal/api/engine_api.py`, `areal/engine/`  | `WeightUpdateMeta`, `set_version`, `update_weights`                                                                                                                       |\n\n## Domain 2: Model Compute & Attention (HIGH/MEDIUM)\n\n| L2 Signal                | File Path Pattern                                                                                                                                          | Code Pattern                                                                                           |\n| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |\n| `tree_attn`              | `areal/models/tree_attn/`                                                                                                                                  | `TreeAttention`, `tree_attn`, `TreeNode`, `tree`                                                       |\n| `sdpa_varlen`            | `attention/sdpa.py`, `attention/varlen.py`, `areal/models/tree_attn/`                                                                                      | `sdpa`, `flash_attn`, `varlen`, `causal_mask`                                                          |\n| `sp_cp_attention_mask`   | `areal/models/tree_attn/`, `areal/experimental/models/archon/attention/`                                                                                   | `SequenceParallel`, `context_parallel`, `mask`                                                         |\n| `triton_kernel`          | `areal/models/tree_attn/triton_kernel.py`                                                                                                                  | `triton`, `kernel`, `autotune`                                                                         |\n| `archon_model_family`    | `areal/experimental/models/archon/model_spec.py`, `areal/experimental/models/archon/qwen*/`                                                                | `ModelSpec`, `register_model_spec`, `supported_model_types`, `state_dict_adapter`, `rope`              |\n| `archon_attention_stack` | `areal/experimental/models/archon/attention/`, `areal/experimental/models/archon/ulysses.py`                                                               | `ulysses_slice_inputs`, `ulysses_gather_output`, `gather_seq_scatter_heads`, `sdpa`, `varlen`          |\n| `archon_moe_modeling`    | `areal/experimental/models/archon/moe/`, `areal/experimental/models/archon/expert_parallel.py`, `areal/experimental/models/archon/moe_weight_converter.py` | `TokenChoiceTopKRouter`, `RouterGateLinear`, `GroupedExperts`, `MoEWeightConverter`, `expert_parallel` |\n\n## Domain 3: Inference Backend & Serving (HIGH)\n\n| L2 Signal                  | File Path Pattern                                              | Code Pattern                                                     |\n| -------------------------- | -------------------------------------------------------------- | ---------------------------------------------------------------- |\n| `vllm_ext`                 | `areal/engine/vllm_ext/`                                       | `areal_vllm_server`, `vllm_worker_extension`, `pause_generation` |\n| `remote_inference_backend` | `areal/engine/vllm_remote.py`, `areal/engine/sglang_remote.py` | `vllm`, `sglang`, `OpenAI`, `request`, `response`                |\n| `request_lifecycle`        | `areal/engine/`, `areal/infra/launcher/`                       | `enqueue`, `dequeue`, `cancel`, `timeout`                        |\n\n## Domain 4: Service Orchestration (HIGH)\n\n| L2 Signal                  | File Path Pattern                                                                                                                                       | Code Pattern                                                     |\n| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |\n| `service_routing_dataflow` | `areal/v2/agent_service/gateway/`, `areal/v2/agent_service/router/`, `areal/v2/inference_service/data_proxy/`, `areal/v2/inference_service/controller/` | `route`, `gateway`, `router`, `DataProxy`, `controller`, `batch` |\n| `session_consistency`      | `areal/v2/agent_service/`, `areal/v2/inference_service/`                                                                                                | `session`, `affinity`, `history`, `state`                        |\n\n## Domain 5: Workflow & Trainer Contract (HIGH/MEDIUM)\n\n| L2 Signal                  | File Path Pattern                                                           | Code Pattern                                        |\n| -------------------------- | --------------------------------------------------------------------------- | --------------------------------------------------- |\n| `workflow_engine_boundary` | `areal/workflow/`, `areal/trainer/`, `areal/engine/`                        | `RolloutWorkflow`, `arun_episode`, `agenerate`      |\n| `dataset_surface`          | `areal/dataset/`                                                            | `DataLoader`, `IterableDataset`, `get_*_dataset`    |\n| `async_contract`           | `areal/workflow/`, `areal/v2/agent_service/`, `areal/v2/inference_service/` | `async def`, `await`, `aiofiles`, `asyncio`         |\n| `weight_version_contract`  | `areal/api/engine_api.py`, `areal/workflow/`, `areal/trainer/`              | `WeightUpdateMeta`, `set_version`, `weight version` |\n\n## Domain 6: API & Config Compatibility (MEDIUM)\n\n| L2 Signal                   | File Path Pattern                     | Code Pattern                                                                     |\n| --------------------------- | ------------------------------------- | -------------------------------------------------------------------------------- |\n| `dataclass_schema`          | `areal/api/`                          | `@dataclass`, `field(`, `__post_init__`                                          |\n| `cli_compat`                | `areal/api/cli_args.py`               | `Literal`, `help`, `default`                                                     |\n| `backward_compat`           | `areal/api/`, `areal/infra/launcher/` | `deprecated`, `compat`, `version`                                                |\n| `project_dependency_config` | `pyproject.toml`, `uv.lock`           | `requires-python`, `dependencies`, `optional-dependencies`, `build-system`, `uv` |\n\n## Domain 7: Numerics & Tensor Semantics (MEDIUM)\n\n| L2 Signal             | File Path Pattern                                                               | Code Pattern                                            |\n| --------------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------- |\n| `shape_dtype`         | `areal/engine/`, `areal/models/`, `areal/trainer/`                              | `.view(`, `.reshape(`, `dtype=`, `.contiguous(`         |\n| `numerical_stability` | `areal/engine/`, `areal/reward/`, `areal/utils/functional/`                     | `log(`, `softmax`, `eps=`, `.clamp(`, `nan`, `inf`      |\n| `reward_surface`      | `areal/reward/`                                                                 | `reward_fn`, `AsyncRewardWrapper`, `MathVerifyWorker`   |\n| `compile_dynamo`      | `areal/experimental/models/archon/compile.py`, `areal/models/`, `areal/engine/` | `torch.compile`, `_dynamo`, `mark_dynamic`, `fullgraph` |\n| `mixed_precision_fp8` | `areal/engine/megatron_utils/fp8/`, `areal/experimental/models/archon/`         | `fp8`, `bf16`, `fp16`, `mixed precision`                |\n\n## Domain 8: Checkpoint & Recovery (CRITICAL/HIGH)\n\n| L2 Signal         | File Path Pattern                                                   | Code Pattern                                    |\n| ----------------- | ------------------------------------------------------------------- | ----------------------------------------------- |\n| `dcp_consistency` | `areal/utils/async_checkpoint.py`, `areal/engine/**/checkpoint*.py` | `dcp.save`, `dcp.load`, `DistributedCheckpoint` |\n| `optimizer_state` | `areal/engine/fsdp_utils/checkpoint.py`, `areal/utils/saver.py`     | `optimizer state`, `state_dict`                 |\n| `resume_compat`   | `areal/utils/recover.py`, `areal/utils/saver.py`                    | `resume`, `load_state_dict`, `migration`        |\n\n## Domain 9: Launcher & Infrastructure (HIGH/MEDIUM)\n\n| L2 Signal                 | File Path Pattern                                            | Code Pattern                                              |\n| ------------------------- | ------------------------------------------------------------ | --------------------------------------------------------- |\n| `launcher_resource_match` | `areal/infra/launcher/`                                      | `LaunchConfig`, `RayLauncher`, `SlurmLauncher`            |\n| `scheduler_contract`      | `areal/infra/scheduler/`, `areal/scheduler/`                 | `Scheduler`, `placement`, `resource`                      |\n| `rpc_transport`           | `areal/infra/rpc/`, `areal/v2/inference_service/data_proxy/` | `RTensor`, `serialize`, `rpc`, `fetch`                    |\n| `runtime_image_config`    | `Dockerfile`, `.dockerignore`                                | `FROM`, `ARG`, `RUN`, `ENV`, `COPY`, `uv sync`, `VARIANT` |\n\n## Domain 10: Low-Risk Hygiene (LOW)\n\n| L2 Signal                 | File Path Pattern                                                                                                                                                       | Code Pattern                                                                                                 |\n| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |\n| `tests_docs_config`       | `tests/`, `docs/`, `*.md`, `*.yaml`, `*.json`, `*.toml`                                                                                                                 | -                                                                                                            |\n| `logging_import_security` | `areal/`, `examples/`                                                                                                                                                   | `getLogger`, `print(`, `import *`, `api_key`, `token`, `password`                                            |\n| `project_docs_metadata`   | `docs/build_all.sh`, `docs/generate_cli_docs.py`, `docs/en/`, `docs/zh/`, `README.md`, `CONTRIBUTING.md`, `.github/PULL_REQUEST_TEMPLATE.md`, `.github/ISSUE_TEMPLATE/` | `jupyter-book`, `generate_cli_docs`, `build_all`, `_build`, `checklist`, `template`, `contributing`, `usage` |\n\n## Domain 11: Harness & Agent Infrastructure (MEDIUM/HIGH)\n\n| L2 Signal               | File Path Pattern                                                             | Code Pattern                                                 |\n| ----------------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------ |\n| `skill_definition`      | `.agents/skills/**/SKILL.md`, `.agents/skills/**/references/`                 | `description:`, `## Workflow`, `## Reference Files`, `skill` |\n| `platform_command_data` | `.claude/commands/`, `.claude/data/`, `.opencode/command/`, `.opencode/data/` | `@.`, `/review-pr`, `/create-pr`, `data/`, `task(`           |\n| `agent_registry_config` | `.codex/config.toml`, `.codex/agents/`, `AGENTS.md`, `CLAUDE.md`              | `agents`, `skills`, `registry`, `subagent`, `config.toml`    |\n\n## Domain 12: CI/CD & Release Automation (HIGH/CRITICAL)\n\n| L2 Signal              | File Path Pattern                                                                                                          | Code Pattern                                              |\n| ---------------------- | -------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- |\n| `github_workflow_jobs` | `.github/workflows/*.yml`                                                                                                  | `jobs:`, `runs-on:`, `needs:`, `if:`, `workflow_dispatch` |\n| `runner_provisioning`  | `.github/workflows/bake-gcp-image.yml`, `.github/workflows/runner-heartbeat.yml`                                           | `gcp`, `runner`, `image`, `heartbeat`                     |\n| `release_delivery`     | `.github/workflows/build-docker-image.yml`, `.github/workflows/tag-release-image.yml`, `.github/workflows/deploy-docs.yml` | `docker`, `tag`, `release`, `pages`, `publish`            |\n\n______________________________________________________________________\n\n## Must-Not-Regress Core Coverage\n\nThe refactor must preserve these existing review surfaces:\n\n- Archon core: `areal/experimental/models/archon/`,\n  `areal/experimental/engine/archon_engine.py`\n- FSDP core: `areal/engine/fsdp_utils/`, `areal/engine/fsdp_engine.py`\n- Megatron core: `areal/engine/megatron_engine.py`, `areal/engine/megatron_utils/`\n- Reward: `areal/reward/`\n- Dataset: `areal/dataset/`\n- Trainer: `areal/trainer/`\n- Harness: `.agents/`, `.claude/`, `.opencode/`, `.codex/`\n- CI/CD and release: `.github/workflows/`, `Dockerfile`, `pyproject.toml`\n\n______________________________________________________________________\n\n## Cross-Domain Linkage Rules\n\n| Detected Signal                                | Auto-Linked Review                                  |\n| ---------------------------------------------- | --------------------------------------------------- |\n| `archon_core` or `archon_parallel`             | Model Compute & Attention checks                    |\n| `archon_model_family` or `archon_moe_modeling` | Numerics & Tensor Semantics checks                  |\n| `tree_attn`                                    | Numerics & Tensor Semantics checks                  |\n| `reward_surface`                               | Workflow & Trainer Contract checks                  |\n| `compile_dynamo`                               | Distributed Runtime checks                          |\n| `vllm_ext`                                     | Launcher & Infrastructure checks                    |\n| `service_routing_dataflow`                     | Workflow & Trainer async-contract checks            |\n| `weight_sync`                                  | DTensor/process-group/checkpoint interaction checks |\n| `rpc_transport`                                | Distributed Runtime synchronization checks          |\n| `mixed_precision_fp8` + Distributed Runtime    | mesh + weight-sync compatibility checks             |\n| `runtime_image_config`                         | Inference Backend & Serving checks                  |\n| `project_dependency_config`                    | API & Config Compatibility checks                   |\n| `github_workflow_jobs` or `release_delivery`   | Launcher & Infrastructure checks                    |\n| `skill_definition` or `platform_command_data`  | Low-Risk Hygiene checks                             |\n\n______________________________________________________________________\n\n## Risk Identification Guidance\n\n### Distributed Runtime Risks\n\n- Archon mesh construction or parallel-dims mismatch\n- EP/TP/CP application order errors in Archon parallelization\n- Activation checkpoint placement violating TP/CP/FSDP ordering assumptions\n- Archon engine lifecycle drift around distributed setup and checkpoint boundaries\n- Collective call order mismatch across ranks\n- Wrong process-group scope in rank-sensitive logic\n- Mesh dimension mismatch and invalid DTensor placement\n- Weight version drift between rollout and training workers\n\n### Model Compute & Attention Risks\n\n- Attention mask inconsistency under TP/SP/CP paths\n- Tree attention index/routing mismatch\n- Archon model-family registration or per-family wiring drift\n- Archon MoE router/expert behavior diverging from weight-conversion expectations\n- Archon Ulysses slicing/gather semantics mismatching attention layout assumptions\n- Kernel assumptions violating dtype/shape invariants\n- Sequence packing alignment errors\n\n### Service Orchestration Risks\n\n- Session affinity or history drift across gateway/router/data proxy\n- Async message handling holes and dropped tasks\n- Controller/worker lifecycle desynchronization\n\n### Inference Backend & Serving Risks\n\n- Request lifecycle inconsistencies (enqueue/cancel/timeout)\n- Worker state transitions leaving requests stranded\n- Backend extension hooks drifting from runtime expectations\n\n### Workflow & Trainer Contract Risks\n\n- Workflow-engine contract drift across async boundaries\n- Weight version handshake mismatch between rollout and train\n- Trainer lifecycle transition inconsistencies\n\n### API & Config Compatibility Risks\n\n- Breaking config/schema changes without migration path\n- Dataclass or CLI default changes altering behavior silently\n- Missing validation for newly introduced fields\n- Dependency or build-system pin changes breaking supported environments\n\n### Numerics & Tensor Semantics Risks\n\n- Silent shape/dtype mismatch under distributed paths\n- Unstable numerical operations in loss/reward logic\n- torch.compile or dynamo guard changes breaking graph assumptions\n- Mixed-precision interaction regressions\n\n### Checkpoint & Recovery Risks\n\n- Partial-rank checkpoint participation\n- Incompatible state key evolution\n- Resume path breaking optimizer/model synchronization\n\n### Launcher & Infrastructure Risks\n\n- Resource assignment mismatching parallel strategy assumptions\n- RPC transport metadata loss (shape/dtype/device)\n- Startup/shutdown ordering races across processes\n- Runtime image or build-arg drift from supported inference/training variants\n\n### Low-Risk Hygiene Risks\n\n- Docs/config drift from actual runtime behavior\n- Logging or import hygiene regressions\n- Sensitive data exposure in logs or config\n- Documentation/build scripts or project templates drifting from actual workflow\n\n### Harness & Agent Infrastructure Risks\n\n- Skill and command docs drifting from actual platform behavior\n- Cross-platform data files falling out of sync with canonical references\n- Agent registry/config changes breaking expert routing or command discovery\n\n### CI/CD & Release Automation Risks\n\n- Workflow trigger or job dependency changes skipping required validation\n- Runner provisioning drift causing flaky or non-reproducible CI\n- Release or docs deployment jobs publishing the wrong artifacts\n","isInternal":false,"tokens":4057,"sizeBytes":22950},{"name":"review-pr-templates.md","path":".agents/skills/review-pr/references/review-pr-templates.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.agents/skills/review-pr/references/review-pr-templates.md","title":"References Skill","category":"anthropic-skill","format":"markdown","content":"# PR Review: Domain Templates Reference\n\nThis file contains canonical domain templates for PR review. Referenced by:\n`.agents/skills/review-pr/SKILL.md`\n\n______________________________________________________________________\n\n## Template Selection Rules\n\n1. Select templates by detected L1 domains and L2 signals.\n1. Use at most one primary template per domain.\n1. Always include **General Logic & Boundary** for non-doc/config-only PRs.\n1. Apply cross-domain linkage checks from `review-pr-domains-and-signals.md`.\n\n______________________________________________________________________\n\n## Universal Template\n\n### General Logic & Boundary\n\n```\nApplicable: Any non-doc/config-only change\nChecklist:\n- Boundary condition correctness (empty inputs, singleton, max-size)\n- Conditional logic correctness (branch inversion, short-circuit mistakes)\n- Error-path behavior (exceptions propagated with actionable context)\n- Return-value consistency across code paths\n- No newly introduced hidden behavior changes\n```\n\n______________________________________________________________________\n\n## Domain 1 Template: Distributed Runtime Review \\[comprehensive\\]\n\n```\nApplicable signals: archon_core, archon_parallel, process_group, fsdp_core, megatron_core, collectives, mesh_dtensor, activation_checkpointing, weight_sync\nChecklist:\n- Archon engine and checkpoint lifecycle remain aligned with distributed runtime assumptions\n- FSDP and Megatron engine invariants still match process-group, sharding, and pipeline assumptions\n- Archon parallel-dims and mesh construction still match downstream placement logic\n- Process-group creation/usage/cleanup is rank-consistent\n- Collective operations are called by all required ranks in consistent order\n- DeviceMesh dimensions and DTensor placements are correct for each path\n- Activation checkpoint placement remains compatible with parallel and sharding order requirements\n- Local/global tensor conversion boundaries are explicit and correct\n- Weight version propagation and update ordering are deterministic\n- No debug-only barriers left in hot path\n```\n\n## Domain 2 Template: Model Compute & Attention Review \\[comprehensive\\]\n\n```\nApplicable signals: tree_attn, sdpa_varlen, sp_cp_attention_mask, triton_kernel, archon_model_family, archon_attention_stack, archon_moe_modeling\nChecklist:\n- Attention mask semantics preserved under TP/SP/CP\n- Archon model-family registration and per-family wiring remain internally consistent\n- Archon attention/Ulysses slicing and gather paths preserve layout assumptions\n- Archon MoE router, grouped experts, and weight-conversion interfaces remain aligned\n- Tree attention index/order invariants are maintained\n- Kernel assumptions on dtype/shape/contiguity are satisfied\n- No silent behavior change in sequence packing/unpacking\n- Tensor layouts remain compatible with downstream modules\n```\n\n## Domain 3 Template: Inference Backend & Serving Review \\[comprehensive\\]\n\n```\nApplicable signals: vllm_ext, remote_inference_backend, request_lifecycle\nChecklist:\n- Request lifecycle (enqueue, execution, cancellation, timeout) is coherent\n- Worker state transitions are safe under concurrency\n- Backend-specific extension points stay API-compatible\n- Error handling does not strand in-flight requests\n- Versioning/weight-update interactions are explicit and safe\n```\n\n## Domain 4 Template: Service Orchestration Review \\[comprehensive\\]\n\n```\nApplicable signals: service_routing_dataflow, session_consistency\nChecklist:\n- Gateway/router/data-proxy routing rules are deterministic\n- Session affinity and history consistency are preserved\n- Controller/worker coordination has no lost-update window\n- Async boundaries avoid blocking operations in critical paths\n- Failure/retry behavior does not duplicate or drop work\n```\n\n## Domain 5 Template: Workflow & Trainer Contract Review \\[comprehensive\\]\n\n```\nApplicable signals: workflow_engine_boundary, dataset_surface, async_contract, weight_version_contract\nChecklist:\n- RolloutWorkflow and Engine interfaces remain contract-compatible\n- Dataset/output structure still matches workflow and trainer consumption expectations\n- Async flow uses await consistently and avoids sync I/O in async paths\n- Weight update/version handshake is preserved end-to-end\n- Trainer lifecycle transitions are valid for all execution branches\n- Call ordering assumptions across trainer/workflow/engine are unchanged or justified\n```\n\n## Domain 6 Template: API & Config Compatibility Review \\[targeted\\]\n\n```\nApplicable signals: dataclass_schema, cli_compat, backward_compat, project_dependency_config\nChecklist:\n- Public API signature and default value changes are intentional and compatible\n- Dataclass validation remains complete and informative\n- CLI options preserve expected compatibility semantics\n- New fields include safe defaults or explicit migration handling\n- Breaking changes are documented and scoped\n- Dependency and build-system changes remain compatible with supported environments\n```\n\n## Domain 7 Template: Numerics & Tensor Semantics Review \\[targeted\\]\n\n```\nApplicable signals: shape_dtype, numerical_stability, reward_surface, compile_dynamo, mixed_precision_fp8\nChecklist:\n- Tensor shape/dtype transitions are explicit and internally consistent\n- Numerical stability is protected (log/division/softmax/clamp paths)\n- Reward-side numerical behavior remains consistent with workflow consumption expectations\n- torch.compile / dynamo assumptions still hold for dynamic shapes and distributed execution\n- Mixed-precision behavior is correct for forward + backward + reduce paths\n- In-place and view/reshape operations do not corrupt gradient flow\n- Device placement and dtype combinations remain legal across code paths\n```\n\n## Domain 8 Template: Checkpoint & Recovery Review \\[comprehensive\\]\n\n```\nApplicable signals: dcp_consistency, optimizer_state, resume_compat\nChecklist:\n- Save/load requires and enforces all-rank participation where needed\n- State dict naming/structure is stable or migration-safe\n- Optimizer state sharding/gather behavior is consistent\n- Resume path restores model + optimizer + version state coherently\n- Async checkpoint behavior preserves ordering and durability assumptions\n```\n\n## Domain 9 Template: Launcher & Infrastructure Review \\[targeted\\]\n\n```\nApplicable signals: launcher_resource_match, scheduler_contract, rpc_transport, runtime_image_config\nChecklist:\n- Resource assignment matches declared parallel strategy assumptions\n- Scheduler decisions preserve required placement/affinity constraints\n- RPC serialization/deserialization keeps shape/dtype/device semantics\n- Transport retries/timeouts do not violate idempotency expectations\n- Cross-process startup/shutdown ordering is robust\n- Runtime image and build configuration remain aligned with supported variants\n```\n\n## Domain 10 Template: Low-Risk Hygiene Review \\[basic\\]\n\n```\nApplicable signals: tests_docs_config, logging_import_security, project_docs_metadata\nChecklist:\n- Tests/docs/config edits are internally consistent and non-misleading\n- Logging follows project conventions and avoids sensitive leakage\n- No wildcard imports or obvious dependency hygiene regressions\n- No accidental secrets/keys/tokens introduced\n- Docs build scripts and project templates stay aligned with real contributor workflow\n```\n\n## Domain 11 Template: Harness & Agent Infrastructure Review \\[targeted\\]\n\n```\nApplicable signals: skill_definition, platform_command_data, agent_registry_config\nChecklist:\n- Canonical skills and derived platform data remain structurally aligned\n- Command docs still point to the correct data files and execution model\n- Agent registry/config changes preserve command discovery and expert routing\n- Cross-platform mirrors are regenerated after canonical changes\n```\n\n## Domain 12 Template: CI/CD & Release Automation Review \\[comprehensive\\]\n\n```\nApplicable signals: github_workflow_jobs, runner_provisioning, release_delivery\nChecklist:\n- Workflow triggers, job dependencies, and permissions still enforce required validation\n- Runner/image provisioning remains reproducible and compatible with job expectations\n- Release, docker, and docs deployment jobs publish the intended artifacts only\n- CI changes do not silently skip tests, formatting, or release gates\n```\n\n______________________________________________________________________\n\n## Signal-Specific Add-On Checklists\n\nUse these only when corresponding L2 signals are detected.\n\n### `tree_attn` Add-On \\[comprehensive\\]\n\n```\n- Node/edge indexing is deterministic and shape-safe\n- Tree traversal order matches attention mask semantics\n- FSDP/Megatron/Archon variant modules remain behaviorally aligned\n```\n\n### `vllm_ext` Add-On \\[comprehensive\\]\n\n```\n- Server and worker extension hooks still match upstream expectations\n- Request pause/resume/cancel semantics remain coherent\n- Integration-specific monkey-patches are scoped and guarded\n```\n\n### `archon_model_family` Add-On \\[comprehensive\\]\n\n```\n- ModelSpec registration stays unique and complete for supported model types\n- Per-family model/args/spec/state-adapter wiring remains consistent\n- Pipelining hooks and model-part boundaries stay compatible with runtime assumptions\n```\n\n### `archon_moe_modeling` Add-On \\[comprehensive\\]\n\n```\n- Router top-k, gating dtype, and expert grouping semantics remain coherent\n- GroupedExperts layout and token reordering assumptions still match expert execution\n- MoE weight conversion paths stay consistent with runtime sharding expectations\n```\n\n### `service_routing_dataflow` Add-On \\[comprehensive\\]\n\n```\n- Route selection and fallback ordering are deterministic\n- Data proxy transformations preserve payload integrity\n- Session-key partitioning logic is collision-safe\n```\n\n### `remote_inference_backend` Add-On \\[targeted\\]\n\n```\n- Remote backend request/response semantics remain consistent across supported engines\n- Backend-specific transport options do not change lifecycle expectations silently\n- Shared request payload assumptions remain compatible across remote backends\n```\n\n### `weight_sync` Add-On \\[comprehensive\\]\n\n```\n- Versioned updates are monotonic and race-safe\n- Broadcast/all-gather points are aligned with consumer expectations\n- Local caching behavior cannot serve stale weights indefinitely\n```\n\n### `activation_checkpointing` Add-On \\[targeted\\]\n\n```\n- Checkpoint wrappers are applied in a parallelism-safe order\n- Selective checkpoint policies still cover the intended modules only\n- Activation recompute paths do not break sharding or sequence-parallel assumptions\n```\n\n### `reward_surface` Add-On \\[targeted\\]\n\n```\n- AsyncRewardWrapper-facing reward interfaces remain contract-compatible\n- Reward outputs keep expected shape, dtype, and per-sample semantics\n- Workflow assumptions about reward timing and batching remain valid\n```\n\n### `compile_dynamo` Add-On \\[targeted\\]\n\n```\n- torch.compile and dynamo guards still tolerate expected dynamic-shape inputs\n- fullgraph and mark_dynamic choices remain compatible with distributed execution paths\n- Compile-specific changes do not silently alter runtime fallback behavior\n```\n\n### `rpc_transport` Add-On \\[targeted\\]\n\n```\n- RTensor conversion is reversible and metadata-complete\n- Batch fetch/request framing preserves ordering and boundaries\n- Retry logic does not replay non-idempotent actions incorrectly\n```\n\n### `runtime_image_config` Add-On \\[targeted\\]\n\n```\n- Docker base image and build args still match supported backend variants\n- Layer ordering preserves expected cache and dependency behavior\n- Image contents remain aligned with runtime assumptions documented in the repo\n```\n\n### `project_dependency_config` Add-On \\[targeted\\]\n\n```\n- Python/version constraints and extras remain internally consistent\n- Lockfile changes match the intended dependency update scope\n- Build backend/tooling changes do not break install or publish workflows\n```\n\n### `github_workflow_jobs` Add-On \\[comprehensive\\]\n\n```\n- Workflow triggers and job graph still run required validation paths\n- Required secrets/permissions are scoped correctly\n- Matrix or conditional changes do not silently skip critical jobs\n```\n\n### `project_docs_metadata` Add-On \\[basic\\]\n\n```\n- Docs build entrypoints and contributor-facing metadata remain mutually consistent\n- Public templates and contributor instructions still match the actual workflow\n- Build/preview guidance still points to the supported commands\n```\n\n### `skill_definition` / `platform_command_data` Add-On \\[targeted\\]\n\n```\n- Canonical and derived review-pr data files stay in sync after edits\n- Command/import paths remain correct after file moves or renames\n- Wrapper-specific routing stays out of canonical reference files\n```\n","isInternal":false,"tokens":2500,"sizeBytes":12722},{"name":"SKILL.md","path":".agents/skills/review-pr/SKILL.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.agents/skills/review-pr/SKILL.md","title":"review-pr","category":"anthropic-skill","format":"markdown","content":"---\nname: review-pr\ndescription: Read-only pull request review workflow with risk analysis, targeted checklists, and Codex subagent consultation.\n---\n\n# Review Pull Request\n\nUse this skill when the user asks for a PR review of the current branch or a specific\nPR.\n\n## Inputs\n\n- Optional PR number\n- Optional `--quick` to stop after the change analysis phase\n\n## Hard Rules\n\n- Stay read-only.\n- Do not edit files, commit, push, rebase, or change GitHub state.\n- Do not run build, install, or test commands that mutate the environment.\n- Use `gh` for PR metadata and git diff retrieval.\n\n## Reference Files\n\n- `references/review-pr-domains-and-signals.md`\n- `references/review-pr-templates.md`\n\n## Workflow\n\n### Phase 1: Resolve PR context\n\n1. Use `gh pr view` to fetch PR title, body, state, draft status, and changed files.\n1. If no PR exists, stop and report that clearly.\n1. If the PR is closed, stop.\n1. Record the branch name and changed file list.\n\n### Phase 2: Change analysis\n\n1. Classify changed files using `references/review-pr-domains-and-signals.md`.\n1. Determine the highest overall risk level: `CRITICAL`, `HIGH`, `MEDIUM`, or `LOW`.\n1. Build a `CHANGE_ANALYSIS_REPORT` that lists:\n   - detected domains/signals\n   - risk level\n   - affected files\n   - related frameworks\n   - likely failure modes\n\nIf `--quick` is set, return the change analysis report and stop here.\n\n### Phase 3: Review planning\n\n1. Select the smallest useful set of review passes from\n   `references/review-pr-templates.md`.\n1. Split by risk area, not by file count.\n1. Always include at least one general logic pass.\n\n### Phase 4: Expert consultation\n\nConsult the matching Codex subagents registered in `.codex/config.toml` when relevant:\n\n- `archon-expert`\n- `fsdp-expert`\n- `megatron-expert`\n- `algorithm-expert`\n- `launcher-expert`\n\nIf the Codex runtime supports parallel subagent execution, run independent review passes\nin parallel. Otherwise, execute them serially.\n\n### Phase 5: Final review\n\nProduce findings first, ordered by severity:\n\n1. `CRITICAL`\n1. `HIGH`\n1. `MEDIUM`\n1. `LOW`\n\nFor every finding, include:\n\n- file path\n- line number when available\n- why it is a bug, regression, or risk\n- concrete fix direction\n\n## What to Ignore\n\n- Pure style nits with no correctness impact\n- Issues outside the changed scope unless the PR makes them worse\n- Failures that standard linters or formatters would already catch\n- Speculative concerns with no concrete trigger in the diff\n\n## Output Shape\n\nUse this structure:\n\n```markdown\nCHANGE_ANALYSIS_REPORT:\n- detected_domains: [...]\n- detected_signals: [...]\n- risk_level: ...\n- affected_files: [...]\n- related_frameworks: [...]\n- identified_risks: [...]\n\nFindings\n1. [severity] Title — path:line\n   - Problem: ...\n   - Fix: ...\n\nOpen Questions\n- ...\n\nResidual Risk\n- ...\n```\n","frontmatter":{"name":"review-pr","description":"Read-only pull request review workflow with risk analysis, targeted checklists, and Codex subagent consultation."},"isInternal":false,"tokens":694,"sizeBytes":2816},{"name":"SKILL.md","path":".agents/skills/translate-doc-zh/SKILL.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.agents/skills/translate-doc-zh/SKILL.md","title":"translate-doc-zh","category":"anthropic-skill","format":"markdown","content":"---\nname: translate-doc-zh\ndescription: Translate an English document under `docs/en/` into the matching Chinese document under `docs/zh/`.\n---\n\n# Translate Docs EN to ZH\n\nUse this skill when the user asks to translate a document from `docs/en/` to `docs/zh/`.\n\n## Input\n\n- A markdown file path under `docs/en/`\n\n## Workflow\n\n### Step 1: Validate the source path\n\n1. Confirm the file exists.\n1. Confirm it is under `docs/en/`.\n1. Confirm it ends with `.md`.\n1. If any check fails, stop and ask the user for a valid `docs/en/...md` path.\n\n### Step 2: Resolve the target path\n\n- Source: `docs/en/<path>.md`\n- Target: `docs/zh/<path>.md`\n\n### Step 3: Choose translation mode\n\n- If the Chinese file already exists, update only the changed parts while preserving the\n  rest.\n- If the Chinese file does not exist, translate the full document.\n\n## Translation Rules\n\n- Preserve technical terms such as FSDP, FSDP2, GRPO, PPO, DAPO, MoE, LLM, RL, RLVR,\n  Codex, Claude Code, OpenCode, Megatron, Archon, SGLang, vLLM, PyTorch, HuggingFace,\n  and Transformers.\n- Do not translate file paths, code blocks, CLI flags, or literal configuration keys.\n- Preserve Markdown structure, tables, and fenced code blocks.\n- Use concise, professional Chinese terminology.\n\n## Error Handling\n\n- If the target directory does not exist, create it before writing the translated file.\n- If the source document is partially translated already, preserve sections that do not\n  need updates.\n","frontmatter":{"name":"translate-doc-zh","description":"Translate an English document under `docs/en/` into the matching Chinese document under `docs/zh/`."},"isInternal":false,"tokens":373,"sizeBytes":1461},{"name":"CHECKLIST_MAINTENANCE.md","path":".agents/skills/upgrade-deps/CHECKLIST_MAINTENANCE.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.agents/skills/upgrade-deps/CHECKLIST_MAINTENANCE.md","title":"Upgrade-deps Skill","category":"anthropic-skill","format":"markdown","content":"# Checklist Maintenance Guide\n\nThis document is the single reference for maintaining the per-package API checklists\nunder `checklists/`. It covers two complementary maintenance activities:\n\n- **Structural validation** — ensuring every AReaL import site is documented in the\n  checklist. Performed in SKILL.md → **Step 0.5** before any dependency changes.\n- **Content update** — ensuring documented API signatures and code snippets match the\n  post-upgrade state. Performed in SKILL.md → **Step 6e** after code changes are\n  applied.\n\nBoth activities share the same checklist format. This guide also covers creating new\nchecklists and writing good API catalog entries.\n\n______________________________________________________________________\n\n## 1. Checklist Structure Reference\n\nEach checklist file lives at `checklists/<package-name>.md` and follows the format\ndefined in `checklists/_TEMPLATE.md`. The key sections are:\n\n| Section                  | Purpose                                                                                                                                                                                                            |\n| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| **YAML frontmatter**     | `package`, `github` (org/repo), `branch_template`, `upstream_paths` — metadata used by the API audit (Step 6a) to clone the correct upstream tag and locate source files.                                          |\n| **Affected Files**       | Three-tier table (Primary / Secondary / Tertiary) listing every AReaL file that imports or uses the package, with a summary of what it imports.                                                                    |\n| **API Usage Catalog**    | Numbered entries documenting each distinct API surface (function, class, HTTP endpoint, schema contract). Each entry includes the upstream source path, the AReaL call-site code, and specific Check instructions. |\n| **Version-Guarded Code** | List of AReaL code locations that have version-specific conditionals for this package (e.g., `if version < \"0.4.10\": ...`).                                                                                        |\n\n______________________________________________________________________\n\n## 2. Import Patterns by Package\n\nUse these patterns to discover all AReaL usages of each focused package. Grep across\n`areal/`, `tests/`, and `examples/`.\n\n| Package           | Import path(s)    | Grep patterns                                    | Notes                                                                                 |\n| ----------------- | ----------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------- |\n| `megatron-core`   | `megatron.core`   | `from megatron.core`, `import megatron.core`     |                                                                                       |\n| `megatron-bridge` | `megatron.bridge` | `from megatron.bridge`, `import megatron.bridge` |                                                                                       |\n| `mbridge`         | `mbridge`         | `from mbridge`, `import mbridge`                 | Pinned to git commit; also check `mbridge.core.*` submodule imports                   |\n| `transformers`    | `transformers`    | `from transformers`, `import transformers`       | Exclude `flash_attn.bert_padding` false positives                                     |\n| `sglang`          | `sglang`          | `from sglang`, `import sglang`                   | Also scan `sglang_remote.py` for HTTP endpoint paths and JSON field names             |\n| `vllm`            | `vllm`            | `from vllm`, `import vllm`                       | Also scan `vllm_remote.py` and `vllm_ext/` for HTTP endpoints and JSON fields         |\n| `peft`            | `peft`            | `from peft`, `import peft`                       | Also check for indirect PEFT schema dicts (`\"r\"`, `\"lora_alpha\"`, `\"target_modules\"`) |\n| `torchao`         | `torchao`         | `from torchao`, `import torchao`                 | All usage goes through `prototype.*` path                                             |\n\n### Edge cases to watch for\n\n- **HTTP-only integrations** (sglang, vllm): The primary integration is REST-based, not\n  Python imports. Grep for endpoint path strings (e.g., `\"/generate\"`,\n  `\"/update_weights_from_distributed\"`) and JSON field names in request/response parsing\n  code.\n- **Indirect / schema-only usage**: Some files reference a package's conventions without\n  importing it directly (e.g., PEFT config dicts in `areal/api/io_struct.py`). These\n  should be listed in Affected Files and may warrant an API catalog entry if the schema\n  contract is non-trivial.\n- **Conditional / lazy imports**: Imports inside `if TYPE_CHECKING:` blocks or inside\n  function bodies. These are still real dependencies — include them.\n- **String references**: Config enums or registry keys that name a package (e.g.,\n  `\"sglang\"` in `SGLangConfig`). These rarely need catalog entries but should appear in\n  Affected Files if the file also has substantive usage.\n- **Test stubs**: Files like `tests/experimental/archon/conftest.py` that conditionally\n  stub out a package. List in Tertiary Affected Files.\n\n______________________________________________________________________\n\n## 3. Structural Validation Procedure\n\n> **Referenced by:** SKILL.md → Step 0.5\n\nThis procedure checks whether a checklist documents ALL current AReaL import sites and\ncall patterns. Run it before upgrading a package to ensure the API audit (Step 6) has\ncomplete coverage.\n\n### 3.1 Discover all usages\n\nFor each focused package being validated:\n\n1. Grep the AReaL codebase using the patterns from § 2. Search across `areal/`,\n   `tests/`, and `examples/`.\n1. For HTTP-based packages, also scan the relevant remote engine files for endpoint\n   paths and JSON field names.\n1. Exclude false positives: comments, docstrings, unrelated substrings (e.g.,\n   `flash_attn.bert_padding` is not a `transformers` import).\n1. Collect results as `{file_path, imported_names[]}` for each hit.\n\n### 3.2 Compare against Affected Files tables\n\nParse the checklist's Primary / Secondary / Tertiary tables and compare against the\ndiscovered files. Build three lists:\n\n- **MISSING** — files found in the codebase that are not listed in any tier.\n- **STALE** — files listed in the checklist that no longer import the package (file\n  deleted, import removed, or moved to a different file).\n- **CHANGED** — files listed in the checklist whose actual imports differ from what the\n  \"Imports / Usage\" column documents.\n\n### 3.3 Classify new files into tiers\n\nUse these rules (consistent with existing checklists):\n\n| Tier          | Directories                                                                                                                                                               | Rationale                                                                |\n| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |\n| **Primary**   | `areal/engine/`, `areal/experimental/engine/`, `areal/experimental/models/`                                                                                               | Engine layer — most likely to break on API changes, highest blast radius |\n| **Secondary** | `areal/models/`, `areal/workflow/`, `areal/infra/`, `areal/api/`, `areal/utils/`, `areal/trainer/`, `areal/dataset/`, `areal/reward/`, `areal/experimental/` (non-engine) | Model / infra / workflow layer — important but lower blast radius        |\n| **Tertiary**  | `tests/`, `examples/`, `docs/`, tool scripts                                                                                                                              | Tests and examples — breakage is contained, lower priority               |\n\nWhen a file spans concerns (e.g., a test that exercises engine internals), classify by\nthe file's location, not its content.\n\n### 3.4 Determine new API catalog entries\n\nFor each MISSING or CHANGED file, examine the specific imports and call sites:\n\n- If the usage pattern is **already covered** by an existing catalog entry (same\n  function/class, same call pattern) → just add the file to the Affected Files table. No\n  new catalog entry needed.\n- If the usage involves a function/class **NOT in the catalog**, or uses it with\n  **significantly different parameters** → create a new API catalog entry (see § 6 for\n  format).\n\n\"Significantly different\" means: different keyword arguments, different return value\nconsumption, or a different code path that could break independently.\n\n### 3.5 Update the checklist\n\n1. Add missing files to the appropriate tier table with their imports summary.\n1. Remove stale file entries. If uncertain whether a removal is correct, add a\n   `<!-- TODO: verify removal -->` comment instead of deleting.\n1. Update the \"Imports / Usage\" column for CHANGED files.\n1. Add new API catalog entries following the format in § 6.\n1. Renumber catalog entries sequentially (1, 2, 3, ...).\n1. Update the entry count in SKILL.md's **Checklist File Status** table at the bottom.\n\n### 3.6 Report format\n\nAfter completing validation, report changes before proceeding:\n\n```text\nChecklist validation for <package>:\n- Added N files to Affected Files (P primary, S secondary, T tertiary)\n- Added M new API catalog entries: [brief list]\n- Removed K stale file entries: [brief list]\n- Updated J existing entries with changed imports\n- No changes needed (if clean)\n```\n\n______________________________________________________________________\n\n## 4. Content Update Procedure\n\n> **Referenced by:** SKILL.md → Step 6e\n\nThis procedure updates a checklist's API signatures and code snippets AFTER an upgrade\nhas been applied and code changes (Step 6d) are complete. The goal is to keep the\nchecklist accurate for the next upgrade cycle.\n\n### 4.1 Update API signatures\n\nFor each catalog entry where the upstream API signature changed at the new version:\n\n1. Update the **Source** path if the upstream file was moved or renamed.\n1. Note any new parameters, removed parameters, or type changes in the **Check**\n   instructions — these become the baseline for the next upgrade.\n1. If a function was removed and replaced, update the entry to reference the\n   replacement.\n\n### 4.2 Update call-site code snippets\n\nIf Step 6d modified an AReaL call site to accommodate the new version:\n\n1. Replace the code snippet in the corresponding catalog entry with the updated code.\n1. Update the line number references if they shifted.\n1. Ensure the snippet includes enough context to understand the parameters used and the\n   return value consumed.\n\n### 4.3 Update version-guarded code\n\n- **Remove** entries for version guards that were cleaned up (the guarded version is now\n  below the minimum supported version).\n- **Add** entries for any new version guards introduced during the upgrade.\n- **Update** threshold versions in existing entries.\n\n### 4.4 Update frontmatter\n\n- Add or remove entries in `upstream_paths` if upstream source files were moved,\n  renamed, or deleted.\n- Update `branch_template` if the upstream project changed its tagging convention (e.g.,\n  from `v${VERSION}` to `release/${VERSION}`).\n\n### 4.5 Update SKILL.md status table\n\nUpdate the entry count and description in the **Checklist File Status** table at the\nbottom of SKILL.md to reflect the current state of the checklist.\n\n______________________________________________________________________\n\n## 5. Creating a New Checklist\n\nWhen a new package becomes \"focused\" (added to SKILL.md's Focused Packages table):\n\n1. **Copy the template.** Copy `checklists/_TEMPLATE.md` to\n   `checklists/<package-name>.md`.\n\n1. **Fill in YAML frontmatter:**\n\n   - `package`: the pip package name (e.g., `sglang`)\n   - `github`: the GitHub org/repo (e.g., `sgl-project/sglang`)\n   - `branch_template`: how to construct the git tag from a version string (e.g.,\n     `v${VERSION}`)\n   - `upstream_paths`: list of source paths in the upstream repo that are most relevant\n     to AReaL's usage\n\n1. **Run structural validation.** Follow the full procedure in § 3 to populate the\n   Affected Files tables and API Usage Catalog.\n\n1. **Update SKILL.md.** Add the package to:\n\n   - The **Focused Packages** table (Architecture section)\n   - The **Package Impact Matrix** (with correct scope and pyproject locations)\n   - The **Checklist File Status** table (bottom of file)\n\n1. **Commit** the new checklist alongside the SKILL.md update.\n\n______________________________________________________________________\n\n## 6. API Catalog Entry Format\n\nEach entry documents one distinct API surface — a function, class, HTTP endpoint, or\nschema contract. Use this format:\n\n````markdown\n### N. `fully.qualified.FunctionOrClass`\n\n**Source:** `upstream-repo/path/to/file.py`\n\nCalled in `areal/path/to/file.py` (description, lines N–M):\n\n```python\n# Paste the ACTUAL call site code — enough context to understand\n# the parameters used and the return value consumed.\nactual_call(param1=..., param2=...)\n```\n\n**Check:** [What to verify — be specific.]\n````\n\n### Guidelines\n\n- **One entry per distinct API surface.** A function, class, HTTP endpoint, or schema\n  contract gets one entry. If the same function is called in multiple files with the\n  same pattern, list all call sites in a single entry.\n- **Multiple call sites.** When the same API is used in several files, list the primary\n  call site with a full code snippet, then note additional call sites with a brief\n  reference: \"Also called in `areal/path/to/other.py`.\"\n- **HTTP endpoints.** Document the endpoint path, HTTP method, request payload fields,\n  and response fields that AReaL parses. These are API surfaces even though they are not\n  Python imports.\n- **Indirect / schema usage.** If AReaL constructs a dict that must conform to a\n  package's schema (e.g., PEFT config dicts), document the schema contract and note that\n  it is indirect.\n- **Check instructions.** Be specific and actionable:\n  - Good: \"Confirm `bias` still accepts the string `\"none\"` (not renamed to an enum).\"\n  - Bad: \"Check if anything changed.\"\n- **Code snippets.** Include enough surrounding code to show parameter names, types, and\n  how the return value is consumed. Strip unrelated code. Include line numbers for\n  reference.\n\n______________________________________________________________________\n\n## 7. When NOT to Add a Catalog Entry\n\nNot every import needs its own API catalog entry. Skip a dedicated entry when:\n\n- **Type-only imports.** A class imported solely for type annotations or `isinstance`\n  checks (e.g., `from transformers import PretrainedConfig` used only as\n  `config: PretrainedConfig`). List the file in Affected Files but skip the catalog\n  unless the type annotation relies on specific attributes that could change.\n- **Re-exports / pass-throughs.** A file that re-exports a symbol without calling it.\n- **Standard patterns unlikely to break.** Accessing `__version__`, basic enum values,\n  or other stable public API that has never changed across versions.\n- **Test files duplicating Primary/Secondary coverage.** If a test file uses the exact\n  same API pattern already documented in a Primary or Secondary entry, list it in\n  Tertiary Affected Files but skip a separate catalog entry. Exception: if the test\n  exercises a unique code path or parameter combination, add an entry.\n\nWhen in doubt, **add the entry**. A slightly verbose checklist is better than a gap that\ncauses a missed breaking change.\n","isInternal":false,"tokens":3327,"sizeBytes":16050},{"name":"_TEMPLATE.md","path":".agents/skills/upgrade-deps/checklists/_TEMPLATE.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.agents/skills/upgrade-deps/checklists/_TEMPLATE.md","title":"Checklists Skill","category":"anthropic-skill","format":"markdown","content":"<!-- Template for per-package API checklists. Copy this file and fill in for each package. -->\n\n______________________________________________________________________\n\npackage: <pip-package-name> github: \\<org/repo> branch_template: \"v${VERSION}\"\nupstream_paths:\n\n- path/to/relevant/file.py\n- path/to/relevant/module/\n\n______________________________________________________________________\n\n## Affected Files\n\n### Primary (engine layer — most likely to break)\n\n| File                    | Imports / Usage                   |\n| ----------------------- | --------------------------------- |\n| `areal/path/to/file.py` | `module.function`, `module.Class` |\n\n### Secondary (model / infra layer)\n\n| File                    | Imports / Usage   |\n| ----------------------- | ----------------- |\n| `areal/path/to/file.py` | `module.function` |\n\n### Tertiary (tests, config)\n\n| File                 | Imports / Usage   |\n| -------------------- | ----------------- |\n| `tests/test_file.py` | `module.function` |\n\n______________________________________________________________________\n\n## API Usage Catalog\n\nFor each function/class below, verify the call signature against the upstream source at\nthe target version. Focus on: **missing new required parameters**, **removed old\nparameters**, **renamed parameters**, **changed return types**, **changed method\nsignatures on returned objects**, and **moved/renamed modules**.\n\n### 1. `module.submodule.FunctionOrClass`\n\n**Source:** `upstream-repo/path/to/file.py`\n\nCalled in `areal/path/to/file.py`:\n\n```python\n# Paste the actual call site code here\nFunctionOrClass(param1=..., param2=...)\n```\n\n**Check:** \\[Describe what to verify — new params? renamed? return type change?\\]\n\n### 2. ...\n\n______________________________________________________________________\n\n## Version-Guarded Code\n\n<!-- List any AReaL code that has version-specific behavior for this package -->\n\n- `areal/path/to/file.py:LINE` — description of version guard and threshold\n","isInternal":false,"tokens":397,"sizeBytes":1986},{"name":"mbridge.md","path":".agents/skills/upgrade-deps/checklists/mbridge.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.agents/skills/upgrade-deps/checklists/mbridge.md","title":"Checklists Skill","category":"anthropic-skill","format":"markdown","content":"---\npackage: mbridge\ngithub: ISEEKYAN/mbridge\nbranch_template: ${VERSION}\nupstream_paths:\n  - mbridge/__init__.py\n  - mbridge/core/__init__.py\n  - mbridge/core/auto_bridge.py\n  - mbridge/core/bridge.py\n  - mbridge/core/llm_bridge.py\n  - mbridge/core/util.py\n---\n\n## Affected Files\n\n### Primary (engine layer — most likely to break)\n\n| File                                        | Imports / Usage                                                                                                                                                                                 |\n| ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `areal/engine/megatron_engine.py`           | `import mbridge` → `mbridge.AutoBridge.from_pretrained`, `.dtype`, `.set_extra_args`                                                                                                            |\n| `areal/models/mcore/hf_load.py`             | `from mbridge.core.bridge import Bridge` → `bridge._get_actual_hf_path`, `._weight_name_mapping_*`, `.hf_config`, `.dtype`, `.config`                                                           |\n| `areal/models/mcore/hf_save.py`             | `from mbridge.core import Bridge`, `from mbridge.core.util import unwrap_model` → `bridge._weight_name_mapping_*`, `._weight_merge_across_tp`, `._weight_to_hf_format`, `.hf_config`, `.config` |\n| `areal/models/mcore/bailing_moe_bridge.py`  | `from mbridge.core import LLMBridge, register_model` → subclasses `LLMBridge`, uses `@register_model` decorator                                                                                 |\n| `areal/models/tree_attn/module_megatron.py` | `from mbridge.core import LLMBridge` → monkey-patches `LLMBridge._get_transformer_layer_spec`                                                                                                   |\n\n### Secondary (model / infra layer)\n\n| File                             | Imports / Usage                                                                          |\n| -------------------------------- | ---------------------------------------------------------------------------------------- |\n| `areal/models/mcore/registry.py` | `from mbridge.core.bridge import Bridge` → `bridge.hf_config`, `.config`, `.get_model()` |\n\n### Tertiary (tests, config)\n\n| File                                          | Imports / Usage                                         |\n| --------------------------------------------- | ------------------------------------------------------- |\n| `tests/test_estimate_num_params.py`           | `import mbridge` → `mbridge.AutoBridge.from_pretrained` |\n| `areal/tools/validation_base.py`              | `\"mbridge\"` in `CRITICAL_PACKAGES` (metadata only)      |\n| `areal/tools/validate_docker_installation.py` | `\"mbridge\"` in package validation list (metadata only)  |\n\n______________________________________________________________________\n\n## API Usage Catalog\n\nFor each function/class below, verify the call signature against the upstream source at\nthe target version. Focus on: **missing new required parameters**, **removed old\nparameters**, **renamed parameters**, **changed return types**, **changed method\nsignatures on returned objects**, and **moved/renamed modules**.\n\n### 1. `mbridge.AutoBridge.from_pretrained`\n\n**Source:** `mbridge/core/auto_bridge.py`\n\nCalled in `areal/engine/megatron_engine.py` (line 507):\n\n```python\nself.bridge = mbridge.AutoBridge.from_pretrained(\n    self.config.path, trust_remote_code=True\n)\n```\n\nAlso called in `tests/test_estimate_num_params.py` (line 43):\n\n```python\nbridge = mbridge.AutoBridge.from_pretrained(model_name_or_path)\n```\n\n**Check:** Confirm `trust_remote_code` is still accepted as a keyword argument. Verify\nthe first positional arg is still the model path (str). Verify the return type is still\na `Bridge` (or subclass like `LLMBridge`) instance with `.hf_config`, `.config`, and\n`.dtype` attributes.\n\n______________________________________________________________________\n\n### 2. `Bridge.dtype` (property/attribute)\n\n**Source:** `mbridge/core/bridge.py`\n\nSet in `areal/engine/megatron_engine.py` (line 510):\n\n```python\nself.bridge.dtype = self.dtype\n```\n\n**Check:** Confirm `dtype` is still a settable attribute (not read-only property).\nVerify it accepts `torch.dtype` values. This controls the precision used for weight\nloading and conversion throughout the bridge.\n\n______________________________________________________________________\n\n### 3. `Bridge.set_extra_args`\n\n**Source:** `mbridge/core/bridge.py` or `mbridge/core/llm_bridge.py`\n\nCalled in `areal/engine/megatron_engine.py` (lines 512–518):\n\n```python\nself.bridge.set_extra_args(\n    recompute_granularity=self.mcore_config.recompute_granularity,\n    recompute_method=self.mcore_config.recompute_method,\n    recompute_num_layers=self.mcore_config.recompute_num_layers,\n    distribute_saved_activations=self.mcore_config.distribute_saved_activations,\n    recompute_modules=self.mcore_config.recompute_modules,\n)\n```\n\n**Check:** Confirm all five keyword arguments are still accepted:\n`recompute_granularity`, `recompute_method`, `recompute_num_layers`,\n`distribute_saved_activations`, `recompute_modules`. Check if new required arguments\nwere added.\n\n______________________________________________________________________\n\n### 4. `Bridge.get_model`\n\n**Source:** `mbridge/core/bridge.py` or `mbridge/core/llm_bridge.py`\n\nCalled in `areal/models/mcore/registry.py` (lines 178–188):\n\n```python\nmodels = bridge.get_model(\n    wrap_with_ddp=mcore_config.wrap_with_ddp,\n    ddp_config=dataclasses.asdict(mcore_config.ddp),\n    use_torch_fsdp2=mcore_config.use_torch_fsdp2,\n    use_custom_fsdp=mcore_config.use_custom_fsdp,\n    fp16=tf_config.fp16,\n    bf16=tf_config.bf16,\n    use_precision_aware_optimizer=mcore_config.use_precision_aware_optimizer,\n    overlap_param_gather_with_optimizer_step=mcore_config.overlap_param_gather_with_optimizer_step,\n)\n```\n\n**Check:** Confirm all keyword arguments are still accepted: `wrap_with_ddp`,\n`ddp_config`, `use_torch_fsdp2`, `use_custom_fsdp`, `fp16`, `bf16`,\n`use_precision_aware_optimizer`, `overlap_param_gather_with_optimizer_step`. Verify the\nreturn type is still an iterable of model instances (GPTModel or DDP-wrapped). Check for\nnewly added required parameters.\n\n______________________________________________________________________\n\n### 5. `Bridge._get_actual_hf_path`\n\n**Source:** `mbridge/core/bridge.py`\n\nCalled in `areal/models/mcore/hf_load.py` (line 539):\n\n```python\nweights_path = bridge._get_actual_hf_path(weights_path)\n```\n\n**Check:** Confirm this private method still exists and accepts a single string argument\n(path). Verify it returns a string (resolved path to the actual HF weights directory).\nSince this is a private API, check if it was renamed or replaced.\n\n______________________________________________________________________\n\n### 6. `Bridge._weight_name_mapping_mcore_local_to_global`\n\n**Source:** `mbridge/core/bridge.py` or `mbridge/core/llm_bridge.py`\n\nCalled in `areal/models/mcore/hf_load.py` (line 570):\n\n```python\nlocal_to_global_map = bridge._weight_name_mapping_mcore_local_to_global(model)\n```\n\nAlso called in `areal/models/mcore/hf_save.py` (line 243):\n\n```python\nbridge._weight_name_mapping_mcore_local_to_global(model, consider_ep=False)\n```\n\n**Check:** Confirm the method still accepts a model as the first positional argument.\nVerify `consider_ep` keyword argument is still accepted (used in hf_save.py). Verify the\nreturn type is still `dict[str, str]` mapping local parameter names to global names.\nCheck if the method was renamed or its signature changed.\n\n______________________________________________________________________\n\n### 7. `Bridge._weight_name_mapping_mcore_to_hf`\n\n**Source:** `mbridge/core/bridge.py` or `mbridge/core/llm_bridge.py`\n\nCalled in `areal/models/mcore/hf_load.py` (line 573):\n\n```python\nlocal_to_hf_map = {\n    k: bridge._weight_name_mapping_mcore_to_hf(v)\n    for k, v in local_to_global_map.items()\n    if \"_extra_state\" not in k\n}\n```\n\n**Check:** Confirm the method accepts a single string argument (mcore global weight\nname). Verify the return type is still `list[str]` (list of HF weight names). This is\nalso overridden in `BailingMoeBridge` — check that the base class signature is stable.\n\n______________________________________________________________________\n\n### 8. `Bridge._weight_merge_across_tp`\n\n**Source:** `mbridge/core/bridge.py` or `mbridge/core/llm_bridge.py`\n\nCalled in `areal/models/mcore/hf_save.py` (lines 414–415):\n\n```python\ninfer_params = bridge._weight_merge_across_tp(\n    s.global_name, infer_params, param\n)\n```\n\n**Check:** Confirm the method accepts three positional arguments: `mcore_weights_name`\n(str), `tp_shards` (list of tensors), `param` (tensor). Verify the return type is still\na `torch.Tensor`. This is overridden in `BailingMoeBridge` — check base class stability.\n\n______________________________________________________________________\n\n### 9. `Bridge._weight_to_hf_format`\n\n**Source:** `mbridge/core/bridge.py` or `mbridge/core/llm_bridge.py`\n\nCalled in `areal/models/mcore/hf_save.py` (lines 422–423):\n\n```python\nconverted_names, converted_params = bridge._weight_to_hf_format(\n    s.global_name, infer_params\n)\n```\n\n**Check:** Confirm the method accepts two positional arguments: `mcore_weights_name`\n(str) and `mcore_weights` (tensor). Verify the return type is still\n`tuple[list[str], list[torch.Tensor]]`. This is overridden in `BailingMoeBridge` — check\nbase class stability.\n\n______________________________________________________________________\n\n### 10. `mbridge.core.util.unwrap_model`\n\n**Source:** `mbridge/core/util.py`\n\nCalled in `areal/models/mcore/hf_save.py` (line 237):\n\n```python\nmodels = [unwrap_model(model) for model in models]\n```\n\n**Check:** Confirm the function still accepts a single model argument. Verify it returns\nthe unwrapped model (strips DDP/FSDP wrappers). Check if the function was moved to a\ndifferent module.\n\n______________________________________________________________________\n\n### 11. `LLMBridge` (subclassing contract)\n\n**Source:** `mbridge/core/llm_bridge.py`\n\nSubclassed in `areal/models/mcore/bailing_moe_bridge.py` (line 87):\n\n```python\nclass BailingMoeBridge(LLMBridge):\n    TransformerConfigClass = MLATransformerConfig\n\n    def _build_config(self): ...\n    def _get_gptmodel_args(self) -> dict: ...\n    def _get_transformer_layer_spec(self, vp_stage: int | None = None): ...\n    def _weight_name_mapping_mcore_to_hf(self, mcore_weights_name: str) -> list[str]: ...\n    def _weight_merge_across_tp(self, mcore_weights_name, tp_shards, param): ...\n    def _weight_to_mcore_format(self, mcore_weights_name, hf_weights): ...\n    def _weight_to_hf_format(self, mcore_weights_name, mcore_weights): ...\n```\n\n**Check:** Confirm `LLMBridge` still exposes these overridable methods with compatible\nsignatures. Verify `TransformerConfigClass` class attribute is still supported. Check if\nany abstract methods were added that subclasses must now implement. Verify\n`_build_base_config(...)` still accepts the keyword arguments used in\n`BailingMoeBridge._build_config()` (especially MoE and MLA parameters). Check if\n`has_vp_stage` attribute is still read by the base class.\n\n______________________________________________________________________\n\n### 12. `register_model` decorator\n\n**Source:** `mbridge/core/__init__.py` (re-exported from `mbridge/core/llm_bridge.py` or\nsimilar)\n\nUsed in `areal/models/mcore/bailing_moe_bridge.py` (lines 84–86):\n\n```python\n@register_model(\"bailing_moe_v2\")\n@register_model(\"bailing_moe_linear\")\n@register_model(\"bailing_hybrid\")\nclass BailingMoeBridge(LLMBridge):\n    ...\n```\n\n**Check:** Confirm `register_model` is still importable from `mbridge.core`. Verify it\nstill accepts a single string argument (model type name). Verify it still works as a\nclass decorator that registers the bridge class for `AutoBridge.from_pretrained`\ndispatch. Check if the registration mechanism changed (e.g., from decorator to explicit\nregistry call).\n\n______________________________________________________________________\n\n### 13. `LLMBridge._get_transformer_layer_spec` (monkey-patch target)\n\n**Source:** `mbridge/core/llm_bridge.py`\n\nMonkey-patched in `areal/models/tree_attn/module_megatron.py` (lines 195–213):\n\n```python\noriginal_layer_spec_getter = LLMBridge._get_transformer_layer_spec\n\ndef _patched_getter(self, vp_stage: int | None = None):\n    spec: TransformerBlockSubmodules = original_layer_spec_getter(self, vp_stage)\n    for layer_spec in spec.layer_specs:\n        ...\n    return spec\n\nLLMBridge._get_transformer_layer_spec = _patched_getter\n```\n\n**Check:** Confirm `_get_transformer_layer_spec` still exists on `LLMBridge` with\nsignature `(self, vp_stage: int | None = None)`. Verify the return type is still\n`TransformerBlockSubmodules` with a `.layer_specs` attribute (list of layer specs). Each\nlayer spec must still have `.module`, `.submodules`, and\n`.submodules.self_attention.submodules.core_attention` that can be reassigned. If the\nmethod was renamed or its return structure changed, the monkey-patch will silently fail.\n\n______________________________________________________________________\n\n### 14. `Bridge.hf_config` and `Bridge.config` (properties)\n\n**Source:** `mbridge/core/bridge.py`\n\nAccessed throughout:\n\n- `bridge.hf_config` → HuggingFace `PretrainedConfig` instance (used in hf_load.py,\n  hf_save.py, registry.py, bailing_moe_bridge.py)\n- `bridge.config` → Megatron `TransformerConfig` instance (used in hf_save.py,\n  registry.py)\n\n```python\n# hf_load.py\nbridge.hf_config  # .num_key_value_heads, .hidden_size, .num_attention_heads, .quantization_config\nbridge.config     # .fp8, .fp8_param\nbridge.dtype      # torch.dtype\n\n# registry.py\nbridge.hf_config  # ._name_or_path\nbridge.config     # returned as TransformerConfig\n```\n\n**Check:** Confirm `hf_config` still returns a HuggingFace `PretrainedConfig` (or\nsubclass). Confirm `config` still returns a Megatron `TransformerConfig` with at least\n`.fp8`, `.fp8_param`, `.num_moe_experts` attributes. These are the most widely accessed\nproperties — any type change will cascade across all Primary files.\n\n______________________________________________________________________\n\n## Version-Guarded Code\n\n_None currently._ The `mbridge` package is pinned to a git commit (`310e8fb`) rather\nthan a versioned release, so no version-conditional code exists.\n","frontmatter":{"package":"mbridge","github":"ISEEKYAN/mbridge","branch_template":"${VERSION}","upstream_paths":["mbridge/__init__.py","mbridge/core/__init__.py","mbridge/core/auto_bridge.py","mbridge/core/bridge.py","mbridge/core/llm_bridge.py","mbridge/core/util.py"]},"isInternal":false,"tokens":3242,"sizeBytes":14667},{"name":"megatron-bridge.md","path":".agents/skills/upgrade-deps/checklists/megatron-bridge.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.agents/skills/upgrade-deps/checklists/megatron-bridge.md","title":"Checklists Skill","category":"anthropic-skill","format":"markdown","content":"---\npackage: megatron-bridge\ngithub: NVIDIA-NeMo/Megatron-Bridge\nbranch_template: v${VERSION}\nupstream_paths:\n  - megatron/bridge/__init__.py\n  - megatron/bridge/models/conversion/auto_bridge.py\n  - megatron/bridge/models/conversion/param_mapping.py\n  - megatron/bridge/models/qwen_vl/modelling_qwen3_vl/text_model.py\n  - megatron/bridge/peft/lora.py\n---\n\n## Affected Files\n\n### Primary (engine layer — most likely to break)\n\n| File                                           | Imports / Usage                                                |\n| ---------------------------------------------- | -------------------------------------------------------------- |\n| `areal/engine/megatron_engine.py`              | `megatron.bridge.AutoBridge`, `megatron.bridge.peft.lora.LoRA` |\n| `areal/engine/megatron_utils/megatron_lora.py` | `megatron.bridge.AutoBridge` (inside function, monkey-patched) |\n\n### Secondary (model / infra layer)\n\n| File                                        | Imports / Usage                                                   |\n| ------------------------------------------- | ----------------------------------------------------------------- |\n| `areal/models/mcore/vocab_parallel_head.py` | `AutoMapping.register_module_type` for dynamic LM-head subclasses |\n\n### Tertiary (tests, config)\n\n| File                                          | Imports / Usage                                                                   |\n| --------------------------------------------- | --------------------------------------------------------------------------------- |\n| `areal/tools/validation_base.py`              | `\"megatron-bridge\"` → `\"megatron.bridge\"` in `PACKAGE_IMPORT_MAP` (metadata only) |\n| `areal/tools/validate_docker_installation.py` | Megatron Bridge package validation metadata                                       |\n| `tests/test_bailing_v3_nccl.py`               | Conditional Bridge availability and real conversion integration                   |\n| `tests/test_megatron_bridge_deterministic.py` | Bridge provider determinism integration                                           |\n| `tests/test_megatron_engine.py`               | Megatron engine integration through Bridge-backed paths                           |\n| `tests/test_megatron_transport.py`            | Conditional import of Bridge-backed Megatron engine                               |\n| `tests/test_megatron_lm_head.py`              | `AutoMapping` registry assertion for promoted LM-head subclasses                  |\n\n______________________________________________________________________\n\n## API Usage Catalog\n\nFor each function/class below, verify the call signature against the upstream source at\nthe target version. Focus on: **missing new required parameters**, **removed old\nparameters**, **renamed parameters**, **changed return types**, **changed method\nsignatures on returned objects**, and **moved/renamed modules**.\n\n### 1. `megatron.bridge.AutoBridge.from_hf_pretrained`\n\n**Source:** `megatron/bridge/models/conversion/auto_bridge.py`\n\nCalled in `areal/engine/megatron_engine.py` (line 430):\n\n```python\nself.bridge = MegatronBridgeAutoBridge.from_hf_pretrained(\n    self.config.path,\n    trust_remote_code=True,\n    dtype=self.config.dtype,\n)\n```\n\n**Check:** Confirm `trust_remote_code` and `dtype` are still accepted keyword arguments.\nVerify the first positional arg is still the model path. Verify the method still returns\na bridge object that exposes `save_hf_pretrained`, `load_hf_weights`, and (depending on\nversion) `save_hf_adapter`. Check for any new required parameters.\n\n______________________________________________________________________\n\n### 2. `megatron.bridge.AutoBridge.save_hf_pretrained`\n\n**Source:** `megatron/bridge/models/conversion/auto_bridge.py`\n\nCalled in `areal/engine/megatron_engine.py` (line 1561):\n\n```python\nbridge.save_hf_pretrained(model, path, source_path=base_model_path)\n```\n\n**Check:** Confirm `source_path` is still a valid keyword argument. Verify positional\norder of `model` and `path` hasn't changed. Check return type (currently void/`None`).\n\n______________________________________________________________________\n\n### 3. `megatron.bridge.AutoBridge.load_hf_weights`\n\n**Source:** `megatron/bridge/models/conversion/auto_bridge.py`\n\nCalled in `areal/engine/megatron_engine.py` (line 1595):\n\n```python\nbridge.load_hf_weights(model, hf_path=path)\n```\n\n**Check:** Confirm `hf_path` is still the correct keyword name. Verify `model` is still\nthe first positional argument. Check for newly added required arguments.\n\n______________________________________________________________________\n\n### 4. `megatron.bridge.AutoBridge.save_hf_adapter`\n\n**Source:** `megatron/bridge/models/conversion/auto_bridge.py`\n\nCalled in `areal/engine/megatron_engine.py` (lines 1554-1559) via the monkey-patched\nmethod on the bridge instance:\n\n```python\nself.bridge.save_hf_adapter(\n    self.model,\n    path=path,\n    peft_config=self.bridge_lora,\n    base_model_name_or_path=base_model_path or self.config.path,\n)\n```\n\n**Check:** If the new version adds this method natively, confirm its signature matches\nthe monkey-patch in `areal/engine/megatron_utils/megatron_lora.py` (line 189). The\nmonkey-patched signature is:\n\n```python\ndef save_hf_adapter(\n    self, model, path, peft_config, base_model_name_or_path=None, show_progress=True\n)\n```\n\nAny mismatch in parameter names or order will silently break adapter saving. Note that\n`peft_config` receives a `megatron.bridge.peft.lora.LoRA` instance (not a dict). See\nalso the [Version-Guarded Code](#version-guarded-code) section.\n\n______________________________________________________________________\n\n### 5. `megatron.bridge.AutoBridge.export_adapter_weights`\n\n**Source:** `megatron/bridge/models/conversion/auto_bridge.py`\n\nCalled inside the monkey-patched `save_hf_adapter` in\n`areal/engine/megatron_utils/megatron_lora.py` (lines 237-240):\n\n```python\nfor name, tensor in self.export_adapter_weights(\n    model, cpu=True, show_progress=show_progress\n):\n    adapter_state[f\"base_model.model.{name}\"] = tensor.clone().float()\n```\n\n**Check:** Confirm `cpu` and `show_progress` are still accepted. Verify the method\nyields `(name, tensor)` tuples (iterable of pairs). The names are module FQNs without\nthe `base_model.model.` prefix — that prefix is added by the caller. This is a native\nbridge method used inside the monkey-patch — if its return type or signature changes,\nthe patch breaks.\n\n______________________________________________________________________\n\n### 6. `megatron.bridge.peft.lora.LoRA`\n\n**Source:** `megatron/bridge/peft/lora.py`\n\nCalled in `areal/engine/megatron_engine.py` (line 233):\n\n```python\nbridge_lora = MegatronBridgeLoRA(\n    target_modules=target_modules,\n    dim=lora_rank,\n    alpha=lora_alpha,\n    dropout=0.0,\n)\n```\n\n**Check:** Confirm `dim` is still the rank parameter (not renamed to `r` or `rank`).\nVerify `alpha` and `dropout` are still accepted. Check the `target_modules` accepted\ntype (list of strings vs. regex).\n\n______________________________________________________________________\n\n### 7. `LoRA.__call__` (apply to model)\n\n**Source:** `megatron/bridge/peft/lora.py`\n\nCalled in `areal/engine/megatron_engine.py` (lines 239-240):\n\n```python\nself.model = _MegatronModelList(self.bridge_lora(self.model, training=True))\nself.bridge_lora.set_params_to_save(self.model)\n```\n\n**Check:** Confirm `LoRA` instances are still callable with `(model, training=...)`.\nVerify the return type — it returns a modified model (or list of model chunks), which is\nthen wrapped in `_MegatronModelList`. Confirm `set_params_to_save(model)` still exists\nand marks LoRA parameters for checkpoint saving. Check if `training=True` is still the\ncorrect keyword to enable grad on LoRA parameters.\n\n______________________________________________________________________\n\n### 8. Qwen3-VL model forward contract\n\n**Source:** `megatron/bridge/models/qwen_vl/modelling_qwen3_vl/text_model.py`\n\nCalled through `packed_context_parallel_forward` in\n`areal/engine/megatron_utils/packed_context_parallel.py`:\n\n```python\noutput = model(\n    input_ids=input_ids,\n    attention_mask=attention_mask,\n    position_ids=position_ids,\n    packed_seq_params=packed_seq_params,\n    loss_mask=mtp_loss_mask,\n)\n```\n\n**Check:** Verify `Qwen3VLModel.forward` and its `Qwen3VLGPTModel` decoder accept\n`loss_mask`, preserve `labels=None` logits output, and pass layout-aligned `input_ids`\nto MCore's native MTP path. Confirm the upstream shadow embedding exposes\n`word_embeddings`; AReaL no longer patches either class.\n\n______________________________________________________________________\n\n### 9. `AutoMapping.register_module_type`\n\n**Source:** `megatron/bridge/models/conversion/param_mapping.py`\n\nCalled after dynamically promoting an MCore output layer in\n`areal/models/mcore/vocab_parallel_head.py`:\n\n```python\nAutoMapping.register_module_type(output_layer.__class__.__name__, \"column\")\n```\n\n**Check:** Confirm the import path, classmethod signature, accepted mapping type\n(`\"column\"`), and registry lifetime. This mapping must allow Bridge to export dynamic\nAReAL LM-head subclasses without replacing their parameters or hooks.\n\n______________________________________________________________________\n\n## Version-Guarded Code\n\n- `areal/engine/megatron_utils/megatron_lora.py:185` —\n  `hasattr(AutoBridge, \"save_hf_adapter\")` guard. The monkey-patch at line 291 is only\n  applied when `save_hf_adapter` does not exist on `AutoBridge`. The module-level call\n  at line 298 (`_monkey_patch_save_hf_adapter()`) runs at import time, so the guard is\n  evaluated once on first import. If upgrading to a version that ships `save_hf_adapter`\n  natively, the guard will skip patching, but the native signature must then match what\n  AReaL's call site expects (see entry 4 above). Once confirmed compatible, the entire\n  function `_monkey_patch_save_hf_adapter()` and its line-298 invocation can be removed.\n","frontmatter":{"package":"megatron-bridge","github":"NVIDIA-NeMo/Megatron-Bridge","branch_template":"v${VERSION}","upstream_paths":["megatron/bridge/__init__.py","megatron/bridge/models/conversion/auto_bridge.py","megatron/bridge/models/conversion/param_mapping.py","megatron/bridge/models/qwen_vl/modelling_qwen3_vl/text_model.py","megatron/bridge/peft/lora.py"]},"isInternal":false,"tokens":2166,"sizeBytes":9982},{"name":"megatron-core.md","path":".agents/skills/upgrade-deps/checklists/megatron-core.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.agents/skills/upgrade-deps/checklists/megatron-core.md","title":"Checklists Skill","category":"anthropic-skill","format":"markdown","content":"---\npackage: megatron-core\ngithub: NVIDIA/Megatron-LM\nbranch_template: core_v${VERSION}\nupstream_paths:\n  - megatron/core/parallel_state.py\n  - megatron/core/distributed/\n  - megatron/core/optimizer/\n  - megatron/core/optimizer_param_scheduler.py\n  - megatron/core/pipeline_parallel/\n  - megatron/core/tensor_parallel/\n  - megatron/core/transformer/transformer_config.py\n  - megatron/core/transformer/pipeline_parallel_layer_layout.py\n  - megatron/core/transformer/multi_token_prediction.py\n  - megatron/core/transformer/multi_latent_attention.py\n  - megatron/core/transformer/spec_utils.py\n  - megatron/core/transformer/transformer_block.py\n  - megatron/core/transformer/transformer_layer.py\n  - megatron/core/dist_checkpointing/\n  - megatron/core/dist_checkpointing/mapping.py\n  - megatron/core/dist_checkpointing/serialization.py\n  - megatron/core/dist_checkpointing/strategies/async_utils.py\n  - megatron/core/dist_checkpointing/strategies/fully_parallel.py\n  - megatron/core/fp8_utils.py\n  - megatron/core/models/gpt/\n  - megatron/core/packed_seq_params.py\n  - megatron/core/models/common/embeddings/rotary_pos_embedding.py\n  - megatron/core/utils.py\n---\n\n## Affected Files\n\n### Primary (engine layer — most likely to break)\n\n| File                                                     | Imports / Usage                                                                                                                                                                                                  |\n| -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `areal/engine/megatron_engine.py`                        | `parallel_state`, `tensor_parallel`, `DDP`, `finalize_model_grads`, `OptimizerConfig`, `get_megatron_optimizer`, `OptimizerParamScheduler`, `get_forward_backward_func`, `TransformerConfig`, `get_model_config` |\n| `areal/engine/megatron_utils/checkpointer.py`            | `dist_checkpointing.save`, `load`, sharded strategies, `ShardedObject`                                                                                                                                           |\n| `areal/engine/megatron_utils/megatron.py`                | `parallel_state`, FP8 detection, `TransformerConfig`                                                                                                                                                             |\n| `areal/engine/megatron_utils/pipeline_parallel.py`       | `TransformerConfig`, `PipelineParallelLayerLayout`                                                                                                                                                               |\n| `areal/engine/megatron_utils/packed_context_parallel.py` | `PackedSeqParams`, `parallel_state`                                                                                                                                                                              |\n| `areal/engine/megatron_utils/fp8/tensor_helper.py`       | `fp8_utils.is_float8tensor`                                                                                                                                                                                      |\n| `areal/engine/megatron_utils/deterministic.py`           | `AttnBackend` enum for deterministic attention selection                                                                                                                                                         |\n| `areal/engine/megatron_utils/weight_residency.py`        | `DistributedDataParallel` flat-buffer residency (`buffers`, `expert_parallel_buffers`)                                                                                                                           |\n\n### Secondary (model / infra layer)\n\n| File                                              | Imports / Usage                                                                                              |\n| ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |\n| `areal/models/mcore/registry.py`                  | `GPTModel`, `DDP`, `TransformerConfig`, `parallel_state`, tensor-parallel utilities                          |\n| `areal/models/mcore/bailing_moe.py`               | `MLATransformerConfig`, layer specs/builders, transformer blocks, TE/torch/fused norms                       |\n| `areal/models/mcore/bailing_v3.py`                | `MLATransformerConfig`, layer specs/builders, transformer blocks, TE/TP linears, TE/torch/fused norms        |\n| `areal/models/mcore/bailing_v3_bridge.py`         | `MLATransformerConfig`, `AttnBackend`, `parallel_state`                                                      |\n| `areal/models/mcore/bailing_v3_mla.py`            | `MLASelfAttention`, `build_module`, TE/TP column-parallel linear                                             |\n| `areal/models/mcore/lightning_attention.py`       | `parallel_state`, `apply_rotary_pos_emb`, `MegatronModule`, `ModuleSpec`, `build_module`, checkpoint helpers |\n| `areal/models/mcore/kda_attention.py`             | Sharded checkpoint types, `PackedSeqParams`, TP mappings/RNG, transformer module/spec/checkpoint helpers     |\n| `areal/models/mcore/hf_load.py`                   | `parallel_state`, `fp8_utils.is_float8tensor`                                                                |\n| `areal/models/mcore/hf_save.py`                   | `parallel_state`, `fp8_utils.is_float8tensor`                                                                |\n| `areal/models/mcore/vocab_parallel_head.py`       | Tensor-parallel layers and collectives                                                                       |\n| `areal/models/tree_attn/module_megatron.py`       | `PackedSeqParams`, `TransformerConfig`, `SelfAttention`, `AttnMaskType`, transformer layer/block specs       |\n| `areal/models/mcore/bailing_moe_bridge.py`        | `MLATransformerConfig`, `AttnBackend`                                                                        |\n| `areal/models/mcore/common.py`                    | `TransformerConfig`                                                                                          |\n| `areal/models/mcore/qwen3.py`                     | `gpt_layer_specs`, `TransformerConfig`                                                                       |\n| `areal/v2/weight_update/awex/delta_detect.py`     | Lazy `parallel_state` access for Megatron delta ownership                                                    |\n| `areal/v2/weight_update/awex/megatron_adapter.py` | Lazy `parallel_state` access for AWEX Megatron rank/group handling                                           |\n\n### Tertiary (tests, infra)\n\n| File                                                | Imports / Usage                                                   |\n| --------------------------------------------------- | ----------------------------------------------------------------- |\n| `areal/infra/workflow_executor.py`                  | conditional `parallel_state` for DP world size                    |\n| `tests/test_estimate_num_params.py`                 | `parallel_state`, `tensor_parallel`                               |\n| `tests/fp8/engine_utils.py`                         | `parallel_state`                                                  |\n| `tests/fp8/model_hooks.py`                          | `parallel_state`                                                  |\n| `tests/fp8/test_fp8_rmsnorm.py`                     | `fp8_utils`, `get_model_config`                                   |\n| `tests/torchrun/run_megatron_engine_distributed.py` | `parallel_state`                                                  |\n| `tests/test_megatron_engine_vlm.py`                 | `GPTModel` unwrapping and VLM engine contracts                    |\n| `tests/test_megatron_lm_head.py`                    | TP layers, `TransformerConfig`, `Float16Module`, PP helpers       |\n| `tests/test_reassemble_cp_logprobs.py`              | Conditional `megatron.core` availability                          |\n| `tests/torchrun/run_vocab_parallel.py`              | `parallel_state`, `ColumnParallelLinear`, `TransformerConfig`     |\n| `areal/tools/validation_base.py`                    | Megatron package/import metadata validation                       |\n| `areal/tools/validate_docker_installation.py`       | Megatron package and submodule smoke imports                      |\n| `tests/test_bailing_v3_hf_load.py`                  | Stubbed `parallel_state` and FP8 import contracts                 |\n| `tests/test_bailing_v3_kda_cp_helpers.py`           | Stubbed KDA checkpoint, packed-sequence, TP, and transformer APIs |\n| `tests/test_bailing_v3_nccl.py`                     | Conditional real MCore integration                                |\n| `tests/test_deterministic_prebuild.py`              | Conditional `AttnBackend` integration                             |\n| `tests/test_megatron_async_save.py`                 | Stubbed async distributed-checkpoint contracts                    |\n| `tests/test_megatron_bridge_deterministic.py`       | Conditional MCore bridge-provider integration                     |\n| `tests/test_megatron_engine.py`                     | Megatron engine integration and package version reporting         |\n| `tests/test_megatron_transport.py`                  | Conditional Megatron engine transport integration                 |\n| `tests/torchrun/run_mopd_teacher_residency.py`      | Patched MCore DDP flat-buffer residency                           |\n\n______________________________________________________________________\n\n## API Usage Catalog\n\nFor each function/class below, verify the call signature against the upstream source at\nthe target version. Focus on: **missing new required parameters**, **removed old\nparameters**, **renamed parameters**, **changed return types**, **changed method\nsignatures on returned objects**, and **moved/renamed modules**.\n\n### 1. `megatron.core.parallel_state.initialize_model_parallel`\n\n**Source:** `megatron/core/parallel_state.py`\n\nCalled in `areal/engine/megatron_engine.py` (line 195):\n\n```python\nmpu.initialize_model_parallel(\n    tensor_model_parallel_size=self.parallel_strategy.tensor_parallel_size,\n    pipeline_model_parallel_size=self.parallel_strategy.pipeline_parallel_size,\n    virtual_pipeline_model_parallel_size=vpp_size if vpp_size > 1 else None,\n    use_sharp=False,\n    order=\"tp-cp-ep-dp-pp\",\n    context_parallel_size=self.parallel_strategy.context_parallel_size,\n    expert_model_parallel_size=self.parallel_strategy.expert_parallel_size,\n    expert_tensor_parallel_size=self.parallel_strategy.expert_tensor_parallel_size,\n    distributed_timeout_minutes=int(DIST_GROUP_DEFAULT_TIMEOUT.seconds / 60),\n)\n```\n\n**Check:** Verify all keyword arguments still accepted — especially\n`expert_tensor_parallel_size` (recently added) and `distributed_timeout_minutes`. This\nis the most critical call — any signature change blocks ALL Megatron training. Confirm\n`order` string format `\"tp-cp-ep-dp-pp\"`.\n\n______________________________________________________________________\n\n### 2. `megatron.core.parallel_state` getter functions\n\n**Source:** `megatron/core/parallel_state.py`\n\nCalled extensively in `areal/engine/megatron_engine.py`, `areal/models/mcore/*.py`:\n\n```python\nmpu.get_data_parallel_rank()\nmpu.get_data_parallel_world_size()\nmpu.get_data_parallel_group()\nmpu.get_tensor_model_parallel_rank()\nmpu.get_tensor_model_parallel_world_size()\nmpu.get_tensor_model_parallel_group()\nmpu.get_pipeline_model_parallel_rank()\nmpu.get_pipeline_model_parallel_world_size()\nmpu.get_pipeline_model_parallel_group()\nmpu.get_context_parallel_rank()\nmpu.get_context_parallel_world_size()\nmpu.get_context_parallel_group()\nmpu.get_expert_model_parallel_rank()\nmpu.get_expert_model_parallel_world_size()\nmpu.is_pipeline_last_stage()\nmpu.is_pipeline_first_stage()\nmpu.model_parallel_is_initialized()\nmpu.destroy_model_parallel()\n```\n\n**Check:** Confirm all getter functions still exist with same no-arg signatures. These\nare called dozens of times across the codebase — any removal or rename is immediately\nfatal.\n\n______________________________________________________________________\n\n### 3. `megatron.core.parallel_state` utility functions\n\n**Source:** `megatron/core/parallel_state.py`\n\nCalled in `areal/engine/megatron_engine.py` (line 1048):\n\n```python\nrank_generator = mpu.RankGenerator(\n    tp=self.parallel_strategy.tensor_parallel_size,\n    ep=1,\n    dp=self.parallel_strategy.data_parallel_size,\n    pp=self.parallel_strategy.pipeline_parallel_size,\n    cp=self.parallel_strategy.context_parallel_size,\n    order=\"tp-cp-ep-dp-pp\",\n    rank_offset=0,\n)\ncontext_and_model_parallel_ranks = rank_generator.get_ranks(\"tp-cp-pp\")\n\ngroup = mpu.create_group(\n    ranks,\n    timeout=DIST_GROUP_DEFAULT_TIMEOUT,\n    pg_options=mpu.get_nccl_options(\"tp-cp-pp\", {}),\n    group_desc=\"CONTEXT_AND_MODEL_PARALLEL_GROUP\",\n)\n```\n\n**Check:** Verify `RankGenerator.__init__` keyword args — especially `rank_offset` and\n`order`. Confirm `get_ranks(group_str)` method. Verify `create_group` accepts `timeout`,\n`pg_options`, `group_desc` kwargs. Confirm `get_nccl_options(group_name, config_dict)`\nsignature.\n\n______________________________________________________________________\n\n### 4. `megatron.core.distributed.DistributedDataParallel`\n\n**Source:** `megatron/core/distributed/`\n\nCalled in `areal/models/mcore/registry.py` (line 310):\n\n```python\nfrom megatron.core.distributed import DistributedDataParallel as DDP\nfrom megatron.core.distributed import DistributedDataParallelConfig as MCoreDDPConfig\n\nddp_config = MCoreDDPConfig(**dataclasses.asdict(mcore_config.ddp))\nwrapped = DDP(\n    config=tf_config,\n    ddp_config=ddp_config,\n    module=model,\n    disable_bucketing=False,\n)\n```\n\n**Check:** Verify keyword arguments `config`, `ddp_config`, `module`,\n`disable_bucketing` are still accepted. Note AReaL does NOT pass `data_parallel_group`\nor `expert_data_parallel_group` — confirm these remain optional with sane defaults.\nCheck for new required parameters. Also verify `MCoreDDPConfig` dataclass fields haven't\nchanged (constructed from `mcore_config.ddp`).\n\n______________________________________________________________________\n\n### 5. `megatron.core.distributed.finalize_model_grads`\n\n**Source:** `megatron/core/distributed/`\n\nAssigned in `areal/engine/megatron_engine.py` (line 403):\n\n```python\nmodel_config.finalize_model_grads_func = finalize_model_grads\n```\n\nNot called directly by AReaL — instead assigned to\n`TransformerConfig.finalize_model_grads_func` and invoked internally by Megatron's\npipeline parallel forward-backward functions.\n\n**Check:** Confirm `finalize_model_grads` still has a compatible signature for\n`TransformerConfig.finalize_model_grads_func`. Verify it accepts `(model_chunks)` or\n`(model_chunks, num_tokens=...)` as expected by the pipeline executor.\n\n______________________________________________________________________\n\n### 6. `megatron.core.optimizer.OptimizerConfig`\n\n**Source:** `megatron/core/optimizer/`\n\nCalled in `areal/engine/megatron_engine.py` (line 1090):\n\n```python\nmcore_opt_config = MCoreOptimizerConfig(\n    optimizer=self.optimizer_config.type,\n    lr=self.optimizer_config.lr,\n    min_lr=self.optimizer_config.min_lr_ratio * self.optimizer_config.lr,\n    weight_decay=self.optimizer_config.weight_decay,\n    bf16=self.dtype is torch.bfloat16,\n    fp16=self.dtype is torch.float16,\n    adam_beta1=self.optimizer_config.beta1,\n    adam_beta2=self.optimizer_config.beta2,\n    adam_eps=self.optimizer_config.eps,\n    use_distributed_optimizer=use_distributed_optimizer,\n    params_dtype=self.dtype,\n    clip_grad=self.optimizer_config.gradient_clipping,\n    fp8_recipe=(self.fp8_config.recipe if self.enable_fp8 else None),\n)\n# Post-init field assignments:\nmcore_opt_config.overlap_param_gather_with_optimizer_step = ...\nmcore_opt_config.use_precision_aware_optimizer = ...\nmcore_opt_config.main_grads_dtype = getattr(torch, ...)\nmcore_opt_config.main_params_dtype = getattr(torch, ...)\nmcore_opt_config.exp_avg_dtype = getattr(torch, ...)\nmcore_opt_config.exp_avg_sq_dtype = getattr(torch, ...)\n```\n\n**Check:** Verify all ~13 constructor kwargs still accepted — especially `fp8_recipe`\nand `fp16`. Confirm the 6 post-init fields (`overlap_param_gather_with_optimizer_step`,\n`use_precision_aware_optimizer`, `main_grads_dtype`, `main_params_dtype`,\n`exp_avg_dtype`, `exp_avg_sq_dtype`) still exist on the dataclass. Check for renamed or\nremoved fields.\n\n______________________________________________________________________\n\n### 7. `megatron.core.optimizer.get_megatron_optimizer`\n\n**Source:** `megatron/core/optimizer/`\n\nCalled in `areal/engine/megatron_engine.py` (line 1122):\n\n```python\nself.optimizer = get_megatron_optimizer(mcore_opt_config, self.model)\n```\n\n**Check:** Verify the two-arg call `(config, model_chunks)` is still valid. Confirm\n`model_chunks` accepts a list of DDP-wrapped modules. Check if new required arguments\nwere added (e.g., `no_weight_decay_cond`, `scale_lr_cond` — AReaL does not pass these,\nso confirm they remain optional).\n\n______________________________________________________________________\n\n### 8. `megatron.core.optimizer_param_scheduler.OptimizerParamScheduler`\n\n**Source:** `megatron/core/optimizer_param_scheduler.py`\n\nCalled in `areal/engine/megatron_engine.py` (line 1126):\n\n```python\nlr_scheduler = OptimizerParamScheduler(\n    self.optimizer,\n    init_lr=0.0 if warmup_steps_proportion > 0 else self.optimizer_config.lr,\n    max_lr=self.optimizer_config.lr,\n    min_lr=self.optimizer_config.min_lr_ratio * self.optimizer_config.lr,\n    lr_warmup_steps=warmup_steps,\n    lr_decay_steps=ft_spec.total_train_steps - warmup_steps,\n    lr_decay_style=self.optimizer_config.lr_scheduler_type,\n    start_wd=self.optimizer_config.weight_decay,\n    end_wd=self.optimizer_config.weight_decay,\n    wd_incr_steps=ft_spec.total_train_steps,\n    wd_incr_style=\"constant\",\n)\n```\n\n**Check:** Confirm all keyword arguments still accepted — especially `start_wd`,\n`end_wd`, `wd_incr_steps`, `wd_incr_style` (weight decay scheduling). Verify\n`lr_decay_style` valid choices (e.g., `\"cosine\"`, `\"linear\"`, `\"WSD\"`). Check for\nrenamed params.\n\n______________________________________________________________________\n\n### 9. `megatron.core.pipeline_parallel.get_forward_backward_func`\n\n**Source:** `megatron/core/pipeline_parallel/`\n\nCalled in `areal/engine/megatron_engine.py` (line 732, hot training loop):\n\n```python\nforward_backward_func = get_forward_backward_func()\nforward_backward_func(\n    forward_step_func=forward_step,\n    data_iterator=data_iterator,\n    model=self.model if len(self.model) > 1 else self.model[0],\n    num_microbatches=len(mb_list),\n    seq_length=mb_list.max_seqlen,\n    micro_batch_size=1,\n    forward_only=forward_only,\n)\n```\n\n**Check:** Verify `get_forward_backward_func()` still returns a callable with this\nsignature. Confirm `seq_length` and `micro_batch_size` kwargs are still accepted (marked\n\"no use when input_shapes was set\" in code). This is in the HOT training loop — any\nbreakage stops all PP training.\n\n______________________________________________________________________\n\n### 10. `megatron.core.transformer.TransformerConfig`\n\n**Source:** `megatron/core/transformer/transformer_config.py`\n\nUsed in `areal/engine/megatron_engine.py`,\n`areal/engine/megatron_utils/pipeline_parallel.py`, `areal/models/mcore/registry.py`,\n`areal/models/mcore/common.py`, `areal/models/mcore/qwen3.py`:\n\n```python\ntransformer_config = TransformerConfig(\n    num_layers=num_layers,\n    hidden_size=hidden_size,\n    num_attention_heads=num_heads,\n    num_key_value_heads=num_kv_heads,\n    # ... many more fields\n)\n```\n\n**Check:** This is the central config dataclass — verify no fields were removed or\nrenamed. New optional fields with defaults are fine. Check `MLATransformerConfig`\nsubclass compatibility (used in `bailing_moe.py` and `bailing_moe_bridge.py`).\n\n______________________________________________________________________\n\n### 11. `megatron.core.dist_checkpointing` save/load\n\n**Source:** `megatron/core/dist_checkpointing/`\n\nCalled in `areal/engine/megatron_utils/checkpointer.py` (lines 23-32, 53-85):\n\n```python\nfrom megatron.core import dist_checkpointing\nfrom megatron.core.dist_checkpointing.mapping import ShardedObject\nfrom megatron.core.dist_checkpointing.serialization import (\n    get_default_save_sharded_strategy,\n    get_default_load_sharded_strategy,\n)\nfrom megatron.core.dist_checkpointing.strategies.fully_parallel import (\n    FullyParallelLoadStrategyWrapper,\n    FullyParallelSaveStrategyWrapper,\n)\n\n# Save (line 62):\nsave_strategy = get_default_save_sharded_strategy(\"torch_dist\")\nsave_strategy = FullyParallelSaveStrategyWrapper(save_strategy, dp_group)\ndist_checkpointing.save(\n    sharded_state_dict, ckpt_path,\n    sharded_strategy=save_strategy,\n    async_sharded_save=async_save,\n    validate_access_integrity=validate_sharding_integrity,\n)\n\n# Load (line 75):\nload_strategy = get_default_load_sharded_strategy(ckpt_dir)\nload_strategy = FullyParallelLoadStrategyWrapper(load_strategy, dp_group)\nstate_dict = dist_checkpointing.load(\n    sharded_state_dict, ckpt_dir, sharded_strategy=load_strategy\n)\n```\n\n**Check:** Verify `save` kwargs — especially `async_sharded_save` and\n`validate_access_integrity`. Confirm `get_default_save_sharded_strategy` accepts\n`\"torch_dist\"` string. Confirm `get_default_load_sharded_strategy` accepts `ckpt_dir`\npath. Verify import paths: `serialization` for strategy factories,\n`strategies.fully_parallel` for wrappers, `mapping` for `ShardedObject`. Checkpoint\nformat compatibility is critical — verify no format version bumps.\n\n______________________________________________________________________\n\n### 12. `megatron.core.transformer.PipelineParallelLayerLayout`\n\n**Source:** `megatron/core/transformer/pipeline_parallel_layer_layout.py`\n\nCalled in `areal/engine/megatron_utils/pipeline_parallel.py` (line 6):\n\n```python\nfrom megatron.core.transformer.pipeline_parallel_layer_layout import (\n    PipelineParallelLayerLayout,\n)\n```\n\n**Check:** Confirm the import path is still\n`megatron.core.transformer.pipeline_parallel_layer_layout` (not\n`transformer_config.py`). Verify constructor kwargs. Check if\n`get_num_layers_to_build()` and `get_transformer_layer_offset()` are still accessible.\n\n______________________________________________________________________\n\n### 13. `megatron.core.fp8_utils.is_float8tensor`\n\n**Source:** `megatron/core/fp8_utils.py`\n\nCalled in `areal/engine/megatron_utils/fp8/tensor_helper.py`:\n\n```python\nfrom megatron.core.fp8_utils import is_float8tensor\nif is_float8tensor(param):\n    ...\n```\n\n**Check:** Confirm function still exists at this path. Verify it accepts a single tensor\nargument. Check if FP8 support was restructured (e.g., moved to\n`megatron.core.extensions.transformer_engine`).\n\n______________________________________________________________________\n\n### 14. `megatron.core.models.gpt.GPTModel`\n\n**Source:** `megatron/core/models/gpt/`\n\nCalled in `areal/models/mcore/registry.py`:\n\n```python\nmodel = GPTModel(\n    config=transformer_config,\n    transformer_layer_spec=layer_spec,\n    vocab_size=vocab_size,\n    max_sequence_length=max_seq_len,\n    pre_process=pre_process,\n    post_process=post_process,\n)\n```\n\n**Check:** Verify constructor kwargs. Confirm `get_gpt_decoder_block_spec()` still\nreturns a valid layer spec. Check for new required parameters.\n\n______________________________________________________________________\n\n### 15. `megatron.core.tensor_parallel` utilities\n\n**Source:** `megatron/core/tensor_parallel/`\n\nCalled in `areal/engine/megatron_engine.py` and\n`areal/models/mcore/lightning_attention.py`; extended by\n`areal/models/mcore/vocab_parallel_head.py`:\n\n```python\ntensor_parallel.model_parallel_cuda_manual_seed(seed)\ntensor_parallel.gather_from_sequence_parallel_region(output)\ntensor_parallel.get_cuda_rng_tracker()\nlinear_with_areal_output(..., gtp_remat_size=gtp_remat_size)\n```\n\n**Check:** Verify these functions still exist with same signatures.\n`model_parallel_cuda_manual_seed` is called once at init;\n`gather_from_sequence_parallel_region` is in hot path. Keep the custom LM-head autograd\nfunction's forward/backward arity aligned with\n`LinearWithGradAccumulationAndAsyncCommunication`, including `gtp_remat_size`.\n\n______________________________________________________________________\n\n### 16. Transformer layer specs and module builders\n\n**Source:** `megatron/core/transformer/`\n\nCalled in `areal/models/mcore/bailing_moe.py`,\n`areal/models/mcore/lightning_attention.py`,\n`areal/models/mcore/tree_attn/module_megatron.py`:\n\n```python\nfrom megatron.core.transformer import ModuleSpec, build_module\nfrom megatron.core.transformer.enums import AttnMaskType, LayerType\nfrom megatron.core.transformer.transformer_layer import (\n    TransformerLayer, TransformerLayerSubmodules,\n)\nfrom megatron.core.transformer.transformer_block import TransformerBlockSubmodules\nfrom megatron.core.transformer.attention import SelfAttention\nfrom megatron.core.transformer.custom_layers.transformer_engine import (\n    # TE norm modules\n)\n```\n\n**Check:** Verify enum values in `AttnMaskType` and `LayerType`. Confirm `ModuleSpec`\nand `build_module` signatures. Check `SelfAttention.__init__` for new required params.\nVerify TE integration module paths haven't been restructured.\n\n______________________________________________________________________\n\n### 17. `PackedSeqParams`\n\n**Source:** `megatron/core/packed_seq_params.py` (or `megatron/core/transformer/`)\n\nCalled in `areal/engine/megatron_utils/packed_context_parallel.py`:\n\n```python\nfrom megatron.core.packed_seq_params import PackedSeqParams\npacked_seq_params = PackedSeqParams(\n    cu_seqlens_q=cu_seqlens_q,\n    cu_seqlens_kv=cu_seqlens_kv,\n    max_seqlen_q=max_seqlen_q,\n    max_seqlen_kv=max_seqlen_kv,\n    qkv_format=\"thd\",\n)\n```\n\n**Check:** Confirm constructor kwargs and import path. Verify `qkv_format` valid\nchoices.\n\n______________________________________________________________________\n\n### 18. RoPE utilities\n\n**Source:** `megatron/core/models/common/embeddings/`\n\nCalled in `areal/models/mcore/bailing_moe.py`,\n`areal/models/mcore/lightning_attention.py`:\n\n```python\nfrom megatron.core.models.common.embeddings.rotary_pos_embedding import apply_rotary_pos_emb\n# Also various rope_utils for extended RoPE (YaRN, etc.)\n```\n\n**Check:** Verify `apply_rotary_pos_emb` signature (`t`, `freqs`) and the optional MLA\nrotary arguments. MCore 0.19 owns THD+CP frequency selection; AReaL must not reinstall\nthe removed global MLA RoPE workaround.\n\n______________________________________________________________________\n\n### 19. Async distributed-checkpoint request and queue internals\n\n**Source:** `megatron/core/dist_checkpointing/strategies/async_utils.py`\n\nUsed in `areal/engine/megatron_utils/checkpointer.py`:\n\n```python\nfrom megatron.core.dist_checkpointing.strategies.async_utils import (\n    AsyncCallsQueue,\n    AsyncRequest,\n)\n\nsave_kwargs[\"async_strategy\"] = \"mcore\"\nasync_request = dist_checkpointing.save(**save_kwargs)\nqueue = AsyncCallsQueue()\nqueue.schedule_async_request(async_request)\n```\n\nThe checkpoint manager also validates and clears the retained tensor payload through the\nprivate `AsyncRequest.async_fn_args` write-bucket layout after D2H staging.\n\n**Check:** Confirm `AsyncRequest` and `AsyncCallsQueue` import paths and methods. Verify\n`dist_checkpointing.save(..., async_strategy=\"mcore\")` still returns an `AsyncRequest`,\nand audit the exact `async_fn_args` / write-bucket payload structure before retaining\nthe private-reference cleanup optimization.\n\n______________________________________________________________________\n\n### 20. MTP model, loss, and metrics APIs\n\n**Source:** `megatron/core/models/gpt/gpt_model.py` and\n`megatron/core/transformer/multi_token_prediction.py`\n\nConfigured in `areal/models/mcore/registry.py`, forwarded by\n`areal/engine/megatron_utils/packed_context_parallel.py`, and collected in\n`areal/engine/megatron_engine.py`:\n\n```python\nprovider.mtp_detach_heads = True\nmodel(..., labels=None, loss_mask=mtp_loss_mask)\nMTPLossLoggingHelper.reduce_metrics_in_tracker()\nloss = MTPLossLoggingHelper.tracker[\"loss_values\"]\nMTPLossLoggingHelper.clean_metrics_in_tracker()\n```\n\n**Check:** Verify `GPTModel.forward` forwards `input_ids` and `loss_mask` to native\n`process_mtp_loss` when `labels=None`; `mtp_detach_heads` must isolate both hidden state\nand tied/untied output weights. Verify the `MTPLossLoggingHelper` tracker keys and\nreduce/clean method names. These are semi-private contracts and changed in 0.19.\n\n______________________________________________________________________\n\n### 21. MLA configuration, attention backend, and layer-layout helpers\n\n**Source:** `megatron/core/transformer/multi_latent_attention.py`,\n`megatron/core/transformer/enums.py`, `transformer_block.py`, and `transformer_layer.py`\n\nUsed by the Bailing model family and pipeline layout helpers:\n\n```python\nfrom megatron.core.transformer.multi_latent_attention import (\n    MLASelfAttention,\n    MLATransformerConfig,\n)\nfrom megatron.core.transformer.enums import AttnBackend, LayerType\nfrom megatron.core.transformer.transformer_block import get_num_layers_to_build\nfrom megatron.core.transformer.transformer_layer import get_transformer_layer_offset\n```\n\n**Check:** Confirm imports and constructor signatures, enum members used by AReaL, and\nthe helper return types. Verify `MLASelfAttention` still exposes the overridable methods\nconsumed by `bailing_v3_mla.py`.\n\n______________________________________________________________________\n\n### 22. Custom attention module and sharded-state helpers\n\n**Source:** `megatron/core/dist_checkpointing/mapping.py`,\n`megatron/core/tensor_parallel/mappings.py`, and `megatron/core/transformer/`\n\nUsed by `areal/models/mcore/kda_attention.py` and the custom attention modules:\n\n```python\nfrom megatron.core.dist_checkpointing import ShardedTensor\nfrom megatron.core.dist_checkpointing.mapping import ReplicaId, ShardedTensorFactory\nfrom megatron.core.tensor_parallel.mappings import (\n    gather_from_tensor_model_parallel_region,\n    scatter_to_sequence_parallel_region,\n)\nfrom megatron.core.transformer.spec_utils import ModuleSpec, build_module\nfrom megatron.core.transformer.utils import (\n    make_sharded_tensors_for_checkpoint,\n    sharded_state_dict_default,\n)\n```\n\n**Check:** Verify import paths, factory/dataclass fields, TP collective signatures, and\nthe sharded-state helper inputs/returns. Confirm the optional\n`PackedSeqParamsWithSeqidx` import remains safely absent or compatible upstream.\n\n______________________________________________________________________\n\n### 23. `Float16Module` and pipeline-stage helpers\n\n**Source:** `megatron/core/transformer/module.py` and\n`megatron/core/pipeline_parallel/utils.py`\n\nExercised in `tests/test_megatron_lm_head.py` to preserve output casting:\n\n```python\nmodel = Float16Module(config, module)\noutput = model(fp32_output=False)\n```\n\nThe test also patches `is_pp_first_stage`, `is_pp_last_stage`, `is_vp_first_stage`, and\n`is_vp_last_stage` with their process-group/stage arguments.\n\n**Check:** Verify the `Float16Module` constructor/forward pass-through behavior and the\npipeline helper signatures so the AReAL LM-head output override is still propagated.\n\n______________________________________________________________________\n\n## Version-Guarded Code\n\nNo known version-guarded code exists in AReaL for `megatron-core`. However, the\n`MLATransformerConfig` class used in `bailing_moe.py` and `bailing_moe_bridge.py` is a\nrelatively new addition — confirm it exists in the target version.\n","frontmatter":{"package":"megatron-core","github":"NVIDIA/Megatron-LM","branch_template":"core_v${VERSION}","upstream_paths":["megatron/core/parallel_state.py","megatron/core/distributed/","megatron/core/optimizer/","megatron/core/optimizer_param_scheduler.py","megatron/core/pipeline_parallel/","megatron/core/tensor_parallel/","megatron/core/transformer/transformer_config.py","megatron/core/transformer/pipeline_parallel_layer_layout.py","megatron/core/transformer/multi_token_prediction.py","megatron/core/transformer/multi_latent_attention.py","megatron/core/transformer/spec_utils.py","megatron/core/transformer/transformer_block.py","megatron/core/transformer/transformer_layer.py","megatron/core/dist_checkpointing/","megatron/core/dist_checkpointing/mapping.py","megatron/core/dist_checkpointing/serialization.py","megatron/core/dist_checkpointing/strategies/async_utils.py","megatron/core/dist_checkpointing/strategies/fully_parallel.py","megatron/core/fp8_utils.py","megatron/core/models/gpt/","megatron/core/packed_seq_params.py","megatron/core/models/common/embeddings/rotary_pos_embedding.py","megatron/core/utils.py"]},"isInternal":false,"tokens":6626,"sizeBytes":32064},{"name":"peft.md","path":".agents/skills/upgrade-deps/checklists/peft.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.agents/skills/upgrade-deps/checklists/peft.md","title":"Checklists Skill","category":"anthropic-skill","format":"markdown","content":"---\npackage: peft\ngithub: huggingface/peft\nbranch_template: v${VERSION}\nupstream_paths:\n  - src/peft/peft_model.py\n  - src/peft/tuners/lora/config.py\n  - src/peft/utils/peft_types.py\n---\n\n## Affected Files\n\n### Primary (engine layer — most likely to break)\n\n| File                          | Imports / Usage                            |\n| ----------------------------- | ------------------------------------------ |\n| `areal/engine/fsdp_engine.py` | `LoraConfig`, `TaskType`, `get_peft_model` |\n\n### Secondary (model / infra layer)\n\n| File                                             | Imports / Usage                                                                                                                                                     |\n| ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `areal/engine/megatron_engine.py`                | indirect — consumes PEFT config dict schema (`r`, `lora_alpha`, `target_modules`, `bias`) via `megatron.bridge.peft.lora`; stores in `WeightUpdateMeta.peft_config` |\n| `areal/engine/megatron_utils/megatron_lora.py`   | no direct import — generates `adapter_config.json` in PEFT format; parses PEFT weight key format (`base_model.model.layers.N.self_attn.q_proj.lora_A.weight`)       |\n| `areal/engine/vllm_ext/vllm_worker_extension.py` | indirect — uses `vllm.lora.peft_helper.PEFTHelper`, not direct peft import                                                                                          |\n| `areal/engine/vllm_remote.py`                    | builds HTTP payloads containing `peft_config` dict for distributed LoRA updates                                                                                     |\n| `areal/api/cli_args.py`                          | config fields only: `use_lora`, `lora_rank`, `lora_alpha`, `target_modules`, `peft_type`                                                                            |\n| `areal/api/io_struct.py`                         | `WeightUpdateMeta.peft_config: dict` — schema `{\"r\": rank, \"lora_alpha\": alpha, \"target_modules\": [...], \"bias\": \"none\"}`                                           |\n\n### Tertiary (tests, config)\n\n| File                                               | Imports / Usage                                                                     |\n| -------------------------------------------------- | ----------------------------------------------------------------------------------- |\n| `tests/torchrun/run_fsdp_memory_efficient_lora.py` | integration test with `use_lora=True, lora_rank=8, lora_alpha=16, peft_type=\"lora\"` |\n\n______________________________________________________________________\n\n## API Usage Catalog\n\nFor each function/class below, verify the call signature against the upstream source at\nthe target version. Focus on: **missing new required parameters**, **removed old\nparameters**, **renamed parameters**, **changed return types**, **changed method\nsignatures on returned objects**, and **moved/renamed modules**.\n\n### 1. `peft.LoraConfig` and `peft.TaskType.CAUSAL_LM`\n\n**Source:** `src/peft/tuners/lora/config.py`, `src/peft/utils/peft_types.py`\n\nCalled in `areal/engine/fsdp_engine.py` (`_apply_peft_wrapper`, lines 930–946):\n\n```python\nfrom peft import LoraConfig, TaskType, get_peft_model\n\npeft_config = {\n    \"task_type\": TaskType.CAUSAL_LM,\n    \"r\": config.lora_rank,\n    \"lora_alpha\": config.lora_alpha,\n    \"target_modules\": target_modules,  # str \"all-linear\" or list[str]\n    \"bias\": \"none\",\n}\nif self.config.peft_type == \"lora\":\n    peft_config = LoraConfig(**peft_config)\nelse:\n    raise NotImplementedError()\n```\n\n**Check:** Verify `LoraConfig.__init__` still accepts `task_type`, `r`, `lora_alpha`,\n`target_modules`, and `bias` as keyword arguments with the same types. Check whether any\nnew required parameters were added. Confirm `bias=\"none\"` is still a valid string choice\n(not renamed or replaced with an enum). Confirm `target_modules` still accepts both a\nstring (`\"all-linear\"`) and a `list[str]`. Verify `TaskType` is still an enum importable\nfrom `peft` and `TaskType.CAUSAL_LM` still exists. Verify the class is still importable\ndirectly from `peft` (not moved to `peft.tuners.lora` only).\n\n______________________________________________________________________\n\n### 2. `peft.get_peft_model`\n\n**Source:** `src/peft/peft_model.py`\n\nCalled in `areal/engine/fsdp_engine.py` (`_apply_peft_wrapper`, lines 948–953), after\n`model.enable_input_require_grads()` and before FSDP sharding:\n\n```python\nself.model.enable_input_require_grads()\nself.model = get_peft_model(\n    self.model,\n    peft_config,\n    autocast_adapter_dtype=False,\n)\n```\n\n**Check:** Confirm `get_peft_model` is still exported at the top-level `peft` namespace.\nVerify its signature is `get_peft_model(model, peft_config, ...)` with\n`autocast_adapter_dtype` still an accepted keyword argument (it was added in a mid-cycle\nrelease and could be renamed or removed). Confirm the return type is still a `PeftModel`\nwrapping the original model. Check whether the returned model's `.parameters()`,\n`.named_modules()`, and FSDP-compatibility hooks remain intact — the call site\nimmediately passes the result into FSDP2 sharding.\n\n______________________________________________________________________\n\n### 3. PEFT weight key format and `adapter_config.json` schema (indirectly consumed)\n\n**Source:** `src/peft/tuners/lora/` (weight saving conventions)\n\nParsed in `areal/engine/megatron_utils/megatron_lora.py`\n(`_infer_target_modules_from_adapter_weights`, lines 124–150):\n\n```python\n# Expected key pattern for LoRA weights:\n# \"base_model.model.layers.0.self_attn.q_proj.lora_A.weight\" -> q_proj\n# \"base_model.model.layers.1.mlp.gate_proj.lora_B.weight\"   -> gate_proj\nfor key in weight_keys:\n    key = key.replace(\"base_model.model.\", \"\")\n    if \".lora_A.weight\" in key:\n        module_name = key.replace(\".lora_A.weight\", \"\").split(\".\")[-1]\n    elif \".lora_B.weight\" in key:\n        module_name = key.replace(\".lora_B.weight\", \"\").split(\".\")[-1]\n```\n\nAlso used in `_build_adapter_config_dict` (lines 153–178) to emit an\n`adapter_config.json`:\n\n```python\n{\n    \"base_model_name_or_path\": base_model_name_or_path,\n    \"peft_type\": \"LORA\",\n    \"task_type\": \"CAUSAL_LM\",\n    \"inference_mode\": False,\n    \"r\": peft_config.dim,\n    \"lora_alpha\": peft_config.alpha,\n    \"lora_dropout\": peft_config.dropout,\n    \"target_modules\": target_modules,\n    \"bias\": \"none\",\n    \"fan_in_fan_out\": False,\n    \"modules_to_save\": None,\n    \"init_lora_weights\": True,\n    \"layers_to_transform\": None,\n    \"layers_pattern\": None,\n}\n```\n\n**Check:** Confirm the saved weight key format (`base_model.model.<...>.lora_A.weight` /\n`lora_B.weight`) hasn't changed. Check whether new LoRA variants (e.g.,\n`lora_magnitude_vector` for DoRA) added extra keys that the parser doesn't handle.\nVerify all `adapter_config.json` fields (`peft_type`, `task_type`, `r`, `lora_alpha`,\n`lora_dropout`, `target_modules`, `bias`, `fan_in_fan_out`, etc.) still match what\nPEFT's `LoraConfig.from_pretrained()` expects when loading. This schema is also used for\nvLLM LoRA hot-swap via `WeightUpdateMeta.peft_config`.\n\n______________________________________________________________________\n\n### 4. `WeightUpdateMeta.peft_config` dict schema (cross-engine contract)\n\n**Source:** PEFT conventions (not a direct import, but a schema contract)\n\nPopulated in `areal/engine/megatron_engine.py` (lines 1196–1203) and\n`areal/engine/fsdp_engine.py` (lines 1148–1153):\n\n```python\nmeta.peft_config = {\n    \"r\": self.config.lora_rank,\n    \"lora_alpha\": self.config.lora_alpha,\n    \"target_modules\": target_modules,  # list[str] or str\n    \"bias\": \"none\",\n}\n```\n\nConsumed in `areal/engine/vllm_remote.py` (lines 169–172):\n\n```python\n\"lora_target_modules\": meta.peft_config[\"target_modules\"],\n\"lora_rank\": meta.peft_config[\"r\"],\n\"lora_alpha\": meta.peft_config[\"lora_alpha\"],\n\"lora_bias\": meta.peft_config[\"bias\"],\n```\n\nAnd in `areal/engine/vllm_ext/vllm_worker_extension.py` (lines 243–246) to reconstruct a\n`PEFTHelper` for vLLM's LoRA manager.\n\n**Check:** This dict is the **contract** between AReaL's training engines and inference\nbackends. If PEFT changes its config field names (`r` → `rank`, `lora_alpha` → `alpha`,\netc.), all three sites must be updated in sync. Verify that vLLM's\n`PEFTHelper.from_dict` still accepts the same key names as what PEFT's `LoraConfig`\nuses.\n\n______________________________________________________________________\n\n## Version-Guarded Code\n\nNo known version-guarded code exists in AReaL for `peft`.\n","frontmatter":{"package":"peft","github":"huggingface/peft","branch_template":"v${VERSION}","upstream_paths":["src/peft/peft_model.py","src/peft/tuners/lora/config.py","src/peft/utils/peft_types.py"]},"isInternal":false,"tokens":2067,"sizeBytes":8725},{"name":"sglang.md","path":".agents/skills/upgrade-deps/checklists/sglang.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.agents/skills/upgrade-deps/checklists/sglang.md","title":"Checklists Skill","category":"anthropic-skill","format":"markdown","content":"---\npackage: sglang\ngithub: sgl-project/sglang\nbranch_template: v${VERSION}\nupstream_paths:\n  - python/sglang/srt/entrypoints/engine.py\n  - python/sglang/srt/entrypoints/http_server.py\n  - python/sglang/srt/entrypoints/openai/protocol.py\n  - python/sglang/srt/function_call/function_call_parser.py\n  - python/sglang/srt/function_call/core_types.py\n  - python/sglang/srt/managers/scheduler.py\n  - python/sglang/srt/managers/io_struct.py\n  - python/sglang/srt/parser/reasoning_parser.py\n  - python/sglang/srt/server_args.py\n  - python/sglang/srt/utils/__init__.py\n  - python/sglang/srt/utils/common.py\n  - python/sglang/srt/utils/network.py\n  - python/sglang/srt/utils/numa_utils.py\n  - python/sglang/launch_server.py\n---\n\n## Affected Files\n\n### Primary (engine layer — most likely to break)\n\n| File                                                 | Imports / Usage                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |\n| ---------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `areal/engine/sglang_remote.py`                      | HTTP-only (no Python imports). Sends requests to `/generate`, `/load_lora_adapter`, `/update_weights_from_disk`, `/update_weights_from_distributed`, `/init_weights_update_group`, `/pause_generation`, `/continue_generation`, `/health`, `/release_memory_occupation`, `/resume_memory_occupation`. Parses `meta_info`, `finish_reason`, `output_token_logprobs`, `routed_experts` from JSON responses.                                                                                                                   |\n| `areal/experimental/openai/tool_call_parser.py`      | `sglang.srt.entrypoints.openai.protocol.Function`, `sglang.srt.entrypoints.openai.protocol.Tool`, `sglang.srt.function_call.function_call_parser.FunctionCallParser`, `sglang.srt.parser.reasoning_parser.ReasoningParser`                                                                                                                                                                                                                                                                                                  |\n| `areal/v2/inference_service/sglang/launch_server.py` | `sglang.srt.entrypoints.engine.Engine`, `sglang.srt.entrypoints.engine.init_tokenizer_manager`, `sglang.srt.entrypoints.http_server._execute_server_warmup`, `sglang.srt.entrypoints.http_server._setup_and_run_http_server`, `sglang.srt.entrypoints.http_server.app`, `sglang.srt.managers.detokenizer_manager.run_detokenizer_process`                                                                                                                                                                                   |\n| `areal/v2/inference_service/sglang/scheduler.py`     | `sglang.srt.managers.scheduler.Scheduler`, `sglang.srt.managers.scheduler.configure_scheduler`, `sglang.srt.observability.trace.process_tracing_init`, `sglang.srt.observability.trace.trace_set_thread_info`, `sglang.srt.utils.get_bool_env_var`, `sglang.srt.utils.kill_itself_when_parent_died`, `sglang.srt.utils.set_gpu_proc_affinity`, `sglang.srt.utils.numa_utils.get_numa_node_if_available`, `sglang.srt.utils.numa_utils.numa_bind_to_node`, `sglang.srt.environ.envs`, `sglang.utils.get_exception_traceback` |\n| `areal/v2/inference_service/sglang/rpc_proxy.py`     | `sglang.srt.managers.io_struct.RpcReqInput`, `sglang.srt.managers.io_struct.RpcReqOutput`, `sglang.srt.server_args.PortArgs`, `sglang.srt.utils.network.get_zmq_socket`                                                                                                                                                                                                                                                                                                                                                     |\n| `areal/v2/inference_service/sglang/bridge.py`        | HTTP-only (same protocol as `sglang_remote.py`). `/generate` request/response, `/pause_generation`, `/continue_generation`, `/release_memory_occupation`, `/resume_memory_occupation`.                                                                                                                                                                                                                                                                                                                                      |\n\n### Secondary (model / infra layer)\n\n| File                                                  | Imports / Usage                                                                                                                      |\n| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |\n| `areal/infra/launcher/sglang_server.py`               | HTTP-only. Polls `/v1/models` to confirm server readiness. Calls `SGLangConfig.build_cmd()` to construct the server launch command.  |\n| `areal/api/cli_args.py`                               | `SGLangConfig.build_cmd()`, `SGLangConfig.build_cmd_from_args()`, `SGLangConfig.build_args()`. Version check against `0.5.10.post1`. |\n| `areal/trainer/rl_trainer.py`                         | `SGLangConfig.build_args()` to construct `RemoteSGLangEngine`.                                                                       |\n| `areal/infra/launcher/ray.py`                         | `SGLangConfig` import + `to_structured_cfg`.                                                                                         |\n| `areal/infra/launcher/local.py`                       | `SGLangConfig` import + `to_structured_cfg`.                                                                                         |\n| `areal/infra/launcher/slurm.py`                       | `SGLangConfig` import + `to_structured_cfg`.                                                                                         |\n| `areal/v2/inference_service/controller/controller.py` | `SGLangConfig` in gateway controller.                                                                                                |\n| `areal/v2/inference_service/data_proxy/backend.py`    | HTTP protocol implementation for inference service proxy (SGLangBridgeBackend).                                                      |\n\n### Tertiary (tests, config)\n\n| File                                                 | Imports / Usage                                             |\n| ---------------------------------------------------- | ----------------------------------------------------------- |\n| `tests/grpo/test_grpo.py`                            | Test parameterization with `sglang` backend.                |\n| `tests/experimental/openai/test_tool_call_parser.py` | `FunctionCallParser` / `ReasoningParser` integration tests. |\n\n______________________________________________________________________\n\n## API Usage Catalog\n\nFor each function/class below, verify the call signature against the upstream source at\nthe target version. Focus on: **missing new required parameters**, **removed old\nparameters**, **renamed parameters**, **changed return types**, **changed method\nsignatures on returned objects**, and **moved/renamed modules**.\n\n> **Note:** Most sglang integration in AReaL is HTTP-based — the server is launched as a\n> subprocess and communicated with over REST. Only `areal/experimental/openai/` imports\n> the Python SDK directly. For the HTTP-based call sites, the risk is in **JSON field\n> renames** in request payloads and response bodies, not in Python API signature\n> changes.\n\n______________________________________________________________________\n\n### 1. `POST /generate` — request payload\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py` (request handler)\n\nCalled in `areal/engine/sglang_remote.py` (`SGLangBackend.build_generation_request`,\nlines 42–88):\n\n```python\nsample_params = {\n    \"top_p\": gconfig.top_p,\n    \"top_k\": gconfig.top_k,\n    \"max_new_tokens\": gconfig.max_new_tokens,\n    \"temperature\": 0.0 if gconfig.greedy else gconfig.temperature,\n    \"stop_token_ids\": stop_token_ids,\n    \"ignore_eos\": gconfig.ignore_eos,\n    \"skip_special_tokens\": gconfig.skip_special_tokens,\n    \"frequency_penalty\": gconfig.frequency_penalty,\n}\nif stop:\n    sample_params[\"stop\"] = stop\n\npayload = {\n    \"input_ids\": req.input_ids.copy(),\n    \"image_data\": req.image_data,\n    \"sampling_params\": sample_params,\n    \"return_logprob\": True,\n    \"stream\": False,\n}\n# conditionally added: \"return_routed_experts\": True, \"lora_path\": <str>\n```\n\n**Check:** Confirm `input_ids`, `image_data`, `sampling_params`, `return_logprob`,\n`lora_path`, and `return_routed_experts` are still accepted top-level fields. Verify all\n`sampling_params` sub-keys are unchanged (`top_p`, `top_k`, `max_new_tokens`,\n`temperature`, `stop_token_ids`, `ignore_eos`, `skip_special_tokens`,\n`frequency_penalty`, `stop`). Check whether `stream=False` is still the correct way to\nrequest a non-streaming response.\n\n______________________________________________________________________\n\n### 2. `POST /generate` — response parsing\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py` (response schema)\n\nParsed in `areal/engine/sglang_remote.py` (`SGLangBackend.parse_generation_response`,\nlines 90–126):\n\n```python\nmeta_info = response[\"meta_info\"]\nfinish_reason = meta_info[\"finish_reason\"]\nstop_reason = finish_reason[\"type\"]\nstop_message = finish_reason.get(\"message\", \"\")\nrouted_experts = meta_info.get(\"routed_experts\", None)\nif routed_experts is not None:\n    num_sgl_token = meta_info[\"prompt_tokens\"] + meta_info[\"completion_tokens\"] - 1\n    routed_experts = np.frombuffer(\n        pybase64.b64decode(routed_experts.encode(\"utf-8\")), dtype=np.int32\n    ).reshape(num_sgl_token, -1)\noutput_tokens = [x[1] for x in meta_info[\"output_token_logprobs\"]]\noutput_logprobs = [x[0] for x in meta_info[\"output_token_logprobs\"]]\n```\n\n**Check:** Confirm `meta_info` is still a top-level key. Verify `finish_reason` is still\na dict with a `\"type\"` key (not a plain string). Verify `output_token_logprobs` is still\na list of `[logprob, token_id]` pairs. Check that `prompt_tokens` and\n`completion_tokens` are still present when `return_logprob=True`. Confirm\n`routed_experts` is still base64-encoded int32 that can be decoded via\n`pybase64.b64decode` and reshaped to `(num_tokens, num_layers*expert_top_k)`.\n\n______________________________________________________________________\n\n### 3. `POST /load_lora_adapter` — request payload\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py`\n\nCalled in `areal/engine/sglang_remote.py`\n(`SGLangBackend.build_disk_weight_update_requests`, lines 139–144):\n\n```python\nHttpRequest(\n    endpoint=\"/load_lora_adapter\",\n    payload={\"lora_name\": lora_name, \"lora_path\": str(meta.path)},\n)\n```\n\n**Check:** Confirm the endpoint still exists and accepts `lora_name` and `lora_path`.\nCheck whether the endpoint was renamed or merged into another route.\n\n______________________________________________________________________\n\n### 4. `POST /update_weights_from_disk` — request payload\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py`\n\nCalled in `areal/engine/sglang_remote.py`\n(`SGLangBackend.build_disk_weight_update_requests`, lines 148–158):\n\n```python\nHttpRequest(\n    endpoint=\"/update_weights_from_disk\",\n    payload={\n        \"model_path\": str(meta.path),\n        \"abort_all_requests\": True,\n    },\n)\n```\n\n**Check:** Confirm `model_path` and `abort_all_requests` are still valid payload keys.\nCheck whether the endpoint was renamed (e.g., to `/update_weights`).\n\n______________________________________________________________________\n\n### 5. `POST /update_weights_from_distributed` — request payload\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py`\n\nCalled in `areal/engine/sglang_remote.py`\n(`SGLangBackend.build_distributed_weight_update_requests`, lines 173–186):\n\n```python\nHttpRequest(\n    endpoint=\"/update_weights_from_distributed\",\n    payload={\n        \"names\": [pspec.name for pspec in param_specs],\n        \"dtypes\": [pspec.dtype for pspec in param_specs],\n        \"shapes\": [pspec.shape for pspec in param_specs],\n        \"group_name\": meta.nccl_group_name,\n        \"abort_all_requests\": True,\n    },\n)\n```\n\n**Check:** Confirm all five payload keys are unchanged. Verify `dtypes` still accepts\nstring representations of torch dtypes. Check whether `abort_all_requests` is still\nhonored.\n\nAReaL's NEXTN compatibility bridge in\n`areal/v2/inference_service/sglang/mtp_weight_update_bridge.py` additionally depends on\nthe following SGLang 0.5.10.post1 internals:\n\n- `Scheduler.tp_worker.model_runner` is the target `ModelRunner`.\n- Spec v1 exposes the NEXTN draft `ModelRunner` as `Scheduler.draft_worker.model_runner`\n  (`EAGLEWorker`).\n- Spec v2 exposes it as `Scheduler.draft_worker.draft_worker.draft_runner`\n  (`EAGLEWorkerV2` wrapping `EagleDraftWorker`).\n- `ModelRunner._model_update_group`, `ModelRunner.device`, and\n  `ModelRunner.update_weights_from_tensor(named_tensors, load_format)` retain their\n  current semantics.\n- `sglang.srt.weight_sync.tensor_bucket.FlattenedTensorBucket` accepts `named_tensors`,\n  exposes `get_flattened_tensor()`, and reconstructs regular `(name, tensor)` pairs with\n  `reconstruct_tensors()`.\n\n**Check:** Revalidate these private APIs before changing the SGLang pin. The bridge must\nreceive every distributed bucket exactly once, then apply the same named tensor objects\nto draft and target runners. Keep the explicit version guard until this path has been\nrevalidated end to end.\n\n______________________________________________________________________\n\n### 6. `POST /init_weights_update_group` — request payload\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py`\n\nCalled in `areal/engine/sglang_remote.py`\n(`SGLangBackend.build_init_weights_group_request`, lines 188–207):\n\n```python\nHttpRequest(\n    endpoint=\"/init_weights_update_group\",\n    payload={\n        \"master_address\": format_host_for_url(meta.nccl_master_address),\n        \"master_port\": str(meta.nccl_master_port),\n        \"rank_offset\": rank_offset,\n        \"world_size\": gen_parallel.world_size + 1,\n        \"backend\": current_platform.communication_backend,\n        \"group_name\": meta.nccl_group_name,\n    },\n)\n```\n\n**Check:** Confirm all six payload keys are still accepted. Verify `rank_offset`\nsemantics (whether the training-rank offset convention changed). Check `backend`\naccepted values (e.g., `\"nccl\"`, `\"gloo\"`).\n\n______________________________________________________________________\n\n### 7. `POST /pause_generation` and `POST /continue_generation`\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py`\n\nCalled in `areal/engine/sglang_remote.py` (`get_pause_request` line 211,\n`get_resume_request` line 215):\n\n```python\nHttpRequest(endpoint=\"/pause_generation\", payload={})\nHttpRequest(endpoint=\"/continue_generation\", payload={})\n```\n\n**Check:** Confirm both endpoints still exist with these exact names. Check whether they\nnow require any payload fields (e.g., `wait_for_inflight_requests`).\n\n______________________________________________________________________\n\n### 8. `GET /health`\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py`\n\nCalled in `areal/engine/sglang_remote.py` (`get_health_check_request`, line 219):\n\n```python\nHttpRequest(endpoint=\"/health\", payload={}, method=\"GET\")\n```\n\n**Check:** Confirm the endpoint is still a `GET` request and returns a 2xx on success.\nVerify it doesn't require authentication headers.\n\n______________________________________________________________________\n\n### 9. `GET /v1/models` — server readiness poll\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py`\n\nCalled in `areal/infra/launcher/sglang_server.py` (`wait_for_server`, lines 65–84):\n\n```python\nresponse = requests.get(\n    f\"{base_url}/v1/models\",\n    headers={\"Authorization\": \"Bearer None\"},\n)\nif response.status_code == 200:\n    break\n```\n\n**Check:** Confirm `/v1/models` still returns 200 once the server is ready. Check\nwhether the `Authorization: Bearer None` header is still accepted (or if auth\nrequirements changed). If SGLang changes its readiness probe endpoint, update this\npolling logic.\n\n______________________________________________________________________\n\n### 10. `POST /release_memory_occupation` and `POST /resume_memory_occupation`\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py`\n\nCalled in `areal/engine/sglang_remote.py` (`get_offload_request` line 223,\n`get_onload_request` lines 225–234):\n\n```python\n# offload (release GPU memory)\nHttpRequest(endpoint=\"/release_memory_occupation\", payload={})\n\n# onload (resume GPU memory, optional tags for multi-stage resume)\npayload = {\"tags\": tags} if tags is not None else {}\nHttpRequest(endpoint=\"/resume_memory_occupation\", payload=payload)\n```\n\n**Check:** Confirm both endpoints still exist. For `/resume_memory_occupation`, verify\n`tags` is still an optional list of strings (available tags: `\"weights\"`, `\"kv_cache\"`).\nCheck if additional fields are now required for either endpoint.\n\n______________________________________________________________________\n\n### 11. `sglang.srt.entrypoints.openai.protocol.Function` and `.Tool`\n\n**Source:** `python/sglang/srt/entrypoints/openai/protocol.py`\n\nCalled in `areal/experimental/openai/tool_call_parser.py` (`_process_tool_calls_sglang`,\nlines 73–94):\n\n```python\nfrom sglang.srt.entrypoints.openai.protocol import Function as SglFunction\nfrom sglang.srt.entrypoints.openai.protocol import Tool as SglTool\n\nSglTool(\n    type=tool[\"type\"],\n    function=SglFunction(\n        name=tool.get(\"name\"),\n        description=tool.get(\"description\"),\n        parameters=tool.get(\"parameters\"),\n    ),\n)\n# or:\nSglTool(type=tool[\"type\"], function=SglFunction(**tool[\"function\"]))\n```\n\n**Check:** Confirm `Function` and `Tool` are still exported from this module path (not\nmoved to `sglang.srt.protocol` or similar). Verify `Function` still accepts `name`,\n`description`, `parameters`. Verify `Tool` still accepts `type` and `function`. Check\nfor newly required fields.\n\n______________________________________________________________________\n\n### 12. `sglang.srt.function_call.function_call_parser.FunctionCallParser`\n\n**Source:** `python/sglang/srt/function_call/function_call_parser.py`\n\nCalled in `areal/experimental/openai/tool_call_parser.py` (`_process_tool_calls_sglang`,\nlines 96, 105, 109):\n\n```python\nfrom sglang.srt.function_call.function_call_parser import FunctionCallParser\n\nparser_p = FunctionCallParser(tools, tool_call_parser)\n# Public methods called:\nparser_p.has_tool_call(content_text)       # -> bool\ncontent_text, call_info_list = parser_p.parse_non_stream(content_text)\n# call_info_list items expose: call_info.name, call_info.parameters\n```\n\n**Check:** Confirm the module path is unchanged. Verify the constructor still takes\n`(tools, tool_call_parser)` positionally where `tools` is a list of `SglTool` objects.\nConfirm `has_tool_call(text) -> bool` and `parse_non_stream(text) -> (str, list)`\nmethods still exist with the same signatures. Verify returned `call_info` objects still\nexpose `.name` and `.parameters` attributes.\n\n______________________________________________________________________\n\n### 13. `sglang.srt.parser.reasoning_parser.ReasoningParser`\n\n**Source:** `python/sglang/srt/parser/reasoning_parser.py`\n\nCalled in `areal/experimental/openai/tool_call_parser.py` (`_process_tool_calls_sglang`,\nlines 76, 97, 101–102):\n\n```python\nfrom sglang.srt.parser.reasoning_parser import ReasoningParser\n\nreasoning_parser_p = ReasoningParser(reasoning_parser)\n# Accessed attributes:\nreasoning_parser_p.detector.think_start_token\nreasoning_parser_p.detector.think_end_token\n```\n\n**Check:** Confirm the module path is unchanged (not moved to\n`sglang.srt.reasoning_parser`). Verify the constructor still accepts a single parser\nname string. Critically, verify the returned instance still exposes\n`.detector.think_start_token` and `.detector.think_end_token` — these are used to\nextract `<think>` blocks from model output.\n\n______________________________________________________________________\n\n### 14. `SGLangConfig.build_cmd` / CLI flag compatibility\n\n**Source:** `python/sglang/launch_server.py`, `python/sglang/srt/server_args.py`\n\nCalled in `areal/api/cli_args.py` via `SGLangConfig.build_args()` and\n`SGLangConfig.build_cmd()`:\n\n```python\n# build_args() assembles a dict of CLI flags from SGLangConfig fields\n# build_cmd() converts those flags into a subprocess command list\nargs = SGLangConfig.build_args()\n# flags like --model-path, --tp-size, --max-loaded-loras, etc.\n```\n\n**Check:** For each of the ~70 config fields in `SGLangConfig`, confirm the\ncorresponding `sglang` CLI flag still exists with the same name and accepted values. Pay\nspecial attention to any fields that were added in recent releases (e.g.,\n`max_loaded_loras` added in 0.4.10.post2). Check `python/sglang/srt/server_args.py` for\nadded/removed/renamed `argparse` arguments.\n\n______________________________________________________________________\n\n### 15. `Engine._launch_subprocesses` — server lifecycle\n\n**Source:** `python/sglang/srt/entrypoints/engine.py`\n\nCalled in `areal/v2/inference_service/sglang/launch_server.py` (`areal_launch_server`):\n\n```python\nfrom sglang.srt.entrypoints.engine import Engine, init_tokenizer_manager\n\n(\n    tokenizer_manager,\n    template_manager,\n    port_args,\n    scheduler_init_result,\n    subprocess_watchdog,\n) = Engine._launch_subprocesses(\n    server_args=server_args,\n    init_tokenizer_manager_func=init_tokenizer_manager,\n    run_scheduler_process_func=areal_run_scheduler_process,\n    run_detokenizer_process_func=run_detokenizer_process,\n)\n```\n\n**Check:** Confirm `_launch_subprocesses` is still a classmethod. Verify it still\naccepts `run_scheduler_process_func` as a callable parameter for scheduler process\ninjection. Verify the return tuple is\n`(TokenizerManager, TemplateManager, PortArgs, SchedulerInitResult, SubprocessWatchdog)`.\nConfirm `SchedulerInitResult.scheduler_infos` attribute exists.\n\n______________________________________________________________________\n\n### 16. `_setup_and_run_http_server` — HTTP server bootstrap\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py`\n\nCalled in `areal/v2/inference_service/sglang/launch_server.py` (`areal_launch_server`):\n\n```python\nfrom sglang.srt.entrypoints.http_server import (\n    _execute_server_warmup,\n    _setup_and_run_http_server,\n    app,\n)\n\n_setup_and_run_http_server(\n    server_args,\n    tokenizer_manager,\n    template_manager,\n    port_args,\n    scheduler_init_result.scheduler_infos,\n    subprocess_watchdog,\n    execute_warmup_func=_execute_server_warmup,\n)\n```\n\n**Check:** Confirm the function still exists at this module path. Verify signature:\n`(server_args, tokenizer_manager, template_manager, port_args, scheduler_infos, subprocess_watchdog, execute_warmup_func, launch_callback)`.\nConfirm `app` is still the FastAPI instance exported from this module.\n\n______________________________________________________________________\n\n### 17. `Scheduler` constructor and `configure_scheduler`\n\n**Source:** `python/sglang/srt/managers/scheduler.py`\n\nCalled in `areal/v2/inference_service/sglang/scheduler.py`\n(`areal_run_scheduler_process`):\n\n```python\nfrom sglang.srt.managers.scheduler import Scheduler, configure_scheduler\n\ndp_rank = configure_scheduler(\n    server_args, tp_rank, attn_cp_rank, moe_dp_rank, moe_ep_rank, pp_rank, dp_rank\n)\n\nscheduler = Scheduler(\n    server_args, port_args, gpu_id, tp_rank, moe_ep_rank, pp_rank,\n    attn_cp_rank, moe_dp_rank, dp_rank,\n)\npipe_writer.send(scheduler.get_init_info())\nscheduler.run_event_loop()\n```\n\n**Check:** Confirm `configure_scheduler` signature is\n`(server_args, tp_rank, attn_cp_rank, moe_dp_rank, moe_ep_rank, pp_rank, dp_rank)`.\nConfirm `Scheduler.__init__` signature is\n`(server_args, port_args, gpu_id, tp_rank, moe_ep_rank, pp_rank, attn_cp_rank, moe_dp_rank, dp_rank)`.\nVerify `get_init_info()` and `run_event_loop()` still exist. Confirm `tp_rank`,\n`dp_rank`, `tp_cpu_group`, `tp_size` attributes are present on the scheduler instance\n(used by `AwexSchedulerBridge`).\n\n______________________________________________________________________\n\n### 18. `RpcReqInput` / `RpcReqOutput` — scheduler RPC protocol\n\n**Source:** `python/sglang/srt/managers/io_struct.py`\n\nUsed in `areal/v2/inference_service/sglang/rpc_proxy.py`:\n\n```python\nfrom sglang.srt.managers.io_struct import RpcReqInput, RpcReqOutput\n\nreq = RpcReqInput(method=method, parameters=kwargs if kwargs else None)\nself._rpc_socket.send_pyobj(req)\nresp: RpcReqOutput = self._rpc_socket.recv_pyobj()\n# resp.success: bool, resp.message: str\n```\n\n**Check:** Confirm `RpcReqInput` still has `method: str` and\n`parameters: Optional[Dict]` fields. Confirm `RpcReqOutput` still has `success: bool`\nand `message: str` fields. Verify `send_pyobj`/`recv_pyobj` serialization still works\n(both inherit from `BaseReq` which is a dataclass).\n\n______________________________________________________________________\n\n### 19. `sglang.srt.utils` module reorganization (v0.5.10+)\n\n**Source:** `python/sglang/srt/utils/__init__.py`, `python/sglang/srt/utils/common.py`,\n`python/sglang/srt/utils/network.py`, `python/sglang/srt/utils/numa_utils.py`\n\nIn v0.5.10+, `sglang.srt.utils/__init__.py` only re-exports from `common.py` via\n`from sglang.srt.utils.common import *`. Functions in submodules must be imported\ndirectly:\n\n- `get_zmq_socket` → `sglang.srt.utils.network`\n- `get_numa_node_if_available`, `numa_bind_to_node` → `sglang.srt.utils.numa_utils`\n- `kill_itself_when_parent_died`, `get_bool_env_var`, `set_gpu_proc_affinity`,\n  `kill_process_tree` → `sglang.srt.utils` (via `common.py`)\n\n**Check:** On each upgrade, verify `sglang.srt.utils/__init__.py` re-export scope. If\nfunctions move between submodules, update AReaL import paths accordingly.\n\n______________________________________________________________________\n\n## Version-Guarded Code\n\n- `areal/api/cli_args.py:1877-1878` —\n  `if not pkg_version.is_version_greater_or_equal(\"sglang\", \"0.5.10.post1\"): raise RuntimeError(...)`.\n  Minimum floor raised from `0.4.9.post2` to `0.5.10.post1` during this upgrade. The\n  legacy `max_loaded_loras` version guard (`<0.4.10.post2`) was removed because the new\n  floor is well past that version.\n","frontmatter":{"package":"sglang","github":"sgl-project/sglang","branch_template":"v${VERSION}","upstream_paths":["python/sglang/srt/entrypoints/engine.py","python/sglang/srt/entrypoints/http_server.py","python/sglang/srt/entrypoints/openai/protocol.py","python/sglang/srt/function_call/function_call_parser.py","python/sglang/srt/function_call/core_types.py","python/sglang/srt/managers/scheduler.py","python/sglang/srt/managers/io_struct.py","python/sglang/srt/parser/reasoning_parser.py","python/sglang/srt/server_args.py","python/sglang/srt/utils/__init__.py","python/sglang/srt/utils/common.py","python/sglang/srt/utils/network.py","python/sglang/srt/utils/numa_utils.py","python/sglang/launch_server.py"]},"isInternal":false,"tokens":5865,"sizeBytes":27398},{"name":"torchao.md","path":".agents/skills/upgrade-deps/checklists/torchao.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.agents/skills/upgrade-deps/checklists/torchao.md","title":"Checklists Skill","category":"anthropic-skill","format":"markdown","content":"---\npackage: torchao\ngithub: pytorch/ao\nbranch_template: v${VERSION}\nupstream_paths:\n  - torchao/prototype/blockwise_fp8_training/linear.py\n  - torchao/prototype/blockwise_fp8_training/kernels.py\n---\n\n## Affected Files\n\n### Primary (engine layer — most likely to break)\n\n| File                                                      | Imports / Usage                                                                                                                        |\n| --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |\n| `areal/experimental/models/archon/fp8.py`                 | `torchao.prototype.blockwise_fp8_training.linear.fp8_blockwise_mm`; `fp8_blockwise_mm.apply(x, weight, block_size, dtype, use_triton)` |\n| `areal/experimental/models/archon/moe/grouped_experts.py` | `torchao.prototype.blockwise_fp8_training.linear.fp8_blockwise_mm`; `fp8_blockwise_mm.apply(x, w, block_size, dtype, use_triton)`      |\n\n### Secondary (model / infra layer)\n\n| File                                         | Imports / Usage                                                                             |\n| -------------------------------------------- | ------------------------------------------------------------------------------------------- |\n| `areal/api/cli_args.py`                      | `ArchonFP8Config` dataclass — string references to `torchao_config` field; no direct import |\n| `areal/tools/check_pyproject_consistency.py` | `torchao` listed in `ESCAPABLE_PACKAGES` — expected to differ between sglang/vllm variants  |\n\n### Tertiary (tests, config)\n\n| File                                                     | Imports / Usage                                                                                                                                                                                                           |\n| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `tests/experimental/archon/conftest.py`                  | Conditional stub of 5 modules: `torchao`, `torchao.quantization`, `torchao.prototype`, `torchao.prototype.blockwise_fp8_training`, `torchao.prototype.blockwise_fp8_training.linear` when unavailable; SM90+ GPU required |\n| `tests/experimental/archon/fp8/test_fp8_linear.py`       | `import torchao.prototype.blockwise_fp8_training.linear`; forward/backward correctness and convergence tests                                                                                                              |\n| `tests/experimental/archon/fp8/test_fp8_scale_layout.py` | `torchao.prototype.blockwise_fp8_training.kernels.triton_fp8_blockwise_act_quant_lhs`, `triton_fp8_blockwise_weight_quant_transposed_rhs`                                                                                 |\n| `tests/experimental/archon/fp8/test_conversion.py`       | Tests `enable_fp8_linear()` and `enable_fp8_experts()` patching logic                                                                                                                                                     |\n| `tests/experimental/archon/fp8/test_moe_dispatch.py`     | Tests `_run_experts_fp8_for_loop()` with 11 edge cases                                                                                                                                                                    |\n\n______________________________________________________________________\n\n## API Usage Catalog\n\nFor each function/class below, verify the call signature against the upstream source at\nthe target version. Focus on: **missing new required parameters**, **removed old\nparameters**, **renamed parameters**, **changed return types**, **changed method\nsignatures on returned objects**, and **moved/renamed modules**.\n\n> **Warning:** All torchao usage goes through the `prototype` module path. These APIs\n> are explicitly experimental and carry a HIGH risk of breakage between releases.\n\n### 1. `torchao.prototype.blockwise_fp8_training.linear.fp8_blockwise_mm` (HIGH RISK)\n\n**Source:** `torchao/prototype/blockwise_fp8_training/linear.py`\n\nImported inside `areal/experimental/models/archon/fp8.py` `enable_fp8_linear()` (line\n36\\) and stored on each patched module as `mod._fp8_mm`. Called in the patched forward\n(line 156-157):\n\n```python\nfrom torchao.prototype.blockwise_fp8_training.linear import fp8_blockwise_mm\n\n# Inside patched nn.Linear.forward — on-the-fly FP8 quantization\nout = self._fp8_mm.apply(x, weight, self._fp8_block, x.dtype, self._fp8_use_triton)\n# Positional args: (input, weight, block_size=128, dtype, use_triton)\n```\n\nImported inside `areal/experimental/models/archon/moe/grouped_experts.py`\n`_run_experts_fp8_for_loop()` (line 132). Called three times per expert (lines 161-166):\n\n```python\nfrom torchao.prototype.blockwise_fp8_training.linear import fp8_blockwise_mm\n\n# SwiGLU: silu(x @ w1.T) * (x @ w3.T) @ w2.T — all via FP8\nh1 = fp8_blockwise_mm.apply(x_e, w1_e, block_size, x_e.dtype, use_triton)\nh3 = fp8_blockwise_mm.apply(x_e, w3_e, block_size, x_e.dtype, use_triton)\nh = F.silu(h1) * h3\nh2 = fp8_blockwise_mm.apply(h, w2_e, block_size, h.dtype, use_triton)\n```\n\n**Check:** Confirm `fp8_blockwise_mm` is still a `torch.autograd.Function` subclass with\nan `.apply()` entry point. Verify positional argument order\n`(x, weight, block_size, dtype, use_triton)` is unchanged. Check whether `block_size`\nstill accepts an `int` (default 128) and `use_triton` remains a plain `bool`. Confirm\nthe module path `torchao.prototype.blockwise_fp8_training.linear` has not been renamed\nor graduated out of `prototype`.\n\n______________________________________________________________________\n\n### 2. `enable_fp8_linear()` (HIGH RISK — monkey-patches `nn.Linear`)\n\n**Source:** `areal/experimental/models/archon/fp8.py` (AReaL function, not torchao)\n\nDefined at line 18, calls `fp8_blockwise_mm` from torchao internally:\n\n```python\ndef enable_fp8_linear(\n    model: nn.Module,\n    *,\n    exclude_fqns: set[str] | None = None,  # defaults to {\"output\", \"router\", \"score\"}\n    use_triton: bool = True,\n) -> None:\n    from torchao.prototype.blockwise_fp8_training.linear import fp8_blockwise_mm\n    # ... patches eligible nn.Linear modules via _patch_fp8_forward(mod, fp8_blockwise_mm, use_triton)\n```\n\nCalled from test at `tests/experimental/archon/fp8/test_fp8_scale_layout.py` (line 46):\n\n```python\nenable_fp8_linear(model, exclude_fqns=set())\n```\n\n**Check:** This is an AReaL function, but it depends on `fp8_blockwise_mm` from torchao\n(entry 1). If torchao renames or moves `fp8_blockwise_mm`, this function's internal\nimport breaks. Verify `exclude_fqns` and `use_triton` are the only kwargs (no\n`block_size` or `dtype` — block size is hardcoded as `_FP8_BLOCK = 128`).\n\n______________________________________________________________________\n\n### 3. `enable_fp8_experts()` (HIGH RISK — monkey-patches MoE expert modules)\n\n**Source:** `areal/experimental/models/archon/fp8.py` (AReaL function, not torchao)\n\nDefined at line 57:\n\n```python\ndef enable_fp8_experts(model: nn.Module, *, use_triton: bool = True) -> None:\n    # Patches each GroupedExperts module to use _run_experts_fp8_for_loop()\n    # which internally calls fp8_blockwise_mm from torchao\n```\n\n**Check:** This is an AReaL function. Its only torchao dependency is transitive via\n`_run_experts_fp8_for_loop()` → `fp8_blockwise_mm` (entry 1). Verify `use_triton` is the\nonly kwarg (no `block_size` or `dtype` — block size is hardcoded as `_FP8_BLOCK = 128`).\nConfirm it targets `GroupedExperts` submodules specifically.\n\n______________________________________________________________________\n\n### 4. `validate_fp8_shard_alignment()`\n\n**Source:** `areal/experimental/models/archon/fp8.py` (AReaL function, not torchao)\n\nDefined at line 166:\n\n```python\ndef validate_fp8_shard_alignment(\n    model_parts: list[nn.Module],\n    block_size: int = 128,  # _FP8_BLOCK\n) -> None:\n    # Checks both nn.Linear (2D) and GroupedExperts (3D) modules\n    # for block-alignment AFTER parallelism (TP/PP) is applied.\n    # Raises ValueError if local weight dims are not multiples of block_size.\n```\n\n**Check:** This is an AReaL function. It does NOT depend on torchao directly — it only\nchecks that modules already patched with `_fp8_block` attribute have compatible local\nweight shapes. However, if torchao changes `_FP8_BLOCK` convention or the kernel's\nalignment requirements, this validation must be updated to match.\n\n______________________________________________________________________\n\n### 5. `triton_fp8_blockwise_act_quant_lhs` / `triton_fp8_blockwise_weight_quant_transposed_rhs` (HIGH RISK)\n\n**Source:** `torchao/prototype/blockwise_fp8_training/kernels.py`\n\nImported in `tests/experimental/archon/fp8/test_fp8_scale_layout.py` (lines 89-92,\n101-104):\n\n```python\nfrom torchao.prototype.blockwise_fp8_training.kernels import (\n    triton_fp8_blockwise_act_quant_lhs,\n    triton_fp8_blockwise_weight_quant_transposed_rhs,\n)\n\n# Call signatures (lines 101-104):\nx_fp8, x_scale = triton_fp8_blockwise_act_quant_lhs(x, block_size)\nw_t_fp8, w_t_scale = triton_fp8_blockwise_weight_quant_transposed_rhs(w, block_size=block_size)\n\n# Scale shape contract tested:\n# x_scale.shape == (m, k_blocks)\n# w_t_scale.shape == (k_blocks, n_blocks)\n# Both MUST be column-major: stride(0) < stride(1)\n```\n\n**Check:** Confirm both kernel symbols still exist at this path and have not been\nrenamed (e.g., to drop the `triton_` prefix or unify under a single dispatch function).\nVerify return types are `(fp8_tensor, scale_tensor)` tuples. Verify the scale layout\nconvention: column-major strides (`stride(0) < stride(1)`), shape\n`(rows, ceil(cols/block_size))`. Any change in output tensor memory layout silently\nbreaks `fp8_blockwise_mm` which consumes these scales.\n\n______________________________________________________________________\n\n## Version-Guarded Code\n\nNo version-guarded code exists in AReaL for `torchao`. Availability is checked at test\ntime via `try/except` stubs in `tests/experimental/archon/conftest.py` — the stubs cover\nall five affected submodules so tests degrade gracefully on machines without SM90+ GPUs\nor without `torchao` installed.\n","frontmatter":{"package":"torchao","github":"pytorch/ao","branch_template":"v${VERSION}","upstream_paths":["torchao/prototype/blockwise_fp8_training/linear.py","torchao/prototype/blockwise_fp8_training/kernels.py"]},"isInternal":false,"tokens":2314,"sizeBytes":10503},{"name":"transformers.md","path":".agents/skills/upgrade-deps/checklists/transformers.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.agents/skills/upgrade-deps/checklists/transformers.md","title":"Checklists Skill","category":"anthropic-skill","format":"markdown","content":"---\npackage: transformers\ngithub: huggingface/transformers\nbranch_template: v${VERSION}\nupstream_paths:\n  - src/transformers/models/auto/auto_factory.py\n  - src/transformers/models/auto/configuration_auto.py\n  - src/transformers/configuration_utils.py\n  - src/transformers/tokenization_utils_fast.py\n  - src/transformers/tokenization_utils_base.py\n  - src/transformers/processing_utils.py\n  - src/transformers/optimization.py\n  - src/transformers/modeling_flash_attention_utils.py\n  - src/transformers/integrations/flash_attention.py\n  - src/transformers/models/qwen2_vl/modeling_qwen2_vl.py\n  - src/transformers/models/qwen2_5_vl/\n  - src/transformers/models/qwen3_vl/modeling_qwen3_vl.py\n  - src/transformers/utils/import_utils.py\n---\n\n## Affected Files\n\n### Primary (engine layer — most likely to break)\n\n| File                                         | Imports / Usage                                                                                                                                                                                                                                 |\n| -------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `areal/engine/fsdp_engine.py`                | `AutoConfig`, `AutoModelForCausalLM`, `AutoModelForImageTextToText`, `AutoModelForTokenClassification`, `PretrainedConfig`, `PreTrainedTokenizerFast`, `ProcessorMixin`, `get_linear_schedule_with_warmup`, `get_constant_schedule_with_warmup` |\n| `areal/engine/megatron_engine.py`            | `PretrainedConfig`                                                                                                                                                                                                                              |\n| `areal/engine/fsdp_utils/__init__.py`        | `PreTrainedModel`                                                                                                                                                                                                                               |\n| `areal/engine/fsdp_utils/parallel.py`        | `PretrainedConfig`                                                                                                                                                                                                                              |\n| `areal/experimental/engine/archon_engine.py` | `AutoConfig`, `PretrainedConfig`, `PreTrainedTokenizerFast`                                                                                                                                                                                     |\n\n### Secondary (model / infra layer — HIGH RISK: monkey-patching)\n\n| File                                                                       | Imports / Usage                                                                                                                                                                                                                                                    |\n| -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `areal/models/transformers/ulyssess_patch.py`                              | `transformers.modeling_flash_attention_utils._flash_attention_forward` (direct import); `transformers.integrations.flash_attention._flash_attention_forward` (monkey-patch); dynamic import of `transformers.models.{qwen2_vl,qwen2_5_vl,qwen3_vl}` module strings |\n| `areal/models/transformers/qwen3_vl.py`                                    | `transformers.integrations.flash_attention.flash_attention_forward`; `transformers.models.qwen3_vl.modeling_qwen3_vl.{apply_rotary_pos_emb, repeat_kv}`                                                                                                            |\n| `areal/models/transformers/qwen2_vl.py`                                    | `transformers.integrations.flash_attention.flash_attention_forward`; `transformers.models.qwen2_vl.modeling_qwen2_vl.{apply_multimodal_rotary_pos_emb, repeat_kv}`                                                                                                 |\n| `areal/models/transformers/vision_sp_shard.py`                             | monkey-patches Qwen VL vision modules (internal submodule access)                                                                                                                                                                                                  |\n| `areal/models/tree_attn/module_fsdp.py`                                    | `transformers.integrations.flash_attention._flash_attention_forward` (monkey-patch save/restore)                                                                                                                                                                   |\n| `areal/workflow/rlvr.py`                                                   | `PreTrainedTokenizerFast` (`decode`, `apply_chat_template`, `eos_token_id`)                                                                                                                                                                                        |\n| `areal/workflow/vision_rlvr.py`                                            | `AutoProcessor`, `PreTrainedTokenizerFast`                                                                                                                                                                                                                         |\n| `areal/workflow/multi_turn.py`                                             | `PreTrainedTokenizerFast`                                                                                                                                                                                                                                          |\n| `areal/utils/hf_utils.py`                                                  | `AutoTokenizer.from_pretrained()`, `AutoProcessor.from_pretrained()`                                                                                                                                                                                               |\n| `areal/utils/seeding.py`                                                   | `transformers.set_seed()`                                                                                                                                                                                                                                          |\n| `areal/infra/platforms/__init__.py`                                        | `transformers.utils.import_utils.is_torch_npu_available`                                                                                                                                                                                                           |\n| `areal/infra/rpc/serialization.py`                                         | `AutoTokenizer`, `PreTrainedTokenizer`, `PreTrainedTokenizerFast`, `AutoProcessor`, `ProcessorMixin`                                                                                                                                                               |\n| `areal/models/mcore/registry.py`                                           | `AutoConfig`, `PretrainedConfig`                                                                                                                                                                                                                                   |\n| `areal/models/mcore/bailing_moe.py`                                        | `PretrainedConfig`                                                                                                                                                                                                                                                 |\n| `areal/experimental/models/archon/*/args.py` and `*/state_dict_adapter.py` | `PretrainedConfig`                                                                                                                                                                                                                                                 |\n\n### Tertiary (tests and examples)\n\n| File                                     | Imports / Usage                                                                                                                    |\n| ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |\n| 30+ files under `tests/` and `examples/` | `AutoTokenizer`, `AutoConfig`, `AutoModelForCausalLM`, `PreTrainedTokenizerFast` — standard load patterns only, no monkey-patching |\n\n______________________________________________________________________\n\n## API Usage Catalog\n\nFor each function/class below, verify the call signature against the upstream source at\nthe target version. Focus on: **missing new required parameters**, **removed old\nparameters**, **renamed parameters**, **changed return types**, **changed method\nsignatures on returned objects**, and **moved/renamed modules**.\n\n### 1. Auto\\* model and config classes\n\n**Source:** `src/transformers/models/auto/auto_factory.py`,\n`src/transformers/models/auto/configuration_auto.py`\n\nCalled in `areal/engine/fsdp_engine.py`:\n\n```python\n# Config loading (line 196)\nself.model_config = AutoConfig.from_pretrained(\n    pretrained_model_name_or_path=self.config.path,\n    trust_remote_code=True,\n)\n\n# VLM path (line 899)\nmodel = AutoModelForImageTextToText.from_pretrained(\n    pretrained_model_name_or_path=self.config.path,\n    trust_remote_code=True,\n    dtype=dtype,\n    attn_implementation=self.config.attn_impl,\n)\n\n# LLM path — _create_llm_actor_or_critic (lines 829–856)\nmodel_class = AutoModelForTokenClassification if is_critic else AutoModelForCausalLM\nmodel_kwargs = {\"num_labels\": 1} if is_critic else {}\nmodel_kwargs.update({\"dtype\": dtype, \"attn_implementation\": self.config.attn_impl})\n\n# Branch: from_config (meta-device, memory-efficient) or from_pretrained\nmodel = model_class.from_config(self.model_config, **model_kwargs)\n# or:\nmodel = model_class.from_pretrained(\n    pretrained_model_name_or_path=self.config.path,\n    trust_remote_code=True,\n    **model_kwargs,\n)\n```\n\nAlso called in `areal/utils/hf_utils.py`, `areal/models/mcore/registry.py`,\n`areal/experimental/engine/archon_engine.py`, and\n`areal/experimental/models/archon/*/args.py`.\n\n**Check:** Verify `from_pretrained` / `from_config` keyword arguments (`dtype`,\n`attn_implementation`, `trust_remote_code`, `num_labels`). Check whether\n`AutoModelForImageTextToText` still exists or was renamed/merged. Confirm `from_config`\nstill accepts `num_labels` for `AutoModelForTokenClassification`. Look for new required\nkwargs introduced in the target version.\n\n______________________________________________________________________\n\n### 2. `PretrainedConfig`\n\n**Source:** `src/transformers/configuration_utils.py`\n\nCalled in `areal/engine/fsdp_engine.py`, `areal/engine/megatron_engine.py`,\n`areal/engine/fsdp_utils/parallel.py`, `areal/experimental/engine/archon_engine.py`,\n`areal/models/mcore/registry.py`, and various\n`areal/experimental/models/archon/*/args.py`:\n\n```python\n# Type annotation / isinstance check only\nconfig: PretrainedConfig\n\n# Attribute access (common patterns)\nconfig.model_type\nconfig.num_attention_heads\nconfig.num_key_value_heads\nconfig.text_config.num_attention_heads   # VLM sub-config\n```\n\n**Check:** Confirm `PretrainedConfig` is still the base class at this path (not moved).\nCheck whether `text_config` sub-config attribute access pattern for VLMs is still valid.\nVerify no new required fields were added to `__init__`.\n\n______________________________________________________________________\n\n### 3. `PreTrainedTokenizerFast` and tokenizer methods\n\n**Source:** `src/transformers/tokenization_utils_fast.py`,\n`src/transformers/tokenization_utils_base.py`\n\nCalled in `areal/utils/hf_utils.py` (`load_hf_tokenizer`, lines 12–30):\n\n```python\n@lru_cache(maxsize=8)\ndef load_hf_tokenizer(model_name_or_path, fast_tokenizer=True, padding_side=None):\n    tokenizer = transformers.AutoTokenizer.from_pretrained(\n        model_name_or_path,\n        fast_tokenizer=fast_tokenizer,\n        trust_remote_code=True,\n        force_download=True,\n        **kwargs,  # padding_side if not None\n    )\n    if tokenizer.pad_token_id is None:\n        tokenizer.pad_token_id = tokenizer.eos_token_id\n```\n\nCalled in `areal/workflow/rlvr.py`, `areal/workflow/multi_turn.py`, and\n`areal/workflow/vision_rlvr.py`:\n\n```python\ntokenizer.decode(token_ids, ...)\ntokenizer.apply_chat_template(messages, ...)\ntokenizer.eos_token_id\n```\n\n**Check:** Verify `fast_tokenizer` parameter still exists in\n`AutoTokenizer.from_pretrained` (it was renamed to `use_fast` in some versions — confirm\nwhich name the current version uses). Confirm `apply_chat_template` signature is\nunchanged (especially `tokenize`, `add_generation_prompt`, `return_tensors` kwargs).\nCheck `force_download` is still accepted. Confirm `eos_token_id` and `pad_token_id` are\nstill plain attributes. Note: the function is `@lru_cache`'d — if the return type\nchanges, the cache must be invalidated.\n\n______________________________________________________________________\n\n### 4. `AutoProcessor` / `ProcessorMixin`\n\n**Source:** `src/transformers/processing_utils.py`\n\nCalled in `areal/utils/hf_utils.py` (`load_hf_processor_and_tokenizer`, lines 33–55):\n\n```python\nprocessor = transformers.AutoProcessor.from_pretrained(\n    model_name_or_path,\n    trust_remote_code=True,\n    force_download=True,\n    use_fast=True,\n)\n```\n\nAlso used as type annotation in `areal/infra/rpc/serialization.py` and\n`areal/engine/fsdp_engine.py` (`ProcessorMixin`).\n\n**Check:** Confirm `AutoProcessor.from_pretrained` still accepts `use_fast`. Verify\n`ProcessorMixin` is still the importable base class at `transformers.ProcessorMixin`.\nCheck for signature changes on processor call methods (image/text preprocessing) if\n`vision_rlvr.py` calls them directly.\n\n______________________________________________________________________\n\n### 5. LR scheduler helpers\n\n**Source:** `src/transformers/optimization.py`\n\nCalled in `areal/engine/fsdp_engine.py` (lines 1017–1026):\n\n```python\n# \"linear\" scheduler (line 1017)\nself.lr_scheduler = get_linear_schedule_with_warmup(\n    self.optimizer,\n    num_warmup_steps,\n    total_train_steps,\n)\n\n# \"constant\" scheduler (line 1023)\nself.lr_scheduler = get_constant_schedule_with_warmup(\n    self.optimizer,\n    num_warmup_steps,\n)\n```\n\n**Check:** Confirm both functions still exist in `transformers.optimization` (they have\noccasionally been soft-deprecated in favor of `get_scheduler`). Verify positional\nargument order (`optimizer`, `num_warmup_steps`, `num_training_steps`) is unchanged.\nCheck return type is still a `LambdaLR`-compatible scheduler.\n\n______________________________________________________________________\n\n### 6. `transformers.set_seed`\n\n**Source:** `src/transformers/trainer_utils.py` (re-exported from top-level)\n\nCalled in `areal/utils/seeding.py` (line 28):\n\n```python\nimport transformers\ntransformers.set_seed(seed)\n```\n\n**Check:** Confirm `set_seed` is still exported at `transformers.set_seed` (top-level\nre-export). Verify signature is `set_seed(seed: int)` with no new required parameters.\n\n______________________________________________________________________\n\n### 7. `transformers.modeling_flash_attention_utils._flash_attention_forward` (HIGH RISK)\n\n**Source:** `src/transformers/modeling_flash_attention_utils.py`\n\nCalled in `areal/models/transformers/ulyssess_patch.py` (import line 6, usage line 57):\n\n```python\nfrom transformers.modeling_flash_attention_utils import _flash_attention_forward\n\n# Called inside _ulysses_flash_attention_forward as a pass-through (line 57):\nattn_output = _flash_attention_forward(\n    query_states, key_states, value_states, *args, **kwargs,\n)\n```\n\n**Check:** This is a **private** function (leading underscore). Verify it still exists\nat this exact import path. Confirm its positional signature\n`(query_states, key_states, value_states, ...)` is unchanged. Any argument reorder or\nrename breaks all Ulysses sequence parallel attention paths. Note: this is used as the\n_real_ flash attention call inside the Ulysses wrapper — it's the only place the actual\nflash attention kernel is invoked.\n\n______________________________________________________________________\n\n### 8. `transformers.integrations.flash_attention._flash_attention_forward` (HIGH RISK — monkey-patched)\n\n**Source:** `src/transformers/integrations/flash_attention.py`\n\nMonkey-patched in `areal/models/transformers/ulyssess_patch.py` (lines 244–246) and\n`areal/models/tree_attn/module_fsdp.py` (lines 163–166, 182–185):\n\n```python\n# ulyssess_patch.py (line 244) — for non-VLM models\nfrom transformers.integrations import flash_attention\nflash_attention._flash_attention_forward = _ulysses_flash_attention_forward\n\n# module_fsdp.py (lines 163-166) — save and replace for tree attention\nORIGINAL_FLASH_ATTENTION_FORWARD = flash_attention._flash_attention_forward\nflash_attention._flash_attention_forward = _tree_attn_fwd_func\n\n# module_fsdp.py (lines 182-185) — restore original after use\nflash_attention._flash_attention_forward = ORIGINAL_FLASH_ATTENTION_FORWARD\nORIGINAL_FLASH_ATTENTION_FORWARD = None\n```\n\n**Check:** Confirm `transformers.integrations.flash_attention` module still exists and\nstill has a `_flash_attention_forward` attribute. If the module was renamed, moved, or\nthe function removed, ALL of the Ulysses and tree-attention patches silently break. Also\nverify `flash_attention_forward` (public, **no** underscore) still exists — it is\nimported directly in `areal/models/transformers/qwen2_vl.py` (line 7) and\n`areal/models/transformers/qwen3_vl.py` (line 12) via\n`from transformers.integrations.flash_attention import flash_attention_forward`.\n\n______________________________________________________________________\n\n### 9. Qwen2-VL internal symbols (HIGH RISK — imported from private modules)\n\n**Source:** `src/transformers/models/qwen2_vl/modeling_qwen2_vl.py`\n\nImported in `areal/models/transformers/qwen2_vl.py` (lines 8–11):\n\n```python\nfrom transformers.models.qwen2_vl.modeling_qwen2_vl import (\n    apply_multimodal_rotary_pos_emb,\n    repeat_kv,\n)\n```\n\nAlso accessed by module string in `areal/models/transformers/ulyssess_patch.py` (lines\n187–193):\n\n```python\n\"module\": \"transformers.models.qwen2_vl.modeling_qwen2_vl\",\n\"attn_class\": \"Qwen2VLAttention\",\n\"model_class\": \"Qwen2VLTextModel\",\n\"patch_module\": \"areal.models.transformers.qwen2_vl\",\n\"patch_attn_func\": \"ulysses_flash_attn_forward\",\n```\n\nThe patch replaces `Qwen2VLAttention.forward` at line 229:\n`attn_class.forward = patch_attn_func`.\n\n**Check:** Confirm `apply_multimodal_rotary_pos_emb` and `repeat_kv` are still exported\nfrom this submodule (they are internal helpers, not public API). Confirm\n`Qwen2VLAttention` and `Qwen2VLTextModel` class names are unchanged. Verify the patch\ntarget `Qwen2VLAttention.forward` still accepts the same signature as\n`areal/models/transformers/qwen2_vl.py:ulysses_flash_attn_forward`.\n\n______________________________________________________________________\n\n### 10. Qwen2.5-VL internal symbols (HIGH RISK — imported by module string)\n\n**Source:** `src/transformers/models/qwen2_5_vl/`\n\nAccessed by module string in `areal/models/transformers/ulyssess_patch.py` (lines\n180–186):\n\n```python\n\"module\": \"transformers.models.qwen2_5_vl.modeling_qwen2_5_vl\",\n\"attn_class\": \"Qwen2_5_VLAttention\",\n\"model_class\": \"Qwen2_5_VLTextModel\",\n\"patch_module\": \"areal.models.transformers.qwen2_vl\",\n\"patch_attn_func\": \"ulysses_flash_attn_forward\",\n```\n\nNote: Qwen2.5-VL reuses the **same** patch function from `qwen2_vl.py` (not a separate\nfile).\n\n**Check:** Confirm the submodule path\n`transformers.models.qwen2_5_vl.modeling_qwen2_5_vl` is unchanged. Confirm class names\n`Qwen2_5_VLAttention` and `Qwen2_5_VLTextModel` still exist. Verify\n`Qwen2_5_VLAttention.forward` has the same signature as `Qwen2VLAttention.forward`\n(since they share the same patch function).\n\n______________________________________________________________________\n\n### 11. Qwen3-VL internal symbols (HIGH RISK — imported from private modules)\n\n**Source:** `src/transformers/models/qwen3_vl/modeling_qwen3_vl.py`\n\nImported in `areal/models/transformers/qwen3_vl.py` (lines 13–16):\n\n```python\nfrom transformers.models.qwen3_vl.modeling_qwen3_vl import (\n    apply_rotary_pos_emb,\n    repeat_kv,\n)\n```\n\nAlso imports `flash_attention_forward` (public, no underscore) at line 12:\n\n```python\nfrom transformers.integrations.flash_attention import flash_attention_forward\n```\n\nAccessed by module string in `areal/models/transformers/ulyssess_patch.py` (lines\n194–200):\n\n```python\n\"module\": \"transformers.models.qwen3_vl.modeling_qwen3_vl\",\n\"attn_class\": \"Qwen3VLTextAttention\",\n\"model_class\": \"Qwen3VLTextModel\",\n\"patch_module\": \"areal.models.transformers.qwen3_vl\",\n\"patch_attn_func\": \"ulysses_flash_attn_forward\",\n```\n\nNote: Qwen3-VL has its **own** patch function (different from Qwen2-VL) with this\nsignature (line 25):\n\n```python\ndef ulysses_flash_attn_forward(\n    self, hidden_states, position_embeddings, attention_mask=None,\n    past_key_values=None, cache_position=None, **kwargs,\n) -> tuple[torch.Tensor, torch.Tensor | None]:\n```\n\n**Check:** Confirm `apply_rotary_pos_emb` and `repeat_kv` still exist at this path.\nConfirm `Qwen3VLTextAttention` and `Qwen3VLTextModel` class names are unchanged. Verify\n`Qwen3VLTextAttention.forward` signature matches the patch:\n`(self, hidden_states, position_embeddings, attention_mask, past_key_values, cache_position, **kwargs)`.\nAny signature change breaks the Ulysses SP for Qwen3-VL.\n\n______________________________________________________________________\n\n### 12. `transformers.utils.import_utils.is_torch_npu_available`\n\n**Source:** `src/transformers/utils/import_utils.py`\n\nCalled in `areal/infra/platforms/__init__.py` (lines 6, 19):\n\n```python\nfrom transformers.utils.import_utils import is_torch_npu_available\nis_npu_available = is_torch_npu_available()\n```\n\n**Check:** Confirm the function is still at this exact submodule path (private utils\nmodule). Check whether it was moved to a different location or replaced with a different\nNPU detection mechanism.\n\n______________________________________________________________________\n\n## Version-Guarded Code\n\nNo known version-guarded code exists in AReaL for `transformers`.\n","frontmatter":{"package":"transformers","github":"huggingface/transformers","branch_template":"v${VERSION}","upstream_paths":["src/transformers/models/auto/auto_factory.py","src/transformers/models/auto/configuration_auto.py","src/transformers/configuration_utils.py","src/transformers/tokenization_utils_fast.py","src/transformers/tokenization_utils_base.py","src/transformers/processing_utils.py","src/transformers/optimization.py","src/transformers/modeling_flash_attention_utils.py","src/transformers/integrations/flash_attention.py","src/transformers/models/qwen2_vl/modeling_qwen2_vl.py","src/transformers/models/qwen2_5_vl/","src/transformers/models/qwen3_vl/modeling_qwen3_vl.py","src/transformers/utils/import_utils.py"]},"isInternal":false,"tokens":4435,"sizeBytes":23134},{"name":"vllm.md","path":".agents/skills/upgrade-deps/checklists/vllm.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.agents/skills/upgrade-deps/checklists/vllm.md","title":"Checklists Skill","category":"anthropic-skill","format":"markdown","content":"---\npackage: vllm\ngithub: vllm-project/vllm\nbranch_template: v${VERSION}\nupstream_paths:\n  - vllm/entrypoints/openai/\n  - vllm/lora/\n  - vllm/model_executor/model_loader/\n  - vllm/v1/worker/gpu_worker.py\n  - vllm/worker/worker.py\n  - vllm/reasoning/\n  - vllm/tool_parsers/\n  - vllm/distributed/parallel_state.py\n  - vllm/utils/argparse_utils.py\n---\n\n## Affected Files\n\n### Primary (engine layer — most likely to break)\n\n| File                                             | Imports / Usage                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |\n| ------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `areal/engine/vllm_ext/vllm_worker_extension.py` | `vllm.logger.init_logger`, `vllm.lora.lora_model.LoRAModel`, `vllm.lora.peft_helper.PEFTHelper`, `vllm.lora.request.LoRARequest`, `vllm.model_executor.model_loader.get_model_loader`; **private APIs**: `_adapter_manager._registered_adapters`, `._add_adapter`, `.activate_adapter`                                                                                                                                                                                                                                                                                                          |\n| `areal/engine/vllm_ext/areal_vllm_server.py`     | `vllm.entrypoints.openai.api_server.build_app`, `.run_server`; `vllm.entrypoints.openai.cli_args.make_arg_parser`, `.validate_parsed_serve_args`; `vllm.entrypoints.openai.completion.api_router.create_completion`; `vllm.entrypoints.openai.completion.protocol.CompletionRequest`; `vllm.entrypoints.openai.engine.protocol.ErrorResponse`, `.OpenAIBaseModel`; `vllm.entrypoints.openai.utils.validate_json_request`; `vllm.entrypoints.utils.cli_env_setup`, `.load_aware_call`, `.with_cancellation`; `vllm.lora.request.LoRARequest`; `vllm.utils.argparse_utils.FlexibleArgumentParser` |\n| `areal/engine/vllm_remote.py`                    | HTTP-only — builds vLLM launch commands via `vLLMConfig.build_cmd_from_args()`; sets `VLLM_ALLOW_RUNTIME_LORA_UPDATING=True`                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |\n\n### Secondary (model / infra layer)\n\n| File                                            | Imports / Usage                                                                              |\n| ----------------------------------------------- | -------------------------------------------------------------------------------------------- |\n| `areal/infra/platforms/cuda.py`                 | `vllm.v1.worker.gpu_worker.Worker` (V1) with fallback to `vllm.worker.worker.Worker` (V0)    |\n| `areal/infra/platforms/unknown.py`              | same V1/V0 Worker import pattern                                                             |\n| `areal/experimental/openai/tool_call_parser.py` | `vllm.reasoning.ReasoningParserManager`, `vllm.tool_parsers.ToolParserManager` (conditional) |\n\n### Tertiary (tests, config)\n\n| File                                                 | Imports / Usage                          |\n| ---------------------------------------------------- | ---------------------------------------- |\n| `tests/experimental/openai/test_tool_call_parser.py` | unit tests with vLLM mocking             |\n| `tests/test_inference_engines.py`                    | integration tests for `RemotevLLMEngine` |\n\n### External (awex — separate subsystem)\n\n| File                                          | Imports / Usage                                                                                                                                                                |\n| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `asystem-awex/awex/vllm_plugin.py`            | `vllm.entrypoints.openai.api_server.router`, `vllm.distributed.parallel_state.*`, `vllm.v1.worker.worker_base.WorkerBase`, `vllm.model_executor.model_loader.get_model_loader` |\n| `asystem-awex/awex/awex_vllm_server.py`       | `vllm.entrypoints.openai.api_server.run_server`, cli_args, `FlexibleArgumentParser`                                                                                            |\n| `asystem-awex/awex/engine/vllm.py`            | VLLMEngine wrapper                                                                                                                                                             |\n| `asystem-awex/awex/vllm_awex_adapter.py`      | `AwexVLLMServerAdapter` for runtime weight updates                                                                                                                             |\n| `asystem-awex/awex/sharding/vllm_sharding.py` | sharding strategy helpers                                                                                                                                                      |\n\n______________________________________________________________________\n\n## API Usage Catalog\n\nFor each function/class below, verify the call signature against the upstream source at\nthe target version. Focus on: **missing new required parameters**, **removed old\nparameters**, **renamed parameters**, **changed return types**, **changed method\nsignatures on returned objects**, and **moved/renamed modules**.\n\n### 1. `vllm.entrypoints.openai.api_server.build_app` and `.run_server`\n\n**Source:** `vllm/entrypoints/openai/api_server.py`\n\nCalled in `areal/engine/vllm_ext/areal_vllm_server.py` (lines 10-11, 366-398):\n\n```python\nfrom vllm.entrypoints.openai.api_server import build_app as _original_build_app\nfrom vllm.entrypoints.openai.api_server import run_server\n\n# build_app is monkey-patched at line 388 to inject AReaL routes:\nimport vllm.entrypoints.openai.api_server as _api_server_module\n_api_server_module.build_app = _areal_build_app  # replaces the module-level reference\n\n# _areal_build_app calls the original with supported_tasks kwarg (line 371):\napp = _original_build_app(args, supported_tasks=supported_tasks)\n\n# run_server is called via uvloop (line 398):\nuvloop.run(run_server(args))\n```\n\n**Check:** Confirm both functions still exist at this path. Verify `build_app` accepts\n`(args, supported_tasks=...)` and returns a FastAPI `app`. Verify `run_server` is an\nasync function accepting `(args)`. Check that monkey-patching\n`_api_server_module.build_app` still works (i.e., `run_server` reads `build_app` from\nthe module, not a closure).\n\n______________________________________________________________________\n\n### 2. `vllm.entrypoints.openai.cli_args.make_arg_parser` and `.validate_parsed_serve_args`\n\n**Source:** `vllm/entrypoints/openai/cli_args.py`\n\nCalled in `areal/engine/vllm_ext/areal_vllm_server.py` (lines 12, 391-396):\n\n```python\nfrom vllm.entrypoints.openai.cli_args import make_arg_parser, validate_parsed_serve_args\n\nparser = FlexibleArgumentParser(description=\"vLLM OpenAI-Compatible RESTful API server.\")\nparser = make_arg_parser(parser)\nargs = parser.parse_args()\nvalidate_parsed_serve_args(args)\n```\n\n**Check:** Verify `make_arg_parser` still accepts a parser object and returns an\naugmented parser. Check `validate_parsed_serve_args` for new validation rules that might\nreject AReaL's custom args.\n\n______________________________________________________________________\n\n### 3. `vllm.entrypoints.openai.completion` — protocol and router\n\n**Source:** `vllm/entrypoints/openai/completion/`\n\nCalled in `areal/engine/vllm_ext/areal_vllm_server.py`:\n\n```python\nfrom vllm.entrypoints.openai.completion.api_router import create_completion as original_create_completion\nfrom vllm.entrypoints.openai.completion.protocol import CompletionRequest\nfrom vllm.entrypoints.openai.engine.protocol import ErrorResponse, OpenAIBaseModel\nfrom vllm.entrypoints.openai.utils import validate_json_request\n```\n\n**Check:** Verify the completion sub-package still exists (was restructured in some vLLM\nversions). Confirm `CompletionRequest` Pydantic model fields. Verify `ErrorResponse` and\n`OpenAIBaseModel` are still at `engine.protocol`. Check `validate_json_request` still\nreturns a FastAPI `Depends`.\n\n______________________________________________________________________\n\n### 4. `vllm.entrypoints.utils` — server utilities\n\n**Source:** `vllm/entrypoints/utils.py`\n\nCalled in `areal/engine/vllm_ext/areal_vllm_server.py` (lines 19, 349-350, 390):\n\n```python\nfrom vllm.entrypoints.utils import cli_env_setup, load_aware_call, with_cancellation\n\n# Used as stacked decorators on the create_completion endpoint (lines 349-350):\n@with_cancellation\n@load_aware_call\nasync def create_completion(request: CompletionRequest, raw_request: Request):\n    ...\n\n# Called at server startup (line 390):\ncli_env_setup()\n```\n\n**Check:** Confirm all three are still exported from `vllm.entrypoints.utils`. Verify\n`with_cancellation` is still a decorator compatible with async endpoints. Verify\n`load_aware_call` is still a decorator (not renamed to `load_aware`). Confirm\n`cli_env_setup()` is still a no-arg function.\n\n______________________________________________________________________\n\n### 5. `vllm.lora.request.LoRARequest`\n\n**Source:** `vllm/lora/request.py`\n\nCalled in `areal/engine/vllm_ext/vllm_worker_extension.py` (lines 10, 61-66) and\n`areal_vllm_server.py` (line 21):\n\n```python\nfrom vllm.lora.request import LoRARequest\n\nlora_request = LoRARequest(\n    lora_name=lora_name,\n    lora_int_id=lora_int_id,\n    lora_path=lora_model_path,\n    base_model_name=base_model_name,\n)\n```\n\n**Check:** Verify constructor kwargs unchanged. Confirm `base_model_name` is still\naccepted (vs `base_model_name_or_path`). Check for new required fields.\n\n______________________________________________________________________\n\n### 6. `vllm.lora.lora_model.LoRAModel.from_lora_tensors`\n\n**Source:** `vllm/lora/lora_model.py`\n\nCalled in `areal/engine/vllm_ext/vllm_worker_extension.py`:\n\n```python\nfrom vllm.lora.lora_model import LoRAModel\n\nlora_model = LoRAModel.from_lora_tensors(\n    lora_model_id=int_id,\n    tensors=tensors,\n    peft_helper=peft_helper,\n    device=device,\n    dtype=dtype,\n    model_vocab_size=vocab_size,\n    weights_mapper=weights_mapper,\n)\n```\n\n**Check:** Verify all keyword arguments. `from_lora_tensors` is a classmethod — confirm\nit still exists and returns a `LoRAModel`. Check `peft_helper` type (should be\n`PEFTHelper`). Verify `weights_mapper` is still accepted.\n\n______________________________________________________________________\n\n### 7. `vllm.lora.peft_helper.PEFTHelper.from_dict`\n\n**Source:** `vllm/lora/peft_helper.py`\n\nCalled in `areal/engine/vllm_ext/vllm_worker_extension.py`:\n\n```python\nfrom vllm.lora.peft_helper import PEFTHelper\n\npeft_helper = PEFTHelper.from_dict(peft_config_dict)\n```\n\n**Check:** Verify `from_dict` classmethod still exists. Confirm it accepts a dict with\nkeys `r`, `lora_alpha`, `target_modules`, `bias`. Check return type.\n\n______________________________________________________________________\n\n### 8. `vllm.model_executor.model_loader.get_model_loader`\n\n**Source:** `vllm/model_executor/model_loader/`\n\nCalled in `areal/engine/vllm_ext/vllm_worker_extension.py` (line 34):\n\n```python\nfrom vllm.model_executor.model_loader import get_model_loader\n\nmodel_loader = get_model_loader(self.model_runner.vllm_config.load_config)\nmodel_loader.load_weights(self.model_runner.model, model_config=self.model_runner.model_config)\n```\n\n**Check:** Verify function exists. Confirm it accepts `vllm_config.load_config` (type\n`LoadConfig`). Verify the returned loader has a `load_weights(model, model_config=...)`\nmethod (not `load_model`).\n\n______________________________________________________________________\n\n### 9. LoRA adapter manager private APIs (HIGH RISK)\n\n**Source:** `vllm/lora/` (internal, unstable)\n\nCalled in `areal/engine/vllm_ext/vllm_worker_extension.py` (lines 154-293):\n\n```python\n# Diagnostic read — public list_adapters() at line 179:\nadapter_ids = self.model_runner.lora_manager.list_adapters()\n\n# Private read — _registered_adapters at line 187:\nlora_model = self.model_runner.lora_manager._adapter_manager._registered_adapters[lora_int_id]\n\n# Public remove — remove_adapter() at line 270:\nself.model_runner.lora_manager.remove_adapter(lora_int_id)\n\n# Private write — _add_adapter / activate_adapter at lines 272-274:\nself.model_runner.lora_manager._adapter_manager._add_adapter(new_lora_model)\nself.model_runner.lora_manager._adapter_manager.activate_adapter(new_lora_model.id)\n```\n\nThe code comments at line 155-156 explicitly note: _\"This code relies on vLLM private\nAPIs: `_adapter_manager`, `_registered_adapters`, and `_add_adapter`/`activate_adapter`,\nwhich may change/breakdown due to newer vllm versions.\"_\n\n**Check:** These are **private APIs** — highest breakage risk. Verify `lora_manager`\nstill exposes `list_adapters()` (public), `remove_adapter()` (public), and\n`_adapter_manager` (private). Confirm `_registered_adapters` is still a dict-like\nmapping of `int_id -> LoRAModel`. Confirm `_add_adapter` and `activate_adapter` method\nsignatures. Check if public alternatives now exist that replace the private path.\n\n______________________________________________________________________\n\n### 10. `vllm.v1.worker.gpu_worker.Worker` / `vllm.worker.worker.Worker`\n\n**Source:** `vllm/v1/worker/gpu_worker.py`, `vllm/worker/worker.py`\n\nCalled in `areal/infra/platforms/cuda.py` and `unknown.py`:\n\n```python\ntry:\n    from vllm.v1.worker.gpu_worker import Worker  # V1\nexcept ImportError:\n    from vllm.worker.worker import Worker  # V0 fallback\n```\n\n**Check:** Confirm at least one of these import paths works. If V0 is being removed,\nupdate to V1 only. Check `Worker` class for method compatibility.\n\n______________________________________________________________________\n\n### 11. `vllm.reasoning.ReasoningParserManager` and `vllm.tool_parsers.ToolParserManager`\n\n**Source:** `vllm/reasoning/`, `vllm/tool_parsers/`\n\nCalled in `areal/experimental/openai/tool_call_parser.py` (lines 157-206, inside\n`_process_tool_calls_vllm`):\n\n```python\nfrom vllm.reasoning import ReasoningParserManager\nfrom vllm.tool_parsers import ToolParserManager\n\n# get_reasoning_parser returns a CLASS, then instantiated with tokenizer (line 164-167):\nreasoning_parser_cls = ReasoningParserManager.get_reasoning_parser(reasoning_parser)\nreasoning_parser_inst = reasoning_parser_cls(tokenizer)\n# Accessed attributes: reasoning_parser_inst.start_token, reasoning_parser_inst.end_token\n\n# get_tool_parser also returns a CLASS, then instantiated with tokenizer (line 191-206):\ntool_parser_cls = ToolParserManager.get_tool_parser(vllm_name)\ntool_parser = tool_parser_cls(tokenizer)\n# Called methods: tool_parser.extract_tool_calls(model_output, request)\n```\n\n**Check:** Confirm `get_reasoning_parser` and `get_tool_parser` return **classes** (not\ninstances). Verify the classes accept `(tokenizer)` in their constructor. Confirm\n`start_token`/`end_token` attributes on reasoning parser instances. Verify\n`extract_tool_calls` method signature on tool parser instances. These are used as\nfallback when SGLang parsers are unavailable.\n\n______________________________________________________________________\n\n### 12. `vllm.utils.argparse_utils.FlexibleArgumentParser`\n\n**Source:** `vllm/utils/argparse_utils.py`\n\nCalled in `areal/engine/vllm_ext/areal_vllm_server.py`:\n\n```python\nfrom vllm.utils.argparse_utils import FlexibleArgumentParser\n```\n\n**Check:** Verify this class still exists at this path (it was added in a specific vLLM\nversion). `asystem-awex/awex/awex_vllm_server.py` already has a try/except fallback for\nthis import.\n\n______________________________________________________________________\n\n### 13. `vllm.logger.init_logger`\n\n**Source:** `vllm/logger.py`\n\nCalled in `areal/engine/vllm_ext/vllm_worker_extension.py` and `areal_vllm_server.py`:\n\n```python\nfrom vllm.logger import init_logger\nlogger = init_logger(__name__)\n```\n\n**Check:** Verify function exists. Confirm it returns a standard\n`logging.Logger`-compatible object.\n\n______________________________________________________________________\n\n### 14. vLLM CLI flag compatibility\n\n**Source:** `vllm/engine/arg_utils.py`, `vllm/entrypoints/openai/cli_args.py`\n\nCalled indirectly via `vLLMConfig.build_cmd_from_args()` in `areal/api/cli_args.py`:\n\n```python\n# Assembles CLI flags: --model, --tensor-parallel-size, --gpu-memory-utilization,\n# --max-model-len, --enable-lora, --max-lora-rank, etc.\n```\n\n**Check:** For each field in `vLLMConfig`, confirm the corresponding vLLM CLI flag still\nexists. Pay attention to `--enable-lora` and related LoRA flags. Verify env var\n`VLLM_ALLOW_RUNTIME_LORA_UPDATING` is still respected.\n\n______________________________________________________________________\n\n## Version-Guarded Code\n\n- `areal/infra/platforms/cuda.py` and `unknown.py` — tries\n  `vllm.v1.worker.gpu_worker.Worker` first, falls back to `vllm.worker.worker.Worker`.\n  Once V0 support is dropped, remove the fallback.\n\n- `asystem-awex/awex/awex_vllm_server.py` — `FlexibleArgumentParser` wrapped in\n  try/except with fallback to `argparse.ArgumentParser`.\n\n- `asystem-awex/awex/vllm_plugin.py` — `get_pcp_group` import wrapped in try/except\n  (added in newer vLLM versions).\n","frontmatter":{"package":"vllm","github":"vllm-project/vllm","branch_template":"v${VERSION}","upstream_paths":["vllm/entrypoints/openai/","vllm/lora/","vllm/model_executor/model_loader/","vllm/v1/worker/gpu_worker.py","vllm/worker/worker.py","vllm/reasoning/","vllm/tool_parsers/","vllm/distributed/parallel_state.py","vllm/utils/argparse_utils.py"]},"isInternal":false,"tokens":3909,"sizeBytes":18731},{"name":"SKILL.md","path":".agents/skills/upgrade-deps/SKILL.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.agents/skills/upgrade-deps/SKILL.md","title":"upgrade-deps","category":"anthropic-skill","format":"markdown","content":"---\nname: upgrade-deps\ndescription: Upgrade focused runtime dependencies in AReaL. First validates and updates per-package API checklists for structural completeness, then updates pyproject files, resolves conflicts, locks, updates the Dockerfile, and audits API compatibility against the checklists.\n---\n\n## Usage\n\n```text\n/upgrade-deps <package==version> [<package==version> ...]\n```\n\n**Arguments:** One or more pinned package versions, e.g.,\n`/upgrade-deps megatron-core==0.17.0 sglang==0.5.10 vllm==0.18.0 transformers==4.58.0`.\n\nIf a package is omitted, its current version can be preserved or upgraded, depending on\nthe resolution of `uv lock`.\n\n______________________________________________________________________\n\n## Architecture\n\n### Dual-Manifest Model\n\nAReaL maintains **two pyproject files** because SGLang and vLLM pin\nmutually-incompatible `torch` / `torchao` versions:\n\n| File                  | Inference backend | Lock file      |\n| --------------------- | ----------------- | -------------- |\n| `pyproject.toml`      | SGLang (default)  | `uv.lock`      |\n| `pyproject.vllm.toml` | vLLM              | `uv.vllm.lock` |\n\nBoth share the same core dependencies, megatron extras, and dev group. They diverge only\nin inference backend extras and `torch`/`torchao` version constraints.\n\nThe `Dockerfile` builds both variants from a single file using `ARG VARIANT` (`sglang`\nor `vllm`). The base image, torch install, and flash-attn wheels are all\nvariant-specific.\n\n### Focused Packages\n\nThe following packages are **focused** — their API usage in AReaL is cataloged, and any\nversion change triggers the API compatibility audit (Step 6):\n\n| Package           | Import path       |\n| ----------------- | ----------------- |\n| `megatron-core`   | `megatron.core`   |\n| `megatron-bridge` | `megatron.bridge` |\n| `mbridge`         | `mbridge`         |\n| `transformers`    | `transformers`    |\n| `sglang`          | `sglang`          |\n| `vllm`            | `vllm`            |\n| `peft`            | `peft`            |\n| `torchao`         | `torchao`         |\n\n`torch` is tracked in the Package Impact Matrix below for scope and Docker awareness,\nbut is **not** a focused package — it does not receive API auditing.\n\n### Package Impact Matrix\n\nEvery entry below has a **variant scope** that determines which files to edit, which\nlock files to regenerate, and whether the Dockerfile needs review. All focused packages\nplus `torch` (tracked for Docker impact only, not API-audited) are listed.\n\n| Package           | Scope            | pyproject.toml locations                                       | pyproject.vllm.toml locations                                  | Docker impact |\n| ----------------- | ---------------- | -------------------------------------------------------------- | -------------------------------------------------------------- | ------------- |\n| `sglang`          | sglang-only      | `[optional-deps].sglang`                                       | —                                                              | Base image    |\n| `vllm`            | vllm-only        | —                                                              | `[optional-deps].vllm`                                         | No            |\n| `megatron-core`   | shared           | `[optional-deps].megatron` + `[tool.uv].override-dependencies` | `[optional-deps].megatron` + `[tool.uv].override-dependencies` | No            |\n| `megatron-bridge` | shared           | `[optional-deps].megatron`                                     | `[optional-deps].megatron`                                     | No            |\n| `mbridge`         | shared           | `[optional-deps].megatron` (git pin)                           | `[optional-deps].megatron` (git pin)                           | No            |\n| `transformers`    | shared           | `[project].dependencies`                                       | `[project].dependencies`                                       | No            |\n| `peft`            | shared           | `[project].dependencies`                                       | `[project].dependencies`                                       | No            |\n| `torch`           | shared-divergent | `[project].dependencies` (≥2.9.1)                              | `[project].dependencies` (≥2.10.0)                             | Stage 1 torch |\n| `torchao`         | shared-divergent | `[tool.uv].override-dependencies` (==0.15.0)                   | `[project].dependencies` (==0.16.0)                            | No            |\n\n**Scope rules:**\n\n- **sglang-only / vllm-only**: Edit and lock only the affected variant.\n- **shared**: Edit both files; lock both variants.\n- **shared-divergent**: The two variants intentionally use different versions of the\n  same package. If the user supplies a single target version, **ask which variant(s) to\n  apply it to** rather than assuming convergence. If the user supplies two targets\n  (e.g., `torch==2.9.1@sglang torch==2.10.0@vllm`), apply each accordingly.\n\n### Upgrade Families\n\nSome packages are tightly coupled and should be upgraded together:\n\n| Family    | Members                                  | Reason                                                    |\n| --------- | ---------------------------------------- | --------------------------------------------------------- |\n| megatron  | `megatron-core`, `megatron-bridge`       | megatron-bridge wraps megatron-core; tightly coupled APIs |\n| inference | `sglang` or `vllm` + `torch` + `torchao` | Inference backends pin specific torch/torchao versions    |\n\nWhen the user upgrades one member, **check the checklist of all family members** for\nrequired co-upgrades and warn if a co-upgrade is needed but not requested.\n\n### Per-Package API Checklists\n\nEach focused package has a dedicated markdown file under `checklists/` that documents\nhow AReaL uses that package's APIs:\n\n```\n.agents/skills/upgrade-deps/\n├── SKILL.md\n└── checklists/\n    ├── megatron-core.md\n    ├── megatron-bridge.md\n    ├── vllm.md\n    ├── sglang.md\n    ├── transformers.md\n    ├── peft.md\n    └── torchao.md\n```\n\nEach checklist file MUST contain the following sections:\n\n````markdown\n---\npackage: <pip-package-name>\ngithub: <org/repo>                    # e.g., NVIDIA/Megatron-LM\nbranch_template: \"v${VERSION}\"        # how to construct the git branch/tag from version\nupstream_paths:                        # source paths to cross-reference\n  - path/to/relevant/file.py\n  - path/to/relevant/module/\n---\n\n## Affected Files\n\n### Primary (most likely to break)\n| File | Imports / Usage |\n| ---- | --------------- |\n| ...  | ...             |\n\n### Secondary\n...\n\n### Tertiary (tests, infra)\n...\n\n## API Usage Catalog\n\nFor each function/class, verify the call signature against the upstream source.\nFocus on: removed params, renamed params, new required params, changed return types,\nmoved/renamed modules.\n\n### 1. `module.submodule.function_or_class`\n\n**Source:** `upstream_path/file.py`\n\nCalled in `areal/path/to/file.py:LINE`:\n\\```python\nactual_call_site_code()\n\\```\n\n**Check:** [what to verify]\n\n### 2. ...\n\n## Version-Guarded Code (if any)\n- [file:line] description of version guard and when it can be removed\n````\n\nTo populate a checklist, catalog AReaL's usage of the package by grepping for imports\nand call sites, then fill in the template sections. Each checklist should be\nself-contained — all API signatures, file paths, and upstream references must be\nrecorded directly in the checklist file.\n\n______________________________________________________________________\n\n## Workflow\n\n### Step 0: Parse Input and Record Baseline\n\n1. Parse arguments. Each argument is `<package==version>` or, for shared-divergent\n   packages, `<package==version@variant>` (e.g., `torchao==0.16.0@vllm`).\n1. Validate that each package name is a recognized focused package (see Focused Packages\n   table). If unrecognized, warn but proceed — it may be a transitive dependency the\n   user wants to pin.\n1. For **shared-divergent** packages (`torch`, `torchao`): if the user supplies a single\n   version without a `@variant` qualifier, **ask which variant(s) to apply it to**\n   before proceeding.\n1. Record the current **resolved** versions of ALL focused packages from the applicable\n   lock file(s) as the baseline snapshot. Use `uv.lock` for sglang-scoped and shared\n   packages; `uv.vllm.lock` for vllm-scoped and shared packages. Packages scoped to a\n   single variant appear only in that variant's lock file. This baseline will be used\n   later to detect which packages actually changed — including transitive bumps caused\n   by `uv lock` resolution, not just explicit pin changes.\n\n### Step 0.5: Validate and Update Checklists\n\nBefore modifying any dependencies, verify that the API checklists for the packages being\nupgraded are structurally complete — i.e., they document all current AReaL import sites\nand call patterns. Stale checklists lead to missed breaking changes in Step 6.\n\nFor each **focused package explicitly requested** in the command that has a checklist\nfile under `checklists/`:\n\n1. **Discover all usages.** Grep the AReaL codebase (`areal/`, `tests/`, `examples/`)\n   for all imports matching the package's import path(s). See the Import Patterns table\n   in `CHECKLIST_MAINTENANCE.md` § 2 for package-specific grep patterns. For HTTP-based\n   integrations (sglang, vllm), also scan request-building code for endpoint paths and\n   JSON field names.\n\n1. **Compare against the checklist.** Diff the discovered files against the Affected\n   Files tables (Primary / Secondary / Tertiary) in the checklist. Identify:\n\n   - **Missing** — files that import the package but are not listed in any tier.\n   - **Stale** — files listed in the checklist that no longer import the package.\n   - **Changed** — files whose actual imports differ from what's documented.\n\n1. **Classify and update.** For each discrepancy, follow the Structural Validation\n   Procedure in `CHECKLIST_MAINTENANCE.md` § 3:\n\n   - Add missing files to the appropriate tier (Primary / Secondary / Tertiary).\n   - Add new API Usage Catalog entries for genuinely new call patterns.\n   - Remove stale entries.\n   - Renumber catalog entries if needed.\n\n1. **Update the Checklist File Status table** at the bottom of this file if entry counts\n   changed.\n\n1. **Report** changes before proceeding:\n\n   ```text\n   Checklist validation for <package>:\n   - Added N files to Affected Files (P primary, S secondary, T tertiary)\n   - Added M new API catalog entries: [brief list]\n   - Removed K stale entries: [brief list]\n   - No changes needed (if clean)\n   ```\n\nIf a requested focused package does **not** have a checklist file, create one from\n`checklists/_TEMPLATE.md` using the full procedure in `CHECKLIST_MAINTENANCE.md` § 5.\n\n**Scope note:** This step validates only the packages explicitly named in the command.\nPackages that are transitively bumped are identified later in Step 5; their checklists\nare validated at that point using the same procedure before the API audit in Step 6.\n\n### Step 1: Update Pyproject Files\n\nFor each requested package, using the Package Impact Matrix:\n\n1. Determine the **variant scope** (sglang-only, vllm-only, shared, shared-divergent).\n1. Edit the version pin in **all declaration locations** for each affected pyproject\n   file. A single package may appear in multiple locations:\n   - `[project].dependencies`\n   - `[project.optional-dependencies].<extra>`\n   - `[tool.uv].override-dependencies`\n1. For **upgrade families**: if `megatron-core` is being upgraded, also check whether\n   `megatron-bridge` needs a corresponding version bump. Warn if it does but was not\n   included in the command.\n\n### Step 2: Lock Dependencies (Variant-Aware)\n\nRegenerate lock files for each **affected variant**. A variant is affected if any of its\npyproject file's dependencies were modified. If conflicts arise during locking, do NOT\nattempt to resolve them in this step — just report them and defer resolution to Step 3.\n\n**SGLang variant** (if `pyproject.toml` was modified):\n\n```bash\nbash scripts/uv_lock.sh\n```\n\n**vLLM variant** (if `pyproject.vllm.toml` was modified):\n\n```bash\nbash scripts/uv_lock.sh\n```\n\nIf `uv lock` fails for either variant:\n\n1. Read the error message carefully.\n1. If it's a version conflict, proceed to Step 3 to resolve, then **return here to\n   re-lock**.\n1. Do NOT modify dependencies without user approval to make the lock succeed.\n\n**Validation:** After locking, verify both lock files exist and are non-empty.\n\n### Step 3: Resolve Dependency Conflicts\n\nResolve any conflicts from Step 2. After all conflicts are resolved, **return to Step 2\nand re-lock** the affected variant(s). Classify each conflict:\n\n**Auto-resolvable** — only AReaL's pin conflicts with an upstream package, and the\nupstream's required version is acceptable. Update AReaL's pin automatically.\n\n**Needs user input** — two upstream packages have mutual conflicts (e.g., sglang\nrequires `torch==2.9.1` but vllm requires `torch==2.10.0`). Summarize and ask the user.\n\nOutput format:\n\n```\nSummary\n\n---\nAuto-resolved (no action required):\n- <name>: <packageA> requires <versionA>, <packageB> requires <versionB>,\n  AReaL specified <oldVersion>, updated to <newVersion>\n- ...\n\n---\nConflicts (need user resolution):\n- <name>: <packageA> requires <versionA>, <packageB> requires <versionB>\n- ...\n```\n\nYou may use `override-dependencies` in `[tool.uv]` to force-pin versions where needed.\nRemember that `sglang` and `vllm` are **separate variants** maintained in different\npyproject files — they are never installed together in the same environment.\n\n### Step 4: Update Dockerfile (If Required)\n\nReview the Package Impact Matrix \"Docker impact\" column. Only proceed with Dockerfile\nchanges if an upgraded package has Docker impact. The most common triggers are:\n\n**Base image change** (triggered by `sglang` upgrade):\n\nThe Dockerfile base image is `lmsysorg/sglang:v{SGLANG_VERSION}-cu129-amd64-runtime`. If\n`sglang` was upgraded, update line 9 of the `Dockerfile`:\n\n```dockerfile\nFROM lmsysorg/sglang:v{NEW_SGLANG_VERSION}-cu129-amd64-runtime\n```\n\nVerify that the new base image tag exists on Docker Hub / GHCR before committing.\n\n**Torch version change** (triggered by `torch` upgrade):\n\nThe Dockerfile Stage 1 installs torch with variant-specific versions. Update the version\nmapping in the `RUN uv venv` command:\n\n```dockerfile\n&& if [ \"$VARIANT\" = \"vllm\" ]; then TORCH_VER=\"{VLLM_TORCH}\"; else TORCH_VER=\"{SGLANG_TORCH}\"; fi \\\n```\n\nAlso check flash-attn wheel compatibility — **both** flash-attn-2 and flash-attn-3\ninstalls use a `TORCH_TAG` that must match the torch major.minor version. Update both\noccurrences in the Dockerfile:\n\n```dockerfile\n# flash-attn-2 (first flash-attn RUN block)\n&& if [ \"$VARIANT\" = \"vllm\" ]; then TORCH_TAG=\"torch{VLLM_TORCH_MAJOR_MINOR}\"; else TORCH_TAG=\"torch{SGLANG_TORCH_MAJOR_MINOR}\"; fi \\\n\n# flash-attn-3 (second flash-attn RUN block — same TORCH_TAG pattern)\n&& if [ \"$VARIANT\" = \"vllm\" ]; then TORCH_TAG=\"torch{VLLM_TORCH_MAJOR_MINOR}\"; else TORCH_TAG=\"torch{SGLANG_TORCH_MAJOR_MINOR}\"; fi \\\n```\n\n**No Dockerfile changes needed** for: `megatron-core`, `megatron-bridge`,\n`transformers`, `peft`, `vllm`, `torchao`. These are installed via\n`uv pip install -r pyproject.toml` in Stage 3, which reads the updated pyproject\nautomatically.\n\n### Step 5: Identify Updated Focused Packages\n\nCompare the baseline snapshot (Step 0) against the **resolved** versions in the lock\nfiles (`uv.lock` and/or `uv.vllm.lock`) produced by Step 2 (or re-locked after Step 3\nconflict resolution). This catches not only explicitly requested upgrades but also\n**transitive version bumps** — e.g., upgrading `sglang` may pull in a newer\n`transformers` through dependency resolution.\n\nBuild a list of focused packages whose resolved version actually changed. This list\ndetermines which API checklists to audit in Step 6.\n\nThe focused packages to check are listed in the Focused Packages table (Architecture\nsection):\n\n- `megatron-core` (imports as `megatron.core`)\n- `megatron-bridge` (imports as `megatron.bridge`)\n- `transformers`\n- `sglang`\n- `vllm`\n- `peft`\n- `torchao`\n\nIf a package version did NOT change (even if it was in the user's input but resolved to\nthe same version), skip its API audit.\n\nFor any newly-identified package whose checklist was NOT already validated in Step 0.5,\nrun the same structural validation procedure (see `CHECKLIST_MAINTENANCE.md` § 3) on its\nchecklist before proceeding to Step 6.\n\n### Step 6: API Compatibility Audit\n\nFor each updated focused package that has a checklist file under `checklists/`:\n\n#### 6a. Clone upstream source\n\nRead the checklist frontmatter to get `github` and `branch_template`. Clone or checkout\nthe target version:\n\n```bash\nREPO_ROOT=$(pwd)\nPKG_DIR=\"${REPO_ROOT}/<package>-src\"\nVERSION=\"<target_version>\"\n# Validate VERSION to prevent command injection\nif [[ ! \"$VERSION\" =~ ^[a-zA-Z0-9._/-]+$ ]]; then\n  echo \"Error: Invalid version format: $VERSION\"; exit 1\nfi\nBRANCH=$(echo \"<branch_template>\" | sed \"s/\\${VERSION}/$VERSION/\")\nif [ ! -d \"$PKG_DIR\" ]; then\n  git clone --depth 1 --branch \"$BRANCH\" \"https://github.com/<github>.git\" \"$PKG_DIR\"\nelse\n  (cd \"$PKG_DIR\" && git fetch origin && git checkout \"$BRANCH\")\nfi\n```\n\nIf cloning fails (tag doesn't exist, etc.), report to the user immediately.\n\n#### 6b. Audit API signatures\n\nFor EACH entry in the checklist's API Usage Catalog:\n\n1. Open the upstream source file at the target version (paths listed in the checklist's\n   `upstream_paths` frontmatter).\n1. Compare the function/class signature against the current AReaL invocation.\n1. Flag any of:\n   - **Removed parameters** still passed by AReaL → must remove from call site\n   - **Renamed parameters** → must rename in call site\n   - **New required parameters** (no default) → must add to call site\n   - **New optional parameters** with useful defaults → document but skip\n   - **Changed return types** → must update consumers\n   - **Removed functions/classes** → must find replacement\n   - **Moved modules** → must update import paths\n   - **Changed method signatures** on returned objects → must update call sites\n1. Record findings per-file.\n\n#### 6c. Check version-guarded code\n\nIf the checklist has a \"Version-Guarded Code\" section, check whether any guards\nreference versions at or below the new target. If so, verify the upstream fix is present\nand note the dead code for cleanup.\n\n#### 6d. Apply code changes (if any)\n\nFor each flagged incompatibility:\n\n1. Update the call site in the affected AReaL file.\n1. **Preserve existing behavior** — do NOT refactor beyond what's required.\n1. If a function was removed, check the upstream migration guide or changelog.\n1. Priority order: engine layer → model layer → infra layer → test files.\n\nIf there are unresolvable breaking changes, **STOP and ask the user** before proceeding.\n\n#### 6e. Update checklist file\n\nUpdate the checklist file to reflect the post-upgrade state. Follow the Content Update\nProcedure in `CHECKLIST_MAINTENANCE.md` § 4:\n\n1. Update API signatures in catalog entries where upstream changed.\n1. Update call-site code snippets where Step 6d modified AReaL code.\n1. Update version-guarded code entries (remove cleaned-up guards, add new ones).\n1. Update frontmatter `upstream_paths` if source files moved.\n1. Update the entry count in the Checklist File Status table (bottom of this file).\n\nThis ensures the checklist remains an accurate reference for future upgrades.\n\n#### 6f. Clean up cloned repositories\n\nRemove the cloned upstream source directories to avoid cluttering the workspace:\n\n```bash\nrm -rf \"${REPO_ROOT}/<package>-src\"\n```\n\n### Step 7: Run Pre-Commit\n\n```bash\npre-commit run --all-files\n```\n\n### Step 8: Generate Upgrade Summary\n\nDump a formatted markdown summary to `upgrade-summary.md` in the repository root (this\nfile is gitignored and ephemeral). The summary MUST include:\n\n```markdown\n## Dependency Upgrade Summary\n\n**Date:** YYYY-MM-DD\n**Requested:** <original command>\n\n### Version Changes\n\n| Package | Old Version | New Version | Variant(s) |\n| ------- | ----------- | ----------- | ---------- |\n| ...     | ...         | ...         | ...        |\n\n### Dependency Resolution\n\n- <auto-resolved change description>\n- ...\n\n### Dockerfile Changes\n\n- <change description, or \"No changes required\">\n\n### API Compatibility Audit\n\n#### <package-name> (old → new)\n\n**Breaking changes found:**\n- [file:line] description of change\n\n**Module moves / renames:**\n- [old_path] → [new_path]\n\n**Version-guarded code:**\n- [file:line] status (still needed / can be removed)\n\n**No breaking changes found** _(if clean)_\n\n#### ...\n\n### Unresolved Issues (if any)\n\n- <description of issue and why it could not be auto-resolved>\n```\n\nIf the upgrade failed at any step, the summary should still be generated with the\nfailure reason clearly documented in the \"Unresolved Issues\" section.\n\n### Step 9: Create PR and Trigger CI (Optional)\n\nAsk the user if they want to create a PR.\n\nIf the user agrees:\n\n1. Load the `create-pr` skill to create the PR.\n1. Trigger the CI workflow manually via `.github/workflows/build-docker-image.yml` (only\n   if the Dockerfile was modified or inference backend versions changed).\n1. The Docker build CI builds both sglang and vllm images, then automatically triggers\n   testing on each. Debug until the overall workflow succeeds.\n1. If you encounter issues that cannot be resolved, ask the user for help.\n\n______________________________________________________________________\n\n## Checklist File Status\n\n| Package           | Checklist file                  | Status                                                                                                                                                                              |\n| ----------------- | ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `megatron-core`   | `checklists/megatron-core.md`   | ✅ 23 API entries (parallel_state, DDP, optimizer, pipeline, checkpointing/async internals, transformer config, FP8, GPT/MTP, tensor_parallel, custom attention, layer specs, RoPE) |\n| `megatron-bridge` | `checklists/megatron-bridge.md` | ✅ 9 API entries (AutoBridge, LoRA, save/load HF, Qwen3-VL patch target, AutoMapping, monkey-patch guard)                                                                           |\n| `mbridge`         | `checklists/mbridge.md`         | ✅ 14 API entries (AutoBridge, Bridge properties, weight mappings, LLMBridge subclassing, register_model, monkey-patch target)                                                      |\n| `vllm`            | `checklists/vllm.md`            | ✅ 14 API entries (entrypoints, LoRA manager, worker V0/V1, tool parsers, CLI)                                                                                                      |\n| `sglang`          | `checklists/sglang.md`          | ✅ 14 API entries (HTTP endpoints, tool/reasoning parsers, CLI flags, version guards)                                                                                               |\n| `transformers`    | `checklists/transformers.md`    | ✅ 12 API entries (Auto\\* classes, tokenizer, flash attention monkey-patches, Qwen VL internals, LR schedulers)                                                                     |\n| `peft`            | `checklists/peft.md`            | ✅ 4 API entries (LoraConfig, TaskType, get_peft_model, weight key format)                                                                                                          |\n| `torchao`         | `checklists/torchao.md`         | ✅ 5 API entries (fp8_blockwise_mm, enable_fp8_linear/experts, shard validation, Triton kernels)                                                                                    |\n","frontmatter":{"name":"upgrade-deps","description":"Upgrade focused runtime dependencies in AReaL. First validates and updates per-package API checklists for structural completeness, then updates pyproject files, resolves conflicts, locks, updates the Dockerfile, and audits API compatibility against the checklists."},"isInternal":false,"tokens":5558,"sizeBytes":24105},{"name":"api-config.md","path":".claude/rules/api-config.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.claude/rules/api-config.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\npaths:\n  - areal/api/**\n---\n\n# API & Config Rules\n\n## Dataclass Conventions\n\n```python\n@dataclass\nclass XxxConfig:\n    \"\"\"One-line description.\n\n    Attributes:\n        field_name: Description with default explained.\n    \"\"\"\n    # Required fields first (no default)\n    required_field: str\n\n    # Optional fields with defaults\n    optional_field: int = 32\n\n    # Internal fields last (underscore prefix)\n    _internal: str = field(default=\"\", repr=False)\n```\n\n## Field Ordering\n\n1. Required fields (no default)\n1. Common optional fields\n1. Advanced/rare optional fields\n1. Internal fields (`_prefix`)\n\n## Validation\n\n- Use `__post_init__` for validation\n- Raise `ValueError` with clear message:\n  ```python\n  def __post_init__(self):\n      if self.batch_size <= 0:\n          raise ValueError(f\"batch_size must be positive, got {self.batch_size}\")\n  ```\n\n## Backward Compatibility\n\n- **Adding fields**: Add with default value (safe)\n- **Removing fields**: Deprecate first, remove in next major version\n- **Renaming fields**: Add new field, keep old with deprecation warning\n- **Changing types**: Avoid; use Union if necessary\n\n## CLI Integration\n\n- Fields exposed to CLI must have clear `help` in metadata\n- Use `Literal` for enum-like choices\n- Avoid complex nested types in CLI-exposed configs\n\n## Documentation\n\n- All public configs must have docstring\n- Document constraints (e.g., \"must be power of 2\")\n- Include example values for non-obvious fields\n","frontmatter":{"paths":["areal/api/**"]},"isInternal":false,"tokens":345,"sizeBytes":1459},{"name":"code-style.md","path":".claude/rules/code-style.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.claude/rules/code-style.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"# Code Style Rules\n\nRules beyond pre-commit (ruff format/lint).\n\n## Design Patterns\n\n- **Prefer composition over inheritance**: Avoid deep class hierarchies\n  - Good: `Engine` holds a `Checkpointer` instance\n  - Avoid: `CheckpointableEngine(Engine)` →\n    `FSDPCheckpointableEngine(CheckpointableEngine)`\n- Keep inheritance shallow (≤2 levels when possible)\n- Use mixins sparingly; prefer explicit delegation\n\n## Logging\n\n- Use `areal.utils.logging.getLogger(name)` with PascalCase descriptive name, NOT\n  `print` or stdlib `logging`\n  - Good: `getLogger(\"RLVRWorkflow\")`, `getLogger(\"ArchonEngine\")`,\n    `getLogger(\"GSM8KReward\")`\n  - Avoid: `getLogger(__name__)` or dotted paths like `getLogger(\"areal.engine.fsdp\")`\n- For per-rank loggers: `[{Component} Rank {N}]` (e.g., `[FSDPEngine Rank 0]`)\n- Log levels:\n  - DEBUG: Detailed tracing (avoid in hot paths)\n  - INFO: Milestones (training start, checkpoint saved)\n  - WARNING: Recoverable issues\n  - ERROR: Failures requiring attention\n- Register new loggers in `areal/utils/logging.py` (`LOGGER_COLORS_EXACT` or\n  `LOGGER_PATTERNS`)\n- Test colors: `python -m areal.utils.logging`\n\n**Color Scheme:**\n\n| Color      | Category                               | Examples                           |\n| ---------- | -------------------------------------- | ---------------------------------- |\n| blue       | Infrastructure (Schedulers, Launchers) | `LocalScheduler`, `RayLauncher`    |\n| white      | Orchestration (Controllers, RPC)       | `TrainController`, `SGLangWrapper` |\n| purple     | RL-specific (Workflows, Rewards)       | `RLVRWorkflow`, `GSM8KReward`      |\n| green      | Data/Metrics (Stats, Dataset)          | `StatsLogger`, `RLTrainer`         |\n| cyan       | Compute backends (Engines, Platforms)  | `FSDPEngine`, `CUDAPlatform`       |\n| yellow/red | Warning/Error levels (override)        | Any logger at WARNING+             |\n\n## Performance Patterns\n\n- **Avoid GPU-CPU sync**: `.item()`, `.tolist()`, `print(tensor)` cause sync\n- **Prefer batch operations**: Avoid Python loops over tensor elements\n- **In-place ops**: Use when safe, but careful with autograd (`.add_()` vs `+`)\n\n## Naming Conventions\n\n| Type             | Pattern       | Example                             |\n| ---------------- | ------------- | ----------------------------------- |\n| Config dataclass | `XxxConfig`   | `GRPOConfig`, `FSDPConfig`          |\n| Engine class     | `XxxEngine`   | `FSDPEngine`, `ArchonEngine`        |\n| Workflow class   | `XxxWorkflow` | `RLVRWorkflow`, `MultiTurnWorkflow` |\n| Reward function  | `xxx_reward`  | `math_reward`, `code_reward`        |\n\n## Tensor Conventions\n\n- Shape convention: `[batch, seq_len, hidden]` or document clearly\n- Use `torch.Size` assertions for shape validation in debug\n- Prefer explicit dtype/device over implicit conversion\n\n## Import Style\n\n- Group: stdlib, third-party, areal (ruff handles order)\n- Avoid `from x import *` (CLAUDE.md rule)\n- Prefer explicit imports over module-level imports for large modules\n","isInternal":false,"tokens":752,"sizeBytes":3026},{"name":"distributed.md","path":".claude/rules/distributed.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.claude/rules/distributed.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\npaths:\n  - areal/engine/**\n  - areal/experimental/**\n  - areal/engine/fsdp_utils/**\n---\n\n# Distributed Code Rules\n\n## Process Group Management\n\n- **Never create global process group** in module-level code\n- Always pass `process_group` explicitly, don't rely on default\n- Use `dist.get_rank(group)` not `dist.get_rank()` when group matters\n- Clean up process groups in `__del__` or context manager\n\n## DeviceMesh & DTensor\n\n- Mesh dimension names must match `ArchonParallelDims`: `dp_shard`, `tp`, `cp`, `ep`,\n  `etp`\n- DTensor requires consistent mesh across all ranks\n- Use `DTensor.from_local()` with correct placements\n\n## Communication Patterns\n\n- **All-reduce**: Must be called by all ranks in the group\n- **Broadcast**: Specify `src` rank explicitly\n- **Barrier**: Avoid unless necessary (debugging only)\n- Check `NCCL_ASYNC_ERROR_HANDLING` for deadlock debugging\n\n## Common Pitfalls\n\n| Issue         | Cause                            | Fix                            |\n| ------------- | -------------------------------- | ------------------------------ |\n| Hang          | Mismatched collective calls      | Ensure all ranks call same op  |\n| Wrong results | Incorrect reduction op           | Check `ReduceOp` (SUM vs MEAN) |\n| OOM           | Unsharded tensor on wrong device | Verify DTensor placements      |\n\n## Debugging\n\n- Set `TORCH_DISTRIBUTED_DEBUG=DETAIL` for verbose logging\n- Use `NCCL_DEBUG=INFO` for NCCL-level issues\n- See `/debug-distributed` skill for comprehensive guide\n","frontmatter":{"paths":["areal/engine/**","areal/experimental/**","areal/engine/fsdp_utils/**"]},"isInternal":false,"tokens":343,"sizeBytes":1502},{"name":"testing.md","path":".claude/rules/testing.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.claude/rules/testing.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"---\npaths:\n  - '**/tests/**'\n  - '*_test.py'\n  - test_*.py\n---\n\n# Testing Rules\n\n## Pytest Markers\n\n| Marker                                  | When to Use          |\n| --------------------------------------- | -------------------- |\n| `@pytest.mark.slow`                     | Takes > 10 seconds   |\n| `@pytest.mark.asyncio`                  | Async test functions |\n| `@pytest.mark.skipif(cond, reason=...)` | Conditional skip     |\n| `@pytest.mark.parametrize(...)`         | Parameterized tests  |\n\n## Test Structure\n\n```python\ndef test_<what>_<condition>_<expected>():\n    \"\"\"Test that <what> does <expected> when <condition>.\"\"\"\n    # Arrange\n    ...\n    # Act\n    ...\n    # Assert\n    ...\n```\n\n## Mocking Distributed\n\n- Use `torch.distributed.fake_pg` for unit tests\n- Mock `dist.get_rank()` and `dist.get_world_size()` explicitly\n- Don't mock internals of FSDP/DTensor - use integration tests\n\n## GPU Test Constraints\n\n- **Always skip gracefully** when GPU unavailable:\n  ```python\n  CUDA_AVAILABLE = torch.cuda.is_available()\n\n  @pytest.mark.skipif(not CUDA_AVAILABLE, reason=\"CUDA not available\")\n  def test_gpu_feature():\n      ...\n  ```\n- Clean up GPU memory: `torch.cuda.empty_cache()` in fixtures\n- Use smallest possible model/batch for unit tests\n\n## Fixtures\n\n- Prefer `tmp_path` over manual temp directories\n- Use `monkeypatch` for environment variables\n- Scope expensive fixtures appropriately (`session` > `module` > `function`)\n\n## Assertions\n\n- Use `torch.testing.assert_close()` for tensor comparison\n- Specify `rtol`/`atol` explicitly for numerical tests\n- Avoid bare `assert tensor.equal()` - no useful error message\n","frontmatter":{"paths":["**/tests/**","*_test.py","test_*.py"]},"isInternal":false,"tokens":387,"sizeBytes":1641},{"name":"SKILL.md","path":".claude/skills/add-archon-model/SKILL.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.claude/skills/add-archon-model/SKILL.md","title":"add-archon-model","category":"anthropic-skill","format":"markdown","content":"---\nname: add-archon-model\ndescription: Guide for adding a new model to the Archon engine. Use when user wants to add support for a new HuggingFace model architecture in ArchonEngine.\n---\n\n# Add Archon Model\n\nAdd support for a new HuggingFace model architecture in the Archon training engine.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a model to Archon?\"\n- User wants to support a new model family (e.g., Llama, Mistral, DeepSeek) in\n  ArchonEngine\n- User mentions adding a new `ModelSpec` or model type for Archon\n\n## Prerequisites\n\nBefore starting, ensure:\n\n- The target model is available on HuggingFace (has `config.json` with `model_type`)\n- You know the HuggingFace model ID (e.g., `meta-llama/Llama-3-8B`)\n- The model uses a standard transformer architecture (decoder-only)\n\n## Step-by-Step Guide\n\n### Step 1: Analyze the Target Model Architecture\n\nRead the HuggingFace model's source code to extract key architecture information.\n\n**Action**: Fetch and analyze the model's HuggingFace configuration and modeling files.\n\n1. Read the model's `config.json` (via `AutoConfig.from_pretrained`) to identify:\n\n   - `model_type` string (this is the key used for registry lookup)\n   - All architecture hyperparameters (hidden_size, num_layers, etc.)\n   - Any model-specific fields (e.g., `qk_norm`, `attention_bias`, MoE fields)\n\n1. Read the HuggingFace `modeling_*.py` source to identify:\n\n   - **Attention variant**: Does it have Q/K norm? Attention bias? Sliding window?\n     Multi-latent attention?\n   - **FFN variant**: SwiGLU (gate_proj + up_proj + down_proj)? GeGLU? Standard MLP?\n   - **MoE support**: Does it have MoE layers? What router type? Shared experts?\n   - **RoPE variant**: Standard RoPE? YaRN? NTK-aware scaling? What is the inv_freq\n     formula?\n   - **Normalization**: RMSNorm or LayerNorm? Pre-norm or post-norm? Elementwise affine?\n   - **Weight tying**: Does `tie_word_embeddings` appear in config?\n   - **State dict key names**: What are the HF weight key naming conventions?\n\n1. Summarize findings in a checklist like:\n\n```\nTarget model: <name>\nHF model_type: \"<model_type>\" (and variants like \"<model_type>_moe\" if applicable)\nAttention: [standard GQA / with QK norm / with bias / sliding window / ...]\nFFN: [SwiGLU / GeGLU / standard MLP / ...]\nMoE: [no / yes - num_experts, top_k, shared_experts]\nRoPE: [standard / YaRN / NTK-aware / ...]\nNorm: [RMSNorm / LayerNorm] with [pre-norm / post-norm]\nWeight tying: [yes / no]\n```\n\n### Step 2: Select the Reference Model\n\nChoose the closest existing implementation as a starting point:\n\n| Target characteristics               | Reference | Why                                     |\n| ------------------------------------ | --------- | --------------------------------------- |\n| Dense-only, standard GQA, no QK norm | `qwen2`   | Simplest baseline, pure dense           |\n| Has QK norm, or has MoE support      | `qwen3`   | Supports QK norm + MoE + shared experts |\n\n**Action**: Copy the reference model directory as the starting point:\n\n```\nareal/experimental/models/archon/<model>/\n  __init__.py\n  spec.py\n  model/\n    args.py\n    model.py\n    rope.py\n    state_dict_adapter.py\n  infra/\n    parallelize.py\n```\n\n### Step 3: Implement `args.py`\n\nAdapt `<Model>ModelArgs` to match the target model's HuggingFace config fields.\n\n**Key changes from reference**:\n\n1. Update the `@dataclass` fields to match the target model's hyperparameters:\n\n   - Field names should use Archon conventions (`dim`, `n_layers`, `n_heads`,\n     `n_kv_heads`, `vocab_size`, `head_dim`, `hidden_dim`, `norm_eps`, `rope_theta`,\n     etc.)\n   - Default values should match the smallest variant of the target model\n   - Add model-specific fields (e.g., `attention_bias`, `qk_norm`, `sliding_window`)\n\n1. Update `from_hf_config()` to correctly map HuggingFace config attributes:\n\n   - Use `getattr(hf_config, \"field_name\", default)` for optional fields\n   - Handle variant-specific fields (e.g., MoE fields only present in MoE variants)\n   - The method must return an instance of the model args class\n\n**Critical**: Verify every field mapping against the HF model's `config.json`. Incorrect\nmappings here cause silent errors downstream.\n\n**Base class contract** (`BaseModelArgs`):\n\n```python\n@dataclass\nclass <Model>ModelArgs(BaseModelArgs):\n    # ... model-specific fields ...\n\n    @classmethod\n    def from_hf_config(\n        cls,\n        hf_config: PretrainedConfig,\n        is_critic: bool = False,\n        **kwargs,\n    ) -> <Model>ModelArgs:\n        # Map HF config fields to Archon model args\n        ...\n```\n\n### Step 4: Implement `model.py`\n\nAdapt the model architecture to match the target model.\n\n**Key components to adapt**:\n\n1. **Normalization** (`RMSNorm` or similar):\n\n   - Check if `elementwise_affine` is configurable\n   - Check the epsilon default value\n   - If the model uses `LayerNorm`, implement accordingly\n\n1. **Attention** module:\n\n   - Q/K/V projection: Check bias presence (`nn.Linear(..., bias=True/False)`)\n   - QK norm: Add `q_norm`/`k_norm` if the model has them, remove if it doesn't\n   - GQA: `n_kv_heads` \\< `n_heads` for grouped-query attention\n   - Ulysses SP: Keep the `set_cp_group` / `_sp_enabled` pattern from the reference\n   - Output projection: Check bias presence\n\n1. **FeedForward** module:\n\n   - SwiGLU: `w2(silu(w1(x)) * w3(x))` -- most common for modern LLMs\n   - Check bias in linear layers\n   - For MoE models: `MoE` module replaces `FeedForward` on designated layers\n\n1. **TransformerBlock**: Pre-norm (most modern LLMs) vs post-norm\n\n   - MoE layer detection via `_is_moe_layer()` if applicable\n\n1. **Top-level Model** (`<Model>Model(BaseArchonModel)`):\n\n   - `tok_embeddings`, `layers` (as `ModuleDict`), `norm`, `output`/`score`\n   - `init_weights()`: Match initialization scheme from HF\n   - `init_buffers()`: RoPE cache + MoE buffers\n   - `forward()`: Must follow `BaseArchonModel` signature:\n     `(tokens, positions, cu_seqlens, max_seqlen, tree_attn_meta=None) -> Tensor`\n\n**Base class contract** (`BaseArchonModel`):\n\n```python\nclass <Model>Model(BaseArchonModel):\n    def forward(self, tokens, positions, cu_seqlens, max_seqlen, tree_attn_meta=None) -> torch.Tensor: ...\n    def init_weights(self) -> None: ...\n    def init_buffers(self, buffer_device) -> None: ...\n```\n\n### Step 5: Implement `rope.py`\n\nHandle the rotary position embedding variant.\n\n**Options**:\n\n1. **Standard RoPE** (same as qwen2/qwen3): Re-export from qwen2:\n\n   ```python\n   from areal.experimental.models.archon.qwen2.model.rope import (\n       apply_rotary_emb,\n       precompute_rope_cache,\n       repeat_kv,\n       reshape_for_broadcast,\n       rotate_half,\n   )\n   ```\n\n1. **Custom RoPE** (YaRN, NTK-aware, etc.): Implement custom `precompute_rope_cache()`\n   and `apply_rotary_emb()` functions. The key difference is usually in how `inv_freq`\n   is computed (scaling factors, interpolation, etc.).\n\n### Step 6: Implement `state_dict_adapter.py`\n\nMap between HuggingFace and Archon weight key names.\n\n**This is the most error-prone step.** The adapter must correctly handle:\n\n1. **Key name mapping** (`from_hf_map` dict):\n\n   - Embedding: `model.embed_tokens.weight` -> `tok_embeddings.weight`\n   - Attention: `model.layers.{}.self_attn.q_proj.weight` ->\n     `layers.{}.attention.wq.weight`\n   - FFN: `model.layers.{}.mlp.gate_proj.weight` -> `layers.{}.feed_forward.w1.weight`\n   - Norms: `model.layers.{}.input_layernorm.weight` ->\n     `layers.{}.attention_norm.weight`\n   - Output: `lm_head.weight` -> `output.weight`\n   - Skip keys (set to `None`): `rotary_emb.inv_freq` (computed at runtime)\n   - Model-specific keys: bias terms, QK norm weights, etc.\n\n1. **Reverse mapping** (`to_hf_map`): Auto-generated from `from_hf_map`\n\n1. **MoE expert weights** (if applicable): 3D\\<->2D conversion for expert weights. Copy\n   the MoE handling from qwen3 if the model has MoE.\n\n1. **Weight tying**: Skip `output.weight` during `to_hf()` if `tie_word_embeddings=True`\n\n**Verification approach**: After implementation, the adapter should satisfy:\n\n```python\n# Roundtrip: archon -> hf -> archon preserves all keys\nhf_sd = adapter.to_hf(archon_sd)\nroundtrip_sd = adapter.from_hf(hf_sd)\nassert set(roundtrip_sd.keys()) == set(archon_sd.keys())\n```\n\n**Base class contract** (`BaseStateDictAdapter`):\n\n```python\nclass <Model>StateDictAdapter(BaseStateDictAdapter):\n    def from_hf(self, hf_state_dict) -> dict[str, Any]: ...\n    def to_hf(self, archon_state_dict) -> dict[str, Any]: ...\n    def convert_single_to_hf(self, name, tensor) -> list[tuple[str, torch.Tensor]]: ...\n```\n\n### Step 7: Implement `parallelize.py`\n\nDefine the parallelization strategy for the model.\n\n**The parallelize function** applies parallelism in this order:\n\n1. TP (Tensor Parallelism) -- shard attention/FFN across devices\n1. EP (Expert Parallelism) -- for MoE models only\n1. CP (Context Parallelism / Ulysses SP) -- sequence parallelism\n1. AC (Activation Checkpointing) -- memory optimization\n1. torch.compile -- compilation optimization\n1. FSDP (Fully Sharded Data Parallelism) -- data parallelism\n\n**Key adaptations by model architecture**:\n\n- **Attention with QK norm**: wq/wk use `use_local_output=False` (DTensor output for\n  norm), add `SequenceParallel(sequence_dim=2)` for q_norm/k_norm\n- **Attention without QK norm**: wq/wk/wv all use `use_local_output=True`\n- **Attention with bias**: Bias terms follow the same parallel plan as their weights\n- **MoE layers**: Separate TP plan for MoE input/output, router gate, and expert\n  weights. Copy from qwen3's `apply_moe_ep_tp()` and `apply_non_moe_tp()`\n- **Dense-only models**: Simpler plan without MoE handling. Copy from qwen2\n\n**Function signature** (must match `ParallelizeFn` protocol):\n\n```python\ndef parallelize_<model>(\n    model: nn.Module,\n    parallel_dims: ArchonParallelDims,\n    param_dtype: torch.dtype = torch.bfloat16,\n    reduce_dtype: torch.dtype = torch.float32,\n    loss_parallel: bool = True,\n    cpu_offload: bool = False,\n    reshard_after_forward_policy: str = \"default\",\n    ac_config: ActivationCheckpointConfig | None = None,\n    enable_compile: bool = True,\n) -> nn.Module:\n```\n\n### Step 8: Create `spec.py` and Register\n\nAssemble the `ModelSpec` and register it.\n\n```python\nfrom areal.experimental.models.archon.model_spec import ModelSpec, register_model_spec\nfrom areal.experimental.models.archon.pipeline_parallel import pipeline_llm\nfrom areal.experimental.models.archon.<model>.infra.parallelize import parallelize_<model>\nfrom areal.experimental.models.archon.<model>.model.args import <Model>ModelArgs\nfrom areal.experimental.models.archon.<model>.model.model import <Model>Model\nfrom areal.experimental.models.archon.<model>.model.state_dict_adapter import (\n    <Model>StateDictAdapter,\n)\n\n<MODEL>_SPEC = ModelSpec(\n    name=\"<Model>\",\n    model_class=<Model>Model,\n    model_args_class=<Model>ModelArgs,\n    state_dict_adapter_class=<Model>StateDictAdapter,\n    parallelize_fn=parallelize_<model>,\n    supported_model_types=frozenset({\"<model_type>\"}),  # From HF config.json\n    pipelining_fn=pipeline_llm,\n)\n\n# Auto-register when module is imported\nregister_model_spec(<MODEL>_SPEC)\n\n__all__ = [\"<MODEL>_SPEC\"]\n```\n\n**Note**: `supported_model_types` should include all HF `model_type` strings that this\nimplementation handles (e.g., `{\"qwen3\", \"qwen3_moe\"}` for Qwen3).\n\n### Step 9: Register in `__init__.py`\n\nAdd the import to `areal/experimental/models/archon/__init__.py`:\n\n```python\nfrom areal.experimental.models.archon.<model> import spec as <model>_spec  # noqa: F401\n```\n\nThis triggers auto-registration when the module is imported.\n\n### Step 10: Verify and Test\n\nVerification should be done in stages, adapting based on available hardware and the test\npatterns in `tests/experimental/archon/`.\n\n**Before writing tests**, examine the existing test files to understand current\npatterns:\n\n```\ntests/experimental/archon/\n  conftest.py             -- Pytest configuration (version checks)\n  utils.py                -- Shared utilities (model loading, comparison)\n  test_qwen3_args.py      -- Args unit tests (CPU-only)\n  test_state_dict_adapter.py  -- State dict roundtrip tests\n  test_weight_sync.py     -- Weight completeness tests (meta device)\n  test_forward.py         -- Forward precision comparison (single GPU)\n  ...\n```\n\n**Test stages** (write tests appropriate for the model's complexity):\n\n#### Stage 1: Args Tests (CPU-only, always write these)\n\nTest `from_hf_config()` with mock HuggingFace configs:\n\n```python\n# Pattern: Create mock PretrainedConfig, verify args mapping\nfrom unittest.mock import MagicMock\n\ndef test_args_from_hf_config():\n    hf_config = MagicMock()\n    hf_config.hidden_size = 4096\n    hf_config.num_hidden_layers = 32\n    # ... set all required fields\n    args = <Model>ModelArgs.from_hf_config(hf_config)\n    assert args.dim == 4096\n    assert args.n_layers == 32\n```\n\n#### Stage 2: State Dict Adapter Tests (CPU-only)\n\nTest key mapping roundtrip:\n\n```python\ndef test_state_dict_roundtrip():\n    # Create adapter with mock config\n    adapter = <Model>StateDictAdapter(mock_config)\n    # Create fake archon state dict with expected keys\n    archon_sd = {\"tok_embeddings.weight\": torch.randn(vocab, dim), ...}\n    # Roundtrip\n    hf_sd = adapter.to_hf(archon_sd)\n    roundtrip = adapter.from_hf(hf_sd)\n    assert set(roundtrip.keys()) == set(archon_sd.keys())\n```\n\n#### Stage 3: Weight Completeness (meta device, CPU-only)\n\nVerify all model parameters have HF mappings:\n\n```python\ndef test_weight_completeness():\n    # Create model on meta device\n    with torch.device(\"meta\"):\n        model = <Model>Model(args)\n    adapter = <Model>StateDictAdapter(hf_config)\n    # Check every archon param has a HF mapping\n    for name, _ in model.named_parameters():\n        hf_pairs = adapter.convert_single_to_hf(name, torch.empty(0))\n        assert len(hf_pairs) > 0, f\"No HF mapping for {name}\"\n```\n\n#### Stage 4: Forward Precision (single GPU, if available)\n\nCompare Archon model output against HuggingFace reference:\n\n```python\n@pytest.mark.skipif(not torch.cuda.is_available(), reason=\"Requires CUDA\")\ndef test_forward_matches_hf():\n    # Load both HF and Archon models\n    # Run forward on same input\n    # Compare logits within tolerance\n```\n\n**Important**: Do NOT hardcode the test categories. Inspect the existing test files in\n`tests/experimental/archon/` and follow the same patterns, fixtures, and markers. Adapt\ntest scope to the model's specific features (e.g., add MoE-specific tests only if the\nmodel has MoE).\n\n## Reference Implementations\n\n| Model | Directory                                 | Features                                                |\n| ----- | ----------------------------------------- | ------------------------------------------------------- |\n| Qwen2 | `areal/experimental/models/archon/qwen2/` | Dense, attention bias, no QK norm                       |\n| Qwen3 | `areal/experimental/models/archon/qwen3/` | Dense + MoE, QK norm, no attention bias, shared experts |\n\n## Architecture Decision Map\n\n| Feature             | qwen2    | qwen3                      | What to check in target model                            |\n| ------------------- | -------- | -------------------------- | -------------------------------------------------------- |\n| Attention bias      | Yes      | No                         | `attention_bias` in HF config                            |\n| QK norm             | No       | Yes                        | `qk_norm` in HF config or QKNorm module in modeling file |\n| MoE                 | No       | Yes                        | `num_experts`/`num_local_experts` in HF config           |\n| Shared experts      | No       | Yes                        | `num_shared_experts` in HF config                        |\n| Decoder sparse step | No       | Yes                        | `decoder_sparse_step` in HF config                       |\n| Weight tying        | Both     | Both                       | `tie_word_embeddings` in HF config                       |\n| RoPE                | Standard | Standard (re-export qwen2) | Check inv_freq formula in HF modeling code               |\n\n## Common Mistakes\n\n- Not mapping all HF keys in `state_dict_adapter.py` (causes silent weight drops)\n- Wrong `from_hf_config()` field mapping (uses wrong HF config attribute name)\n- Forgetting to handle `None` keys in `from_hf_map` (keys to skip like\n  `rotary_emb.inv_freq`)\n- Missing MoE expert weight 3D\\<->2D conversion when model has MoE\n- Wrong TP plan for attention with/without QK norm (`use_local_output` must match)\n- Forgetting to add import line in `areal/experimental/models/archon/__init__.py`\n- Not including all `model_type` variants in `supported_model_types` frozenset\n- Using `print` instead of `areal.utils.logging.getLogger()`\n\n## File Checklist\n\nAfter completion, verify all files exist and are consistent:\n\n- [ ] `areal/experimental/models/archon/<model>/__init__.py`\n- [ ] `areal/experimental/models/archon/<model>/spec.py` -- ModelSpec + register\n- [ ] `areal/experimental/models/archon/<model>/model/args.py` -- ModelArgs +\n  from_hf_config\n- [ ] `areal/experimental/models/archon/<model>/model/model.py` -- Model + Attention +\n  FFN\n- [ ] `areal/experimental/models/archon/<model>/model/rope.py` -- RoPE (or re-export)\n- [ ] `areal/experimental/models/archon/<model>/model/state_dict_adapter.py` -- Key\n  mapping\n- [ ] `areal/experimental/models/archon/<model>/infra/parallelize.py` -- Parallel\n  strategy\n- [ ] `areal/experimental/models/archon/__init__.py` -- Import line added\n- [ ] `tests/experimental/archon/test_<model>_*.py` -- Tests\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/skills/add-archon-model/SKILL.md\nInvocation: /add-archon-model <model_name>\n\n## Purpose\n\nSemi-automated guide for adding new model architectures to the Archon training engine.\nUnlike simpler skills (add-reward, add-dataset), this skill actively guides Claude to:\n1. Analyze HuggingFace source code to extract architecture details\n2. Select the closest reference implementation (qwen2 or qwen3)\n3. Generate code skeletons adapted to the target architecture\n4. Create appropriate tests based on existing test patterns\n\n## How to Update\n\n### When New Reference Models Are Added\n1. Add to \"Reference Implementations\" table\n2. Update \"Architecture Decision Map\" with new feature columns\n3. Update Step 2 (reference selection) with new options\n\n### When Base Classes Change\n1. Update contract signatures in Steps 3, 4, 6, 7\n2. Update file checklist if new files are required\n\n### When ModelSpec Changes\n1. Update Step 8 with new ModelSpec fields\n2. Update spec.py template\n\n### When Test Patterns Change\n1. Update Step 10 with new test patterns\n2. Do NOT hardcode test categories -- keep it flexible\n\n### Important Design Decisions\n- This skill is SEMI-AUTOMATED: Claude should read HF source and generate code,\n  not just provide templates for the user to fill in manually\n- The skill references existing test files rather than hardcoding test categories,\n  ensuring it stays current as the test suite evolves\n- Reference model selection (qwen2 vs qwen3) is based on MoE and QK norm presence\n\n================================================================================\n-->\n","frontmatter":{"name":"add-archon-model","description":"Guide for adding a new model to the Archon engine. Use when user wants to add support for a new HuggingFace model architecture in ArchonEngine."},"isInternal":false,"tokens":4772,"sizeBytes":19502},{"name":"SKILL.md","path":".claude/skills/add-dataset/SKILL.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.claude/skills/add-dataset/SKILL.md","title":"add-dataset","category":"anthropic-skill","format":"markdown","content":"---\nname: add-dataset\ndescription: Guide for adding a new dataset loader to AReaL. Use when user wants to add a new dataset.\n---\n\n# Add Dataset\n\nAdd a new dataset loader to AReaL.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a dataset?\"\n- User wants to integrate a new dataset\n- User mentions creating a dataset loader\n\n## Step-by-Step Guide\n\n### Step 1: Create Dataset File\n\nCreate `areal/dataset/<name>.py`:\n\n```python\nfrom datasets import Dataset, load_dataset\n\n\ndef get_<name>_sft_dataset(\n    path: str,\n    split: str,\n    tokenizer,\n    max_length: int | None = None,\n) -> Dataset:\n    \"\"\"Load dataset for SFT training.\n\n    Args:\n        path: Path to dataset (HuggingFace hub or local path)\n        split: Dataset split (train/validation/test)\n        tokenizer: Tokenizer for processing\n        max_length: Maximum sequence length (optional)\n\n    Returns:\n        HuggingFace Dataset with processed samples\n    \"\"\"\n    dataset = load_dataset(path=path, split=split)\n\n    def process(sample):\n        # Tokenize the full sequence (prompt + response)\n        seq_token = tokenizer.encode(\n            sample[\"question\"] + sample[\"answer\"] + tokenizer.eos_token\n        )\n        prompt_token = tokenizer.encode(sample[\"question\"])\n        # Loss mask: 0 for prompt, 1 for response\n        loss_mask = [0] * len(prompt_token) + [1] * (len(seq_token) - len(prompt_token))\n        return {\"input_ids\": seq_token, \"loss_mask\": loss_mask}\n\n    dataset = dataset.map(process).remove_columns([\"question\", \"answer\"])\n\n    if max_length is not None:\n        dataset = dataset.filter(lambda x: len(x[\"input_ids\"]) <= max_length)\n\n    return dataset\n\n\ndef get_<name>_rl_dataset(\n    path: str,\n    split: str,\n    tokenizer,\n    max_length: int | None = None,\n) -> Dataset:\n    \"\"\"Load dataset for RL training.\n\n    Args:\n        path: Path to dataset\n        split: Dataset split\n        tokenizer: Tokenizer for length filtering\n        max_length: Maximum sequence length\n\n    Returns:\n        HuggingFace Dataset with prompts and answers for reward computation\n    \"\"\"\n    dataset = load_dataset(path=path, split=split)\n\n    def process(sample):\n        messages = [\n            {\n                \"role\": \"user\",\n                \"content\": sample[\"question\"],\n            }\n        ]\n        return {\"messages\": messages, \"answer\": sample[\"answer\"]}\n\n    dataset = dataset.map(process).remove_columns([\"question\"])\n\n    if max_length is not None:\n\n        def filter_length(sample):\n            content = sample[\"messages\"][0][\"content\"]\n            tokens = tokenizer.encode(content)\n            return len(tokens) <= max_length\n\n        dataset = dataset.filter(filter_length)\n\n    return dataset\n```\n\n### Step 2: Register in __init__.py\n\nUpdate `areal/dataset/__init__.py`:\n\n```python\n# Add to VALID_DATASETS\nVALID_DATASETS = [\n    # ... existing datasets\n    \"<name>\",\n]\n\n# Add to _get_custom_dataset function\ndef _get_custom_dataset(name: str, ...):\n    # ... existing code\n    elif name == \"<name>\":\n        from areal.dataset.<name> import get_<name>_sft_dataset, get_<name>_rl_dataset\n        if dataset_type == \"sft\":\n            return get_<name>_sft_dataset(path, split, max_length, tokenizer)\n        else:\n            return get_<name>_rl_dataset(path, split, max_length, tokenizer)\n```\n\n### Step 3: Add Config (Optional)\n\nIf the dataset needs special configuration, add to `areal/api/cli_args.py`:\n\n```python\n@dataclass\nclass TrainDatasetConfig:\n    # ... existing fields\n    <name>_specific_field: Optional[str] = None\n```\n\n### Step 4: Add Tests\n\nCreate `tests/test_<name>_dataset.py`:\n\n```python\nimport pytest\nfrom areal.dataset.<name> import get_<name>_sft_dataset, get_<name>_rl_dataset\n\ndef test_sft_dataset_loads(tokenizer):\n    dataset = get_<name>_sft_dataset(\"path/to/data\", split=\"train\", tokenizer=tokenizer)\n    assert len(dataset) > 0\n    assert \"input_ids\" in dataset.column_names\n    assert \"loss_mask\" in dataset.column_names\n\ndef test_rl_dataset_loads(tokenizer):\n    dataset = get_<name>_rl_dataset(\"path/to/data\", split=\"train\", tokenizer=tokenizer)\n    assert len(dataset) > 0\n    assert \"messages\" in dataset.column_names\n    assert \"answer\" in dataset.column_names\n```\n\n## Reference Implementations\n\n| Dataset    | File                               | Description              |\n| ---------- | ---------------------------------- | ------------------------ |\n| GSM8K      | `areal/dataset/gsm8k.py`           | Math word problems       |\n| Geometry3K | `areal/dataset/geometry3k.py`      | Geometry problems        |\n| CLEVR      | `areal/dataset/clevr_count_70k.py` | Visual counting          |\n| HH-RLHF    | `areal/dataset/hhrlhf.py`          | Helpfulness/Harmlessness |\n| TORL       | `areal/dataset/torl_data.py`       | Tool-use RL              |\n\n## Required Fields\n\n### SFT Dataset\n\n```python\n{\n    \"messages\": [\n        {\"role\": \"user\", \"content\": \"...\"},\n        {\"role\": \"assistant\", \"content\": \"...\"},\n    ]\n}\n```\n\n### RL Dataset\n\n```python\n{\n    \"messages\": [\n        {\"role\": \"user\", \"content\": \"...\"},\n    ],\n    \"answer\": \"ground_truth_for_reward\",\n    # Optional metadata for reward function\n}\n```\n\n## Common Mistakes\n\n- ❌ Returning `List[Dict]` instead of HuggingFace `Dataset`\n- ❌ Using Python loops instead of `dataset.map()`/`filter()`\n- ❌ Missing `\"messages\"` field for RL datasets\n- ❌ Wrong message format (should be list of dicts with `role` and `content`)\n- ❌ Not registering in `__init__.py`\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/skills/add-dataset/SKILL.md\nInvocation: /add-dataset <name>\n\n## Purpose\n\nStep-by-step guide for adding new dataset loaders.\n\n## How to Update\n\n### When Dataset API Changes\n1. Update the code templates\n2. Update required fields section\n3. Update registration example\n\n### When New Dataset Types Added\n1. Add to \"Reference Implementations\" table\n2. Add any new required fields\n\n================================================================================\n-->\n","frontmatter":{"name":"add-dataset","description":"Guide for adding a new dataset loader to AReaL. Use when user wants to add a new dataset."},"isInternal":false,"tokens":1444,"sizeBytes":6259},{"name":"SKILL.md","path":".claude/skills/add-reward/SKILL.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.claude/skills/add-reward/SKILL.md","title":"add-reward","category":"anthropic-skill","format":"markdown","content":"---\nname: add-reward\ndescription: Guide for adding a new reward function to AReaL. Use when user wants to create a reward function.\n---\n\n# Add Reward\n\nAdd a new reward function to AReaL.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a reward function?\"\n- User wants to implement custom rewards\n- User mentions reward computation\n\n## Step-by-Step Guide\n\n### Step 1: Create Reward File\n\nCreate `areal/reward/<name>.py`:\n\n```python\nfrom typing import Any\n\nfrom areal.utils import logging\n\nlogger = logging.getLogger(\"MyReward\")\n\n\ndef <name>_reward_fn(\n    prompt: str,\n    completions: str,\n    prompt_ids,\n    completion_ids,\n    answer: str | None = None,\n    **kwargs: Any,\n) -> float:\n    \"\"\"Compute reward for a single completion.\n\n    Args:\n        prompt: Prompt string\n        completions: Completion string (model output)\n        prompt_ids: Tokenized prompt IDs\n        completion_ids: Tokenized completion IDs\n        answer: Ground truth answer from dataset (optional)\n        **kwargs: Additional data from dataset\n\n    Returns:\n        Reward value (float), typically 0.0 or 1.0\n    \"\"\"\n    try:\n        # Extract answer from completion\n        extracted = _extract_answer(completions)\n\n        # Compare with ground truth\n        if answer is not None and extracted == str(answer):\n            return 1.0\n        return 0.0\n    except Exception:\n        logger.warning(\"Exception in reward computation\", exc_info=True)\n        return 0.0\n\n\ndef _extract_answer(completion: str) -> str:\n    \"\"\"Extract the answer from a completion string.\n\n    Implement your extraction logic here.\n    \"\"\"\n    # Example: Extract content from \\boxed{}\n    import re\n\n    match = re.search(r\"\\\\boxed\\{([^}]+)\\}\", completion)\n    if match:\n        return match.group(1).strip()\n    return completion.strip()\n```\n\n### Step 2: Register in __init__.py\n\nUpdate `areal/reward/__init__.py`:\n\n```python\n# Add to VALID_REWARD_FN\nVALID_REWARD_FN = [\n    # ... existing reward functions\n    \"<name>\",\n]\n\n# Add to get_reward_fn function\ndef get_reward_fn(name: str, **kwargs):\n    # ... existing code\n    elif name == \"<name>\":\n        from areal.reward.<name> import <name>_reward_fn\n        return <name>_reward_fn\n```\n\n### Step 3: Handle Blocking Operations\n\nIf your reward function uses blocking operations (e.g., API calls, model inference), the\nworkflow will wrap it with `AsyncRewardWrapper`:\n\n```python\n# In your workflow\nfrom areal.reward import AsyncRewardWrapper\n\nself.reward_fn = AsyncRewardWrapper(reward_fn)\n\n# Then call it asynchronously\nrewards = await self.reward_fn(prompt, completions, **data)\n```\n\n### Step 4: Add Tests\n\nCreate `tests/test_<name>_reward.py`:\n\n```python\nimport pytest\nfrom areal.reward.<name> import <name>_reward_fn\n\ndef test_reward_correct_answer():\n    reward = <name>_reward_fn(\n        prompt=\"What is 2+2?\",\n        completions=\"The answer is \\\\boxed{4}\",\n        prompt_ids=None,\n        completion_ids=None,\n        answer=\"4\",\n    )\n    assert reward == 1.0\n\ndef test_reward_wrong_answer():\n    reward = <name>_reward_fn(\n        prompt=\"What is 2+2?\",\n        completions=\"The answer is \\\\boxed{5}\",\n        prompt_ids=None,\n        completion_ids=None,\n        answer=\"4\",\n    )\n    assert reward == 0.0\n```\n\n## Reference Implementations\n\n| Reward     | File                              | Description                  |\n| ---------- | --------------------------------- | ---------------------------- |\n| GSM8K      | `areal/reward/gsm8k.py`           | Math answer verification     |\n| Geometry3K | `areal/reward/geometry3k.py`      | Geometry answer verification |\n| CLEVR      | `areal/reward/clevr_count_70k.py` | Counting verification        |\n| MathVerify | `areal/reward/math_verify.py`     | General math verification    |\n\n## Function Signature\n\nAll reward functions must follow this signature:\n\n```python\ndef reward_fn(\n    prompt: str,               # Input prompt string\n    completions: str,          # Model completion string\n    prompt_ids,                # Tokenized prompt\n    completion_ids,            # Tokenized completion\n    **kwargs: Any,             # Additional data from dataset (e.g., answer)\n) -> float:                    # Reward value (typically 0.0 or 1.0)\n```\n\n**Note**: The reward function is called once per sample. Batching is handled by\n`AsyncRewardWrapper` in the workflow.\n\n## Key Requirements\n\n1. **Deterministic**: Same inputs should produce same outputs\n1. **Return float**: Output is a single float value per sample\n1. **No blocking in async context**: Use `AsyncRewardWrapper` if needed\n1. **Logging**: Use `areal.utils.logging`, not `print`\n1. **Handle exceptions**: Return 0.0 on error, don't raise\n\n## Common Mistakes\n\n- ❌ Returning a tensor instead of a float\n- ❌ Expecting batched inputs (reward is called per sample)\n- ❌ Non-deterministic behavior\n- ❌ Blocking operations without `AsyncRewardWrapper`\n- ❌ Raising exceptions instead of returning 0.0\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/skills/add-reward/SKILL.md\nInvocation: /add-reward <name>\n\n## Purpose\n\nStep-by-step guide for adding new reward functions.\n\n## How to Update\n\n### When Reward API Changes\n1. Update the function signature section\n2. Update the code template\n3. Update key requirements\n\n### When New Reward Patterns Emerge\n1. Add to \"Reference Implementations\" table\n2. Add examples for new patterns\n\n================================================================================\n-->\n","frontmatter":{"name":"add-reward","description":"Guide for adding a new reward function to AReaL. Use when user wants to create a reward function."},"isInternal":false,"tokens":1303,"sizeBytes":5735},{"name":"SKILL.md","path":".claude/skills/add-unit-tests/SKILL.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.claude/skills/add-unit-tests/SKILL.md","title":"add-unit-tests","category":"anthropic-skill","format":"markdown","content":"---\nname: add-unit-tests\ndescription: Guide for adding unit tests to AReaL. Use when user wants to add tests for new functionality or increase test coverage.\n---\n\n# Add Unit Tests\n\nAdd unit tests to AReaL following the project's testing conventions.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add tests?\"\n- User wants to increase test coverage\n- User needs to write tests for new functionality\n- User wants to understand AReaL testing patterns\n\n## Step-by-Step Guide\n\n### Step 1: Understand Test Types\n\nAReaL has two main test categories:\n\n| Test Type             | Purpose                            | Location Pattern                   | How It Runs                                |\n| --------------------- | ---------------------------------- | ---------------------------------- | ------------------------------------------ |\n| **Unit Tests**        | Test individual functions/modules  | `tests/test_<module>_<feature>.py` | Directly via pytest                        |\n| **Distributed Tests** | Test distributed/parallel behavior | `tests/torchrun/run_*.py`          | Via torchrun (called by pytest subprocess) |\n\n**Note**: All tests are invoked via pytest. Distributed tests use `torchrun` but are\nstill called from pytest test files.\n\n### Step 2: Create Test File Structure\n\nCreate test file with naming convention: `test_<module>_<feature>.py`\n\n```python\nimport pytest\nimport torch\n\n# Import the module to test\nfrom areal.dataset.gsm8k import get_gsm8k_sft_dataset\nfrom tests.utils import get_dataset_path  # Optional test utilities\n# For mocking tokenizer: from unittest.mock import MagicMock\n```\n\n### Step 3: Write Test Functions\n\nFollow Arrange-Act-Assert pattern:\n\n```python\ndef test_function_under_condition_returns_expected():\n    \"\"\"Test that function returns expected value under condition.\"\"\"\n    # Arrange\n    input_data = 5\n    expected_output = 10\n\n    # Act\n    result = function_under_test(input_data)\n\n    # Assert\n    assert result == expected_output\n```\n\n### Step 4: Add Pytest Markers and CI Strategy\n\nUse appropriate pytest markers:\n\n| Marker                                  | When to Use                                                  |\n| --------------------------------------- | ------------------------------------------------------------ |\n| `@pytest.mark.slow`                     | Test takes > 10 seconds (excluded from CI by default)        |\n| `@pytest.mark.ci`                       | Slow test that must run in CI (use with `@pytest.mark.slow`) |\n| `@pytest.mark.asyncio`                  | Async test functions                                         |\n| `@pytest.mark.skipif(cond, reason=...)` | Conditional skip                                             |\n| `@pytest.mark.parametrize(...)`         | Parameterized tests                                          |\n\n**CI Test Strategy**:\n\n- `@pytest.mark.slow`: Excluded from CI by default (CI runs `pytest -m \"not slow\"`)\n- `@pytest.mark.slow` + `@pytest.mark.ci`: Slow but must run in CI\n- No marker: Runs in CI (fast unit tests)\n\n```python\n@pytest.mark.asyncio\nasync def test_async_function():\n    result = await async_function()\n    assert result == expected\n\n@pytest.mark.skipif(not torch.cuda.is_available(), reason=\"CUDA not available\")\ndef test_gpu_feature():\n    tensor = torch.tensor([1, 2, 3], device=\"cuda\")\n    # ... assertions\n\n@pytest.mark.parametrize(\"batch_size\", [1, 4, 16])\ndef test_with_parameters(batch_size):\n    # Parameterized test\n\n@pytest.mark.slow\ndef test_slow_function():\n    # Excluded from CI by default\n\n@pytest.mark.slow\n@pytest.mark.ci\ndef test_slow_but_required_in_ci():\n    # Slow but must run in CI\n```\n\n### Step 5: Mock Distributed Environment\n\nFor unit tests that need distributed mocks:\n\n```python\nimport torch.distributed as dist\n\ndef test_distributed_function(monkeypatch):\n    monkeypatch.setattr(dist, \"get_rank\", lambda: 0)\n    monkeypatch.setattr(dist, \"get_world_size\", lambda: 2)\n    result = distributed_function()\n    assert result == expected\n```\n\n### Step 6: Handle GPU Dependencies\n\nAlways skip gracefully when GPU unavailable:\n\n```python\nCUDA_AVAILABLE = torch.cuda.is_available()\n\n@pytest.mark.skipif(not CUDA_AVAILABLE, reason=\"CUDA not available\")\ndef test_gpu_function():\n    tensor = torch.tensor([1, 2, 3], device=\"cuda\")\n    # ... assertions\n```\n\n## Key Requirements (Based on testing.md)\n\n### Mocking Distributed\n\n- Use `torch.distributed.fake_pg` for unit tests\n- Mock `dist.get_rank()` and `dist.get_world_size()` explicitly\n- Don't mock internals of FSDP/DTensor\n\n### GPU Test Constraints\n\n- **Always skip gracefully** when GPU unavailable\n- Clean up GPU memory: `torch.cuda.empty_cache()` in fixtures\n- Use smallest possible model/batch for unit tests\n\n### Assertions\n\n- Use `torch.testing.assert_close()` for tensor comparison\n- Specify `rtol`/`atol` explicitly for numerical tests\n- Avoid bare `assert tensor.equal()` - no useful error message\n\n## Reference Implementations\n\n| Test File                        | Description                            | Key Patterns                                      |\n| -------------------------------- | -------------------------------------- | ------------------------------------------------- |\n| `tests/test_utils.py`            | Utility function tests                 | Fixtures, parametrized tests                      |\n| `tests/test_examples.py`         | Integration tests with dataset loading | Dataset path resolution, success pattern matching |\n| `tests/test_fsdp_engine_nccl.py` | Distributed tests                      | Torchrun integration                              |\n\n## Common Mistakes\n\n- ❌ **Missing test file registration**: Ensure file follows `test_*.py` naming\n- ❌ **GPU dependency without skip**: Always use `@pytest.mark.skipif` for GPU tests\n- ❌ **Incorrect tensor comparisons**: Use `torch.testing.assert_close()` not\n  `assert tensor.equal()`\n- ❌ **Memory leaks in GPU tests**: Clean up with `torch.cuda.empty_cache()`\n- ❌ **Mocking too much**: Don't mock FSDP/DTensor internals\n- ❌ **Unclear test names**: Follow `test_<what>_<condition>_<expected>` pattern\n- ❌ **No docstrings**: Add descriptive docstrings to test functions\n\n## Integration with Other Skills\n\nThis skill complements other AReaL development skills:\n\n- **After `/add-dataset`**: Add tests for new dataset loaders\n- **After `/add-workflow`**: Add tests for new workflows\n- **After `/add-reward`**: Add tests for new reward functions\n- **With `planner` agent**: Reference this skill when planning test implementation\n\n## Running Tests\n\n```bash\n# First check GPU availability (many tests require GPU)\npython -c \"import torch; print('GPU available:', torch.cuda.is_available())\"\n\n# Run specific test file\nuv run pytest tests/test_<name>.py\n\n# Skip slow tests (CI default)\nuv run pytest -m \"not slow\"\n\n# Run with verbose output\nuv run pytest -v\n\n# Run distributed tests (requires torchrun and multi-GPU)\n# Note: Usually invoked via pytest test files\ntorchrun --nproc_per_node=2 tests/torchrun/run_<test>.py\n```\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/skills/add-unit-tests/SKILL.md\nInvocation: /add-unit-tests\n\n## Purpose\n\nStep-by-step guide for adding unit tests to AReaL.\n\n## How to Update\n\n### When Testing Conventions Change\n1. Update \"Key Requirements\" section based on `testing.md`\n2. Update test examples to match new patterns\n3. Update reference implementations\n\n### When Test Types Need Update\n1. Update \"Understand Test Types\" table (currently two main types)\n2. Add new examples if needed\n3. Update common mistakes\n\n### Integration with Other Skills\nEnsure references to other skills (`/add-dataset`, `/add-workflow`, `/add-reward`) remain accurate.\n\n================================================================================\n-->\n","frontmatter":{"name":"add-unit-tests","description":"Guide for adding unit tests to AReaL. Use when user wants to add tests for new functionality or increase test coverage."},"isInternal":false,"tokens":1682,"sizeBytes":7976},{"name":"SKILL.md","path":".claude/skills/add-workflow/SKILL.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.claude/skills/add-workflow/SKILL.md","title":"add-workflow","category":"anthropic-skill","format":"markdown","content":"---\nname: add-workflow\ndescription: Guide for adding a new RolloutWorkflow to AReaL. Use when user wants to create a new workflow.\n---\n\n# Add Workflow\n\nAdd a new RolloutWorkflow implementation to AReaL.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a workflow?\"\n- User wants to create a new RolloutWorkflow\n- User mentions implementing a custom rollout\n\n## Prerequisites\n\nBefore starting, ensure you understand:\n\n- The workflow's purpose and requirements\n- Input/output data format\n- Reward function to use\n\n## Step-by-Step Guide\n\n### Step 1: Create Workflow File\n\nCreate `areal/workflow/<name>.py`:\n\n```python\nimport uuid\nfrom typing import Any, Callable\n\nimport torch\n\nfrom areal.api.cli_args import GenerationHyperparameters\nfrom areal.api.engine_api import InferenceEngine\nfrom areal.api.io_struct import ModelRequest, ModelResponse\nfrom areal.api.reward_api import AsyncRewardWrapper\nfrom areal.api.workflow_api import RolloutWorkflow\nfrom areal.utils import logging\n\nlogger = logging.getLogger(\"MyWorkflow\")\n\n\nclass MyWorkflow(RolloutWorkflow):\n    \"\"\"Description of your workflow.\"\"\"\n\n    def __init__(\n        self,\n        gconfig: GenerationHyperparameters,\n        tokenizer,\n        reward_fn: Callable,\n    ):\n        self.gconfig = gconfig.new_with_stop_and_pad_token_ids(tokenizer)\n        self.tokenizer = tokenizer\n        self.async_reward_fn = AsyncRewardWrapper(reward_fn)\n\n    async def arun_episode(\n        self,\n        engine: InferenceEngine,\n        data: dict[str, Any],\n    ) -> dict[str, torch.Tensor]:\n        \"\"\"Run a single episode. MUST be async and non-blocking.\"\"\"\n\n        # 1. Prepare input_ids from data\n        input_ids = self.tokenizer.apply_chat_template(\n            data[\"messages\"],\n            tokenize=True,\n            add_generation_prompt=True,\n        )\n\n        # 2. Build ModelRequest\n        req = ModelRequest(\n            rid=uuid.uuid4().hex,\n            input_ids=list(input_ids),\n            gconfig=self.gconfig.new(n_samples=1),\n            tokenizer=self.tokenizer,\n        )\n\n        # 3. Generate completion (async)\n        resp: ModelResponse = await engine.agenerate(req)\n\n        # 4. Compute reward (async)\n        prompt_str = self.tokenizer.decode(input_ids)\n        completion_str = self.tokenizer.decode(resp.output_tokens)\n        reward = await self.async_reward_fn(\n            prompt_str,\n            completion_str,\n            resp.input_tokens,\n            resp.output_tokens,\n            **data,\n        )\n\n        # 5. Return results in expected format\n        return {\n            \"input_ids\": torch.tensor(resp.input_tokens),\n            \"output_ids\": torch.tensor(resp.output_tokens),\n            \"reward\": torch.tensor(reward),\n        }\n```\n\n### Step 2: Register in __init__.py\n\nAdd to `areal/workflow/__init__.py`:\n\n```python\nfrom areal.workflow.<name> import MyWorkflow\n\n__all__ = [\n    # ... existing exports\n    \"MyWorkflow\",\n]\n```\n\n### Step 3: Update Entry Script\n\nUpdate your training script to use the new workflow:\n\n```python\ntrainer.train(\n    workflow=\"areal.workflow.<name>.MyWorkflow\",\n    # ... other args\n)\n```\n\n### Step 4: Add Tests\n\nCreate `tests/test_<name>_workflow.py`:\n\n```python\nimport pytest\nfrom areal.workflow.<name> import MyWorkflow\n\n@pytest.mark.asyncio\nasync def test_workflow_basic():\n    # Test basic functionality\n    pass\n```\n\n## Reference Implementations\n\n| Workflow           | File                            | Description                |\n| ------------------ | ------------------------------- | -------------------------- |\n| MultiTurnWorkflow  | `areal/workflow/multi_turn.py`  | Multi-turn conversation    |\n| RLVRWorkflow       | `areal/workflow/rlvr.py`        | RL with verifiable rewards |\n| VisionRLVRWorkflow | `areal/workflow/vision_rlvr.py` | Vision + RLVR              |\n\n## Key Requirements\n\n1. **Async**: `arun_episode` must be `async def` and non-blocking\n1. **No sync I/O**: Use `aiofiles` for file operations\n1. **Wrap rewards**: Use `AsyncRewardWrapper` for reward functions\n1. **Tensor format**: Output tensors should be `[batch, seq_len, ...]`\n1. **Use helpers**: `concat_padded_tensors` for combining outputs\n\n## Common Mistakes\n\n- ❌ Using `open()` instead of `aiofiles.open()`\n- ❌ Forgetting to `await` async calls\n- ❌ Not wrapping reward function with `AsyncRewardWrapper`\n- ❌ Wrong tensor shape conventions\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/skills/add-workflow/SKILL.md\nInvocation: /add-workflow <name>\n\n## Purpose\n\nStep-by-step guide for adding new RolloutWorkflow implementations.\n\n## How to Update\n\n### When Workflow API Changes\n1. Update the code template in Step 1\n2. Update the required imports\n3. Update the method signature if changed\n\n### When New Patterns Emerge\n1. Add to \"Reference Implementations\" table\n2. Update \"Key Requirements\" if new requirements added\n\n================================================================================\n-->\n","frontmatter":{"name":"add-workflow","description":"Guide for adding a new RolloutWorkflow to AReaL. Use when user wants to create a new workflow."},"isInternal":false,"tokens":1122,"sizeBytes":5211},{"name":"SKILL.md","path":".claude/skills/commit-conventions/SKILL.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.claude/skills/commit-conventions/SKILL.md","title":"commit-conventions","category":"anthropic-skill","format":"markdown","content":"---\nname: commit-conventions\ndescription: AReaL commit message conventions. MUST load on every git commit -- provides Conventional Commits format with scope inference from file paths.\n---\n\n# Commit Conventions\n\nCommit message conventions and scope inference rules for the AReaL repository.\n\n## When to Use\n\n**ALWAYS load this skill when making any git commit in AReaL.** This includes:\n\n- Direct commits (`git commit`)\n- Commits during PR creation (`/create-pr`)\n- Commits delegated via Agent tool with `skills: [\"commit-conventions\"]`\n- Any agent workflow that produces a commit\n\n## Commit Message Format\n\n```\n<type>(<scope>): <subject>\n\n<body>\n\n[Optional sections:]\nKey changes:\n- change 1\n- change 2\n\nRefs: #123, #456\n```\n\n## Type Selection\n\n| Type       | When to Use                      |\n| ---------- | -------------------------------- |\n| `feat`     | New feature or capability        |\n| `fix`      | Bug fix                          |\n| `docs`     | Documentation only               |\n| `gov`      | Governance or maintainer changes |\n| `style`    | Formatting/style-only changes    |\n| `refactor` | Code change without feature/fix  |\n| `perf`     | Performance improvement          |\n| `test`     | Adding or fixing tests           |\n| `build`    | Build system or dependencies     |\n| `ci`       | CI pipeline or workflow changes  |\n| `chore`    | Build, deps, config changes      |\n| `revert`   | Revert a previous commit         |\n\n## Scope Inference\n\nInfer scope from the **primary** changed file paths:\n\n| File Path Pattern                                            | Scope                          |\n| ------------------------------------------------------------ | ------------------------------ |\n| `areal/workflow/`                                            | `workflow`                     |\n| `areal/engine/`                                              | `engine`                       |\n| `areal/reward/`                                              | `reward`                       |\n| `areal/dataset/`                                             | `dataset`                      |\n| `areal/api/`                                                 | `api`                          |\n| `areal/utils/`                                               | `utils`                        |\n| `areal/infra/`                                               | `infra`                        |\n| `areal/trainer/`                                             | `trainer`                      |\n| `areal/models/`                                              | `models`                       |\n| `areal/experimental/`                                        | `archon`                       |\n| `docs/`                                                      | `docs`                         |\n| `examples/`                                                  | `examples`                     |\n| `AGENTS.md`, `.agents/`, `.claude/`, `.codex/`, `.opencode/` | `agents`                       |\n| Multiple areas                                               | Omit scope or use broader term |\n\n## Rules\n\n- **Subject**: imperative mood, ~50-72 chars, no trailing period\n- **Body**: explain \"why\" not \"what\", wrap at 72 chars\n- **Key changes**: bullet list of main modifications (for complex commits with 3+ files)\n- **Refs**: reference issues/PRs if applicable\n\n## Examples\n\n**Single file fix:**\n\n```\nfix(reward): handle empty completion in gsm8k\n\nReturn 0 reward instead of raising exception when\ncompletion string is empty after extraction.\n```\n\n**Multi-file feature:**\n\n```\nfeat(engine): add CPU offload support to ArchonEngine\n\nEnable torch_memory_saver for model offloading during\nrollout phase to reduce GPU memory pressure.\n\nKey changes:\n- Add offload/onload methods to ArchonEngine\n- Integrate with weight update flow\n- Handle ROCm compatibility\n```\n\n**Docs only:**\n\n```\ndocs: update algorithm comparison table\n\nAdd SAPO and GSPO to the algorithm family documentation\nwith configuration examples.\n```\n\n**Agent/tooling changes:**\n\n```\nchore(agents): port review-pr command to OpenCode\n\nAdd OpenCode-native commands with task() category\ndelegation instead of hardcoded model names.\n\nKey changes:\n- Create .opencode/command/ with review-pr, create-pr\n- Replace hardcoded model routing with platform-native review routing\n- Add expert subagent consultation patterns\n```\n\n**Governance/maintainer changes:**\n\n```\ngov(agents): add maintainer ownership for service modules\n\nUpdate CODEOWNERS and maintainer references to reflect\ncurrent governance responsibilities.\n\nKey changes:\n- Add maintainers for agent_service and infra ownership\n- Align governance docs with updated reviewer responsibilities\n```\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/skills/commit-conventions/SKILL.md\nInvocation: Automatically loaded on every git commit via load_skills\n\n## Purpose\n\nProvides Conventional Commits format with AReaL-specific scope inference\nfrom file paths. Unlike other skills, this one is NOT user-triggered --\nit is loaded by the system/agent on every commit operation.\n\n## How to Update\n\n### When New Modules Are Added\n1. Add the file path pattern and scope to the \"Scope Inference\" table\n2. Keep table sorted by areal/ subpackages first, then top-level dirs\n\n### When Commit Types Change\n1. Update the \"Type Selection\" table\n2. Add/update examples to illustrate the new type\n\n### When Adding Examples\n1. Each example should demonstrate a distinct commit pattern\n2. Keep examples realistic -- use actual AReaL module names\n3. Show both subject-only and subject+body+key-changes variants\n\n### Important Design Decisions\n- This skill is ALWAYS loaded (not optional) -- keep it concise to\n  minimize token overhead on every commit\n- Scope inference is path-based, not content-based -- simpler and\n  more deterministic\n- \"Multiple areas\" -> omit scope rather than invent a new one\n\n================================================================================\n-->\n","frontmatter":{"name":"commit-conventions","description":"AReaL commit message conventions. MUST load on every git commit -- provides Conventional Commits format with scope inference from file paths."},"isInternal":false,"tokens":1219,"sizeBytes":6217},{"name":"SKILL.md","path":".claude/skills/debug-distributed/SKILL.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.claude/skills/debug-distributed/SKILL.md","title":"debug-distributed","category":"anthropic-skill","format":"markdown","content":"---\nname: debug-distributed\ndescription: Guide for debugging distributed training issues in AReaL. Use when user encounters hangs, wrong results, OOM, or communication errors.\n---\n\n# Debug Distributed Training\n\nDebugging guide for distributed training issues in AReaL (FSDP2, TP, CP, EP).\n\n## When to Use\n\nThis skill is triggered when:\n\n- Training hangs or deadlocks\n- Results differ across ranks or are numerically wrong\n- OOM errors in distributed settings\n- NCCL/communication errors or device mesh issues\n\n## Debugging Principles\n\n### Minimal Reproduction\n\n**Always follow the minimal demo principle**: Reproduce with the least amount of code to\nnarrow down the issue faster.\n\n```python\n# Bad: Debug in full training loop\n# Good: Create minimal script\nimport torch\nimport torch.distributed as dist\n\ndist.init_process_group(\"nccl\")\nrank = dist.get_rank()\n\n# Reproduce the exact operation that fails\ntensor = torch.ones(10).cuda()\ndist.all_reduce(tensor)  # <-- Isolate the failing op\nprint(f\"Rank {rank}: {tensor}\")\n```\n\n**Reduction strategy:**\n\n1. Remove unrelated model components\n1. Use small tensor sizes\n1. Reduce world_size to minimum (e.g., 2 GPUs)\n1. Remove torch.compile if possible\n1. Disable activation checkpointing\n\n## Step-by-Step Debugging Guide\n\n### 1. Hang Debugging (Deadlocks, Synchronization)\n\n**Environment Variables for Debugging**:\n\n```bash\n# Full debug logging\nexport TORCH_DISTRIBUTED_DEBUG=DETAIL\nexport NCCL_DEBUG=INFO\nexport NCCL_DEBUG_SUBSYS=ALL\n\n# torch.compile debugging\nexport TORCH_LOGS=\"+dynamo,recompiles\"\nexport TORCHDYNAMO_VERBOSE=1\n```\n\n**Dump Call Stack with py-spy** (for hung processes):\n\n```bash\n# Find process IDs\nps aux | grep python\n\n# Dump call stack of specific rank\npy-spy dump --pid <PID>\n\n# Record flame graph for performance analysis\npy-spy record -o profile.svg --pid <PID> --duration 30\n```\n\n**Common Causes**:\n\n1. **Mismatched Collectives**: One rank calls `all_reduce`, another doesn't.\n1. **Wrong Process Group**: Using wrong group for collective.\n1. **Tensor Shape Mismatch**: Different shapes across ranks.\n\n**Debug Steps**:\n\n```python\n# Verify group membership\nmesh = parallel_dims.get_mesh(\"dp_shard_cp\")\ngroup = mesh.get_group()\nprint(f\"Rank {dist.get_rank()}: group size = {dist.get_world_size(group)}\")\n\n# Print shapes on all ranks\nprint(f\"Rank {dist.get_rank()}: tensor.shape = {tensor.shape}\")\ndist.barrier()\n```\n\n**Timeout Adjustment** (for debugging only):\n\n```python\nfrom areal.engine.core.distributed import patch_dist_group_timeout\nfrom datetime import timedelta\npatch_dist_group_timeout(timedelta(minutes=30))\n```\n\n### 2. Wrong Results (Gradient, Reduction Issues)\n\n**Check DTensor Placements**:\n\n```python\nfrom torch.distributed.tensor import DTensor\nif isinstance(param, DTensor):\n    print(f\"Param {name}: placements={param.placements}, mesh={param.device_mesh}\")\n```\n\n**Verify Gradient Reduction**:\n\n```python\nfor name, param in model.named_parameters():\n    if param.grad is not None:\n        print(f\"Rank {dist.get_rank()}: {name} grad_sum = {param.grad.sum().item()}\")\n```\n\n### 3. OOM Issues (Memory, Sharding)\n\n**Check Memory Usage**:\n\n```python\nprint(f\"Rank {dist.get_rank()}: \"\n      f\"allocated={torch.cuda.memory_allocated()/1e9:.2f}GB, \"\n      f\"reserved={torch.cuda.memory_reserved()/1e9:.2f}GB\")\n```\n\n**Check FSDP Coverage**:\n\n```python\nfor name, param in model.named_parameters():\n    is_dtensor = isinstance(param, DTensor)\n    print(f\"{name}: is_dtensor={is_dtensor}, shape={param.shape}\")\n```\n\n### 4. Communication Errors\n\n| Error                     | Cause                | Solution                           |\n| ------------------------- | -------------------- | ---------------------------------- |\n| `NCCL WARN Cuda failure`  | GPU communication    | Check NCCL version, GPU topology   |\n| `RuntimeError: Timed out` | Rank synchronization | Increase timeout, check code paths |\n| `Invalid device mesh`     | Mesh configuration   | Verify world_size = dp * tp * cp   |\n\n## Debugging Tools\n\n### Environment Variables Reference\n\n| Variable                          | Purpose                                |\n| --------------------------------- | -------------------------------------- |\n| `TORCH_DISTRIBUTED_DEBUG=DETAIL`  | Detailed distributed logging           |\n| `NCCL_DEBUG=INFO`                 | NCCL communication logging             |\n| `NCCL_DEBUG_SUBSYS=ALL`           | All NCCL subsystems                    |\n| `TORCH_LOGS=\"+dynamo,recompiles\"` | torch.compile logging                  |\n| `TORCHDYNAMO_VERBOSE=1`           | Dynamo verbose output                  |\n| `CUDA_LAUNCH_BLOCKING=1`          | Synchronous CUDA (slow, for debugging) |\n\n### py-spy for Call Stack Analysis\n\n```bash\n# Install\npip install py-spy\n\n# Dump call stack of hung process\npy-spy dump --pid <PID>\n\n# Dump all Python processes\npgrep -f python | xargs -I {} py-spy dump --pid {}\n\n# Record flame graph\npy-spy record -o profile.svg --pid <PID> --duration 30\n```\n\n### Rank-Conditional Printing\n\n```python\ndef print_all_ranks(msg):\n    for r in range(dist.get_world_size()):\n        if dist.get_rank() == r:\n            print(f\"[Rank {r}] {msg}\")\n        dist.barrier()\n```\n\n### Check Device Mesh\n\n```python\ndef debug_mesh(parallel_dims):\n    mesh = parallel_dims.world_mesh\n    for dim_name in mesh.mesh_dim_names:\n        submesh = parallel_dims.get_mesh(dim_name)\n        if submesh:\n            print(f\"Rank {dist.get_rank()}: {dim_name} size={submesh.size()}\")\n```\n\n### Validate Tensor Consistency\n\n```python\ndef check_tensor_consistency(tensor, name, group=None):\n    local_sum = tensor.sum().item()\n    tensor_sums = [None] * dist.get_world_size(group)\n    dist.all_gather_object(tensor_sums, local_sum, group=group)\n    if dist.get_rank() == 0 and len(set(tensor_sums)) > 1:\n        print(f\"WARNING: {name} inconsistent: {tensor_sums}\")\n```\n\n## Key Files Reference\n\n| Component       | File                                                          |\n| --------------- | ------------------------------------------------------------- |\n| Parallel Dims   | `areal/experimental/models/archon/parallel_dims.py`           |\n| Expert Parallel | `areal/experimental/models/archon/expert_parallel.py`         |\n| Ulysses (CP)    | `areal/experimental/models/archon/ulysses.py`                 |\n| FSDP/TP Apply   | `areal/experimental/models/archon/qwen2/infra/parallelize.py` |\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/skills/debug-distributed/SKILL.md\nInvocation: /debug-distributed\n\n## Purpose\n\nDebugging guide for distributed training issues.\nCovers FSDP2, Tensor Parallelism, Context Parallelism, and Expert Parallelism.\n\n## How to Update\n\n### When Adding New Parallelism Features\n1. Add section for the parallelism type\n2. Document common error patterns and debugging snippets\n\n### When PyTorch Distributed APIs Change\n1. Update DTensor/DeviceMesh examples\n2. Update environment variable references\n\n### When New Error Patterns Emerge\n1. Add to \"Common Errors and Solutions\" table\n2. Reference relevant source files\n\n================================================================================\n-->\n","frontmatter":{"name":"debug-distributed","description":"Guide for debugging distributed training issues in AReaL. Use when user encounters hangs, wrong results, OOM, or communication errors."},"isInternal":false,"tokens":1680,"sizeBytes":7356},{"name":"SKILL.md","path":".opencode/skills/add-archon-model/SKILL.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.opencode/skills/add-archon-model/SKILL.md","title":"add-archon-model","category":"anthropic-skill","format":"markdown","content":"---\nname: add-archon-model\ndescription: Guide for adding a new model to the Archon engine. Use when user wants to add support for a new HuggingFace model architecture in ArchonEngine.\n---\n\n# Add Archon Model\n\nAdd support for a new HuggingFace model architecture in the Archon training engine.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a model to Archon?\"\n- User wants to support a new model family (e.g., Llama, Mistral, DeepSeek) in\n  ArchonEngine\n- User mentions adding a new `ModelSpec` or model type for Archon\n\n## Prerequisites\n\nBefore starting, ensure:\n\n- The target model is available on HuggingFace (has `config.json` with `model_type`)\n- You know the HuggingFace model ID (e.g., `meta-llama/Llama-3-8B`)\n- The model uses a standard transformer architecture (decoder-only)\n\n## Step-by-Step Guide\n\n### Step 1: Analyze the Target Model Architecture\n\nRead the HuggingFace model's source code to extract key architecture information.\n\n**Action**: Fetch and analyze the model's HuggingFace configuration and modeling files.\n\n1. Read the model's `config.json` (via `AutoConfig.from_pretrained`) to identify:\n\n   - `model_type` string (this is the key used for registry lookup)\n   - All architecture hyperparameters (hidden_size, num_layers, etc.)\n   - Any model-specific fields (e.g., `qk_norm`, `attention_bias`, MoE fields)\n\n1. Read the HuggingFace `modeling_*.py` source to identify:\n\n   - **Attention variant**: Does it have Q/K norm? Attention bias? Sliding window?\n     Multi-latent attention?\n   - **FFN variant**: SwiGLU (gate_proj + up_proj + down_proj)? GeGLU? Standard MLP?\n   - **MoE support**: Does it have MoE layers? What router type? Shared experts?\n   - **RoPE variant**: Standard RoPE? YaRN? NTK-aware scaling? What is the inv_freq\n     formula?\n   - **Normalization**: RMSNorm or LayerNorm? Pre-norm or post-norm? Elementwise affine?\n   - **Weight tying**: Does `tie_word_embeddings` appear in config?\n   - **State dict key names**: What are the HF weight key naming conventions?\n\n1. Summarize findings in a checklist like:\n\n```\nTarget model: <name>\nHF model_type: \"<model_type>\" (and variants like \"<model_type>_moe\" if applicable)\nAttention: [standard GQA / with QK norm / with bias / sliding window / ...]\nFFN: [SwiGLU / GeGLU / standard MLP / ...]\nMoE: [no / yes - num_experts, top_k, shared_experts]\nRoPE: [standard / YaRN / NTK-aware / ...]\nNorm: [RMSNorm / LayerNorm] with [pre-norm / post-norm]\nWeight tying: [yes / no]\n```\n\n### Step 2: Select the Reference Model\n\nChoose the closest existing implementation as a starting point:\n\n| Target characteristics               | Reference | Why                                     |\n| ------------------------------------ | --------- | --------------------------------------- |\n| Dense-only, standard GQA, no QK norm | `qwen2`   | Simplest baseline, pure dense           |\n| Has QK norm, or has MoE support      | `qwen3`   | Supports QK norm + MoE + shared experts |\n\n**Action**: Copy the reference model directory as the starting point:\n\n```\nareal/experimental/models/archon/<model>/\n  __init__.py\n  spec.py\n  model/\n    args.py\n    model.py\n    rope.py\n    state_dict_adapter.py\n  infra/\n    parallelize.py\n```\n\n### Step 3: Implement `args.py`\n\nAdapt `<Model>ModelArgs` to match the target model's HuggingFace config fields.\n\n**Key changes from reference**:\n\n1. Update the `@dataclass` fields to match the target model's hyperparameters:\n\n   - Field names should use Archon conventions (`dim`, `n_layers`, `n_heads`,\n     `n_kv_heads`, `vocab_size`, `head_dim`, `hidden_dim`, `norm_eps`, `rope_theta`,\n     etc.)\n   - Default values should match the smallest variant of the target model\n   - Add model-specific fields (e.g., `attention_bias`, `qk_norm`, `sliding_window`)\n\n1. Update `from_hf_config()` to correctly map HuggingFace config attributes:\n\n   - Use `getattr(hf_config, \"field_name\", default)` for optional fields\n   - Handle variant-specific fields (e.g., MoE fields only present in MoE variants)\n   - The method must return an instance of the model args class\n\n**Critical**: Verify every field mapping against the HF model's `config.json`. Incorrect\nmappings here cause silent errors downstream.\n\n**Base class contract** (`BaseModelArgs`):\n\n```python\n@dataclass\nclass <Model>ModelArgs(BaseModelArgs):\n    # ... model-specific fields ...\n\n    @classmethod\n    def from_hf_config(\n        cls,\n        hf_config: PretrainedConfig,\n        is_critic: bool = False,\n        **kwargs,\n    ) -> <Model>ModelArgs:\n        # Map HF config fields to Archon model args\n        ...\n```\n\n### Step 4: Implement `model.py`\n\nAdapt the model architecture to match the target model.\n\n**Key components to adapt**:\n\n1. **Normalization** (`RMSNorm` or similar):\n\n   - Check if `elementwise_affine` is configurable\n   - Check the epsilon default value\n   - If the model uses `LayerNorm`, implement accordingly\n\n1. **Attention** module:\n\n   - Q/K/V projection: Check bias presence (`nn.Linear(..., bias=True/False)`)\n   - QK norm: Add `q_norm`/`k_norm` if the model has them, remove if it doesn't\n   - GQA: `n_kv_heads` \\< `n_heads` for grouped-query attention\n   - Ulysses SP: Keep the `set_cp_group` / `_sp_enabled` pattern from the reference\n   - Output projection: Check bias presence\n\n1. **FeedForward** module:\n\n   - SwiGLU: `w2(silu(w1(x)) * w3(x))` -- most common for modern LLMs\n   - Check bias in linear layers\n   - For MoE models: `MoE` module replaces `FeedForward` on designated layers\n\n1. **TransformerBlock**: Pre-norm (most modern LLMs) vs post-norm\n\n   - MoE layer detection via `_is_moe_layer()` if applicable\n\n1. **Top-level Model** (`<Model>Model(BaseArchonModel)`):\n\n   - `tok_embeddings`, `layers` (as `ModuleDict`), `norm`, `output`/`score`\n   - `init_weights()`: Match initialization scheme from HF\n   - `init_buffers()`: RoPE cache + MoE buffers\n   - `forward()`: Must follow `BaseArchonModel` signature:\n     `(tokens, positions, cu_seqlens, max_seqlen, tree_attn_meta=None) -> Tensor`\n\n**Base class contract** (`BaseArchonModel`):\n\n```python\nclass <Model>Model(BaseArchonModel):\n    def forward(self, tokens, positions, cu_seqlens, max_seqlen, tree_attn_meta=None) -> torch.Tensor: ...\n    def init_weights(self) -> None: ...\n    def init_buffers(self, buffer_device) -> None: ...\n```\n\n### Step 5: Implement `rope.py`\n\nHandle the rotary position embedding variant.\n\n**Options**:\n\n1. **Standard RoPE** (same as qwen2/qwen3): Re-export from qwen2:\n\n   ```python\n   from areal.experimental.models.archon.qwen2.model.rope import (\n       apply_rotary_emb,\n       precompute_rope_cache,\n       repeat_kv,\n       reshape_for_broadcast,\n       rotate_half,\n   )\n   ```\n\n1. **Custom RoPE** (YaRN, NTK-aware, etc.): Implement custom `precompute_rope_cache()`\n   and `apply_rotary_emb()` functions. The key difference is usually in how `inv_freq`\n   is computed (scaling factors, interpolation, etc.).\n\n### Step 6: Implement `state_dict_adapter.py`\n\nMap between HuggingFace and Archon weight key names.\n\n**This is the most error-prone step.** The adapter must correctly handle:\n\n1. **Key name mapping** (`from_hf_map` dict):\n\n   - Embedding: `model.embed_tokens.weight` -> `tok_embeddings.weight`\n   - Attention: `model.layers.{}.self_attn.q_proj.weight` ->\n     `layers.{}.attention.wq.weight`\n   - FFN: `model.layers.{}.mlp.gate_proj.weight` -> `layers.{}.feed_forward.w1.weight`\n   - Norms: `model.layers.{}.input_layernorm.weight` ->\n     `layers.{}.attention_norm.weight`\n   - Output: `lm_head.weight` -> `output.weight`\n   - Skip keys (set to `None`): `rotary_emb.inv_freq` (computed at runtime)\n   - Model-specific keys: bias terms, QK norm weights, etc.\n\n1. **Reverse mapping** (`to_hf_map`): Auto-generated from `from_hf_map`\n\n1. **MoE expert weights** (if applicable): 3D\\<->2D conversion for expert weights. Copy\n   the MoE handling from qwen3 if the model has MoE.\n\n1. **Weight tying**: Skip `output.weight` during `to_hf()` if `tie_word_embeddings=True`\n\n**Verification approach**: After implementation, the adapter should satisfy:\n\n```python\n# Roundtrip: archon -> hf -> archon preserves all keys\nhf_sd = adapter.to_hf(archon_sd)\nroundtrip_sd = adapter.from_hf(hf_sd)\nassert set(roundtrip_sd.keys()) == set(archon_sd.keys())\n```\n\n**Base class contract** (`BaseStateDictAdapter`):\n\n```python\nclass <Model>StateDictAdapter(BaseStateDictAdapter):\n    def from_hf(self, hf_state_dict) -> dict[str, Any]: ...\n    def to_hf(self, archon_state_dict) -> dict[str, Any]: ...\n    def convert_single_to_hf(self, name, tensor) -> list[tuple[str, torch.Tensor]]: ...\n```\n\n### Step 7: Implement `parallelize.py`\n\nDefine the parallelization strategy for the model.\n\n**The parallelize function** applies parallelism in this order:\n\n1. TP (Tensor Parallelism) -- shard attention/FFN across devices\n1. EP (Expert Parallelism) -- for MoE models only\n1. CP (Context Parallelism / Ulysses SP) -- sequence parallelism\n1. AC (Activation Checkpointing) -- memory optimization\n1. torch.compile -- compilation optimization\n1. FSDP (Fully Sharded Data Parallelism) -- data parallelism\n\n**Key adaptations by model architecture**:\n\n- **Attention with QK norm**: wq/wk use `use_local_output=False` (DTensor output for\n  norm), add `SequenceParallel(sequence_dim=2)` for q_norm/k_norm\n- **Attention without QK norm**: wq/wk/wv all use `use_local_output=True`\n- **Attention with bias**: Bias terms follow the same parallel plan as their weights\n- **MoE layers**: Separate TP plan for MoE input/output, router gate, and expert\n  weights. Copy from qwen3's `apply_moe_ep_tp()` and `apply_non_moe_tp()`\n- **Dense-only models**: Simpler plan without MoE handling. Copy from qwen2\n\n**Function signature** (must match `ParallelizeFn` protocol):\n\n```python\ndef parallelize_<model>(\n    model: nn.Module,\n    parallel_dims: ArchonParallelDims,\n    param_dtype: torch.dtype = torch.bfloat16,\n    reduce_dtype: torch.dtype = torch.float32,\n    loss_parallel: bool = True,\n    cpu_offload: bool = False,\n    reshard_after_forward_policy: str = \"default\",\n    ac_config: ActivationCheckpointConfig | None = None,\n    enable_compile: bool = True,\n) -> nn.Module:\n```\n\n### Step 8: Create `spec.py` and Register\n\nAssemble the `ModelSpec` and register it.\n\n```python\nfrom areal.experimental.models.archon.model_spec import ModelSpec, register_model_spec\nfrom areal.experimental.models.archon.pipeline_parallel import pipeline_llm\nfrom areal.experimental.models.archon.<model>.infra.parallelize import parallelize_<model>\nfrom areal.experimental.models.archon.<model>.model.args import <Model>ModelArgs\nfrom areal.experimental.models.archon.<model>.model.model import <Model>Model\nfrom areal.experimental.models.archon.<model>.model.state_dict_adapter import (\n    <Model>StateDictAdapter,\n)\n\n<MODEL>_SPEC = ModelSpec(\n    name=\"<Model>\",\n    model_class=<Model>Model,\n    model_args_class=<Model>ModelArgs,\n    state_dict_adapter_class=<Model>StateDictAdapter,\n    parallelize_fn=parallelize_<model>,\n    supported_model_types=frozenset({\"<model_type>\"}),  # From HF config.json\n    pipelining_fn=pipeline_llm,\n)\n\n# Auto-register when module is imported\nregister_model_spec(<MODEL>_SPEC)\n\n__all__ = [\"<MODEL>_SPEC\"]\n```\n\n**Note**: `supported_model_types` should include all HF `model_type` strings that this\nimplementation handles (e.g., `{\"qwen3\", \"qwen3_moe\"}` for Qwen3).\n\n### Step 9: Register in `__init__.py`\n\nAdd the import to `areal/experimental/models/archon/__init__.py`:\n\n```python\nfrom areal.experimental.models.archon.<model> import spec as <model>_spec  # noqa: F401\n```\n\nThis triggers auto-registration when the module is imported.\n\n### Step 10: Verify and Test\n\nVerification should be done in stages, adapting based on available hardware and the test\npatterns in `tests/experimental/archon/`.\n\n**Before writing tests**, examine the existing test files to understand current\npatterns:\n\n```\ntests/experimental/archon/\n  conftest.py             -- Pytest configuration (version checks)\n  utils.py                -- Shared utilities (model loading, comparison)\n  test_qwen3_args.py      -- Args unit tests (CPU-only)\n  test_state_dict_adapter.py  -- State dict roundtrip tests\n  test_weight_sync.py     -- Weight completeness tests (meta device)\n  test_forward.py         -- Forward precision comparison (single GPU)\n  ...\n```\n\n**Test stages** (write tests appropriate for the model's complexity):\n\n#### Stage 1: Args Tests (CPU-only, always write these)\n\nTest `from_hf_config()` with mock HuggingFace configs:\n\n```python\n# Pattern: Create mock PretrainedConfig, verify args mapping\nfrom unittest.mock import MagicMock\n\ndef test_args_from_hf_config():\n    hf_config = MagicMock()\n    hf_config.hidden_size = 4096\n    hf_config.num_hidden_layers = 32\n    # ... set all required fields\n    args = <Model>ModelArgs.from_hf_config(hf_config)\n    assert args.dim == 4096\n    assert args.n_layers == 32\n```\n\n#### Stage 2: State Dict Adapter Tests (CPU-only)\n\nTest key mapping roundtrip:\n\n```python\ndef test_state_dict_roundtrip():\n    # Create adapter with mock config\n    adapter = <Model>StateDictAdapter(mock_config)\n    # Create fake archon state dict with expected keys\n    archon_sd = {\"tok_embeddings.weight\": torch.randn(vocab, dim), ...}\n    # Roundtrip\n    hf_sd = adapter.to_hf(archon_sd)\n    roundtrip = adapter.from_hf(hf_sd)\n    assert set(roundtrip.keys()) == set(archon_sd.keys())\n```\n\n#### Stage 3: Weight Completeness (meta device, CPU-only)\n\nVerify all model parameters have HF mappings:\n\n```python\ndef test_weight_completeness():\n    # Create model on meta device\n    with torch.device(\"meta\"):\n        model = <Model>Model(args)\n    adapter = <Model>StateDictAdapter(hf_config)\n    # Check every archon param has a HF mapping\n    for name, _ in model.named_parameters():\n        hf_pairs = adapter.convert_single_to_hf(name, torch.empty(0))\n        assert len(hf_pairs) > 0, f\"No HF mapping for {name}\"\n```\n\n#### Stage 4: Forward Precision (single GPU, if available)\n\nCompare Archon model output against HuggingFace reference:\n\n```python\n@pytest.mark.skipif(not torch.cuda.is_available(), reason=\"Requires CUDA\")\ndef test_forward_matches_hf():\n    # Load both HF and Archon models\n    # Run forward on same input\n    # Compare logits within tolerance\n```\n\n**Important**: Do NOT hardcode the test categories. Inspect the existing test files in\n`tests/experimental/archon/` and follow the same patterns, fixtures, and markers. Adapt\ntest scope to the model's specific features (e.g., add MoE-specific tests only if the\nmodel has MoE).\n\n## Reference Implementations\n\n| Model | Directory                                 | Features                                                |\n| ----- | ----------------------------------------- | ------------------------------------------------------- |\n| Qwen2 | `areal/experimental/models/archon/qwen2/` | Dense, attention bias, no QK norm                       |\n| Qwen3 | `areal/experimental/models/archon/qwen3/` | Dense + MoE, QK norm, no attention bias, shared experts |\n\n## Architecture Decision Map\n\n| Feature             | qwen2    | qwen3                      | What to check in target model                            |\n| ------------------- | -------- | -------------------------- | -------------------------------------------------------- |\n| Attention bias      | Yes      | No                         | `attention_bias` in HF config                            |\n| QK norm             | No       | Yes                        | `qk_norm` in HF config or QKNorm module in modeling file |\n| MoE                 | No       | Yes                        | `num_experts`/`num_local_experts` in HF config           |\n| Shared experts      | No       | Yes                        | `num_shared_experts` in HF config                        |\n| Decoder sparse step | No       | Yes                        | `decoder_sparse_step` in HF config                       |\n| Weight tying        | Both     | Both                       | `tie_word_embeddings` in HF config                       |\n| RoPE                | Standard | Standard (re-export qwen2) | Check inv_freq formula in HF modeling code               |\n\n## Common Mistakes\n\n- Not mapping all HF keys in `state_dict_adapter.py` (causes silent weight drops)\n- Wrong `from_hf_config()` field mapping (uses wrong HF config attribute name)\n- Forgetting to handle `None` keys in `from_hf_map` (keys to skip like\n  `rotary_emb.inv_freq`)\n- Missing MoE expert weight 3D\\<->2D conversion when model has MoE\n- Wrong TP plan for attention with/without QK norm (`use_local_output` must match)\n- Forgetting to add import line in `areal/experimental/models/archon/__init__.py`\n- Not including all `model_type` variants in `supported_model_types` frozenset\n- Using `print` instead of `areal.utils.logging.getLogger()`\n\n## File Checklist\n\nAfter completion, verify all files exist and are consistent:\n\n- [ ] `areal/experimental/models/archon/<model>/__init__.py`\n- [ ] `areal/experimental/models/archon/<model>/spec.py` -- ModelSpec + register\n- [ ] `areal/experimental/models/archon/<model>/model/args.py` -- ModelArgs +\n  from_hf_config\n- [ ] `areal/experimental/models/archon/<model>/model/model.py` -- Model + Attention +\n  FFN\n- [ ] `areal/experimental/models/archon/<model>/model/rope.py` -- RoPE (or re-export)\n- [ ] `areal/experimental/models/archon/<model>/model/state_dict_adapter.py` -- Key\n  mapping\n- [ ] `areal/experimental/models/archon/<model>/infra/parallelize.py` -- Parallel\n  strategy\n- [ ] `areal/experimental/models/archon/__init__.py` -- Import line added\n- [ ] `tests/experimental/archon/test_<model>_*.py` -- Tests\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .opencode/skills/add-archon-model/SKILL.md\nInvocation: /add-archon-model <model_name>\n\n## Purpose\n\nSemi-automated guide for adding new model architectures to the Archon training engine.\nUnlike simpler skills (add-reward, add-dataset), this skill actively guides Claude to:\n1. Analyze HuggingFace source code to extract architecture details\n2. Select the closest reference implementation (qwen2 or qwen3)\n3. Generate code skeletons adapted to the target architecture\n4. Create appropriate tests based on existing test patterns\n\n## How to Update\n\n### When New Reference Models Are Added\n1. Add to \"Reference Implementations\" table\n2. Update \"Architecture Decision Map\" with new feature columns\n3. Update Step 2 (reference selection) with new options\n\n### When Base Classes Change\n1. Update contract signatures in Steps 3, 4, 6, 7\n2. Update file checklist if new files are required\n\n### When ModelSpec Changes\n1. Update Step 8 with new ModelSpec fields\n2. Update spec.py template\n\n### When Test Patterns Change\n1. Update Step 10 with new test patterns\n2. Do NOT hardcode test categories -- keep it flexible\n\n### Important Design Decisions\n- This skill is SEMI-AUTOMATED: Claude should read HF source and generate code,\n  not just provide templates for the user to fill in manually\n- The skill references existing test files rather than hardcoding test categories,\n  ensuring it stays current as the test suite evolves\n- Reference model selection (qwen2 vs qwen3) is based on MoE and QK norm presence\n\n================================================================================\n-->\n","frontmatter":{"name":"add-archon-model","description":"Guide for adding a new model to the Archon engine. Use when user wants to add support for a new HuggingFace model architecture in ArchonEngine."},"isInternal":false,"tokens":4772,"sizeBytes":19504},{"name":"SKILL.md","path":".opencode/skills/add-dataset/SKILL.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.opencode/skills/add-dataset/SKILL.md","title":"add-dataset","category":"anthropic-skill","format":"markdown","content":"---\nname: add-dataset\ndescription: Guide for adding a new dataset loader to AReaL. Use when user wants to add a new dataset.\n---\n\n# Add Dataset\n\nAdd a new dataset loader to AReaL.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a dataset?\"\n- User wants to integrate a new dataset\n- User mentions creating a dataset loader\n\n## Step-by-Step Guide\n\n### Step 1: Create Dataset File\n\nCreate `areal/dataset/<name>.py`:\n\n```python\nfrom datasets import Dataset, load_dataset\n\n\ndef get_<name>_sft_dataset(\n    path: str,\n    split: str,\n    tokenizer,\n    max_length: int | None = None,\n) -> Dataset:\n    \"\"\"Load dataset for SFT training.\n\n    Args:\n        path: Path to dataset (HuggingFace hub or local path)\n        split: Dataset split (train/validation/test)\n        tokenizer: Tokenizer for processing\n        max_length: Maximum sequence length (optional)\n\n    Returns:\n        HuggingFace Dataset with processed samples\n    \"\"\"\n    dataset = load_dataset(path=path, split=split)\n\n    def process(sample):\n        # Tokenize the full sequence (prompt + response)\n        seq_token = tokenizer.encode(\n            sample[\"question\"] + sample[\"answer\"] + tokenizer.eos_token\n        )\n        prompt_token = tokenizer.encode(sample[\"question\"])\n        # Loss mask: 0 for prompt, 1 for response\n        loss_mask = [0] * len(prompt_token) + [1] * (len(seq_token) - len(prompt_token))\n        return {\"input_ids\": seq_token, \"loss_mask\": loss_mask}\n\n    dataset = dataset.map(process).remove_columns([\"question\", \"answer\"])\n\n    if max_length is not None:\n        dataset = dataset.filter(lambda x: len(x[\"input_ids\"]) <= max_length)\n\n    return dataset\n\n\ndef get_<name>_rl_dataset(\n    path: str,\n    split: str,\n    tokenizer,\n    max_length: int | None = None,\n) -> Dataset:\n    \"\"\"Load dataset for RL training.\n\n    Args:\n        path: Path to dataset\n        split: Dataset split\n        tokenizer: Tokenizer for length filtering\n        max_length: Maximum sequence length\n\n    Returns:\n        HuggingFace Dataset with prompts and answers for reward computation\n    \"\"\"\n    dataset = load_dataset(path=path, split=split)\n\n    def process(sample):\n        messages = [\n            {\n                \"role\": \"user\",\n                \"content\": sample[\"question\"],\n            }\n        ]\n        return {\"messages\": messages, \"answer\": sample[\"answer\"]}\n\n    dataset = dataset.map(process).remove_columns([\"question\"])\n\n    if max_length is not None:\n\n        def filter_length(sample):\n            content = sample[\"messages\"][0][\"content\"]\n            tokens = tokenizer.encode(content)\n            return len(tokens) <= max_length\n\n        dataset = dataset.filter(filter_length)\n\n    return dataset\n```\n\n### Step 2: Register in __init__.py\n\nUpdate `areal/dataset/__init__.py`:\n\n```python\n# Add to VALID_DATASETS\nVALID_DATASETS = [\n    # ... existing datasets\n    \"<name>\",\n]\n\n# Add to _get_custom_dataset function\ndef _get_custom_dataset(name: str, ...):\n    # ... existing code\n    elif name == \"<name>\":\n        from areal.dataset.<name> import get_<name>_sft_dataset, get_<name>_rl_dataset\n        if dataset_type == \"sft\":\n            return get_<name>_sft_dataset(path, split, max_length, tokenizer)\n        else:\n            return get_<name>_rl_dataset(path, split, max_length, tokenizer)\n```\n\n### Step 3: Add Config (Optional)\n\nIf the dataset needs special configuration, add to `areal/api/cli_args.py`:\n\n```python\n@dataclass\nclass TrainDatasetConfig:\n    # ... existing fields\n    <name>_specific_field: Optional[str] = None\n```\n\n### Step 4: Add Tests\n\nCreate `tests/test_<name>_dataset.py`:\n\n```python\nimport pytest\nfrom areal.dataset.<name> import get_<name>_sft_dataset, get_<name>_rl_dataset\n\ndef test_sft_dataset_loads(tokenizer):\n    dataset = get_<name>_sft_dataset(\"path/to/data\", split=\"train\", tokenizer=tokenizer)\n    assert len(dataset) > 0\n    assert \"input_ids\" in dataset.column_names\n    assert \"loss_mask\" in dataset.column_names\n\ndef test_rl_dataset_loads(tokenizer):\n    dataset = get_<name>_rl_dataset(\"path/to/data\", split=\"train\", tokenizer=tokenizer)\n    assert len(dataset) > 0\n    assert \"messages\" in dataset.column_names\n    assert \"answer\" in dataset.column_names\n```\n\n## Reference Implementations\n\n| Dataset    | File                               | Description              |\n| ---------- | ---------------------------------- | ------------------------ |\n| GSM8K      | `areal/dataset/gsm8k.py`           | Math word problems       |\n| Geometry3K | `areal/dataset/geometry3k.py`      | Geometry problems        |\n| CLEVR      | `areal/dataset/clevr_count_70k.py` | Visual counting          |\n| HH-RLHF    | `areal/dataset/hhrlhf.py`          | Helpfulness/Harmlessness |\n| TORL       | `areal/dataset/torl_data.py`       | Tool-use RL              |\n\n## Required Fields\n\n### SFT Dataset\n\n```python\n{\n    \"messages\": [\n        {\"role\": \"user\", \"content\": \"...\"},\n        {\"role\": \"assistant\", \"content\": \"...\"},\n    ]\n}\n```\n\n### RL Dataset\n\n```python\n{\n    \"messages\": [\n        {\"role\": \"user\", \"content\": \"...\"},\n    ],\n    \"answer\": \"ground_truth_for_reward\",\n    # Optional metadata for reward function\n}\n```\n\n## Common Mistakes\n\n- Returning `List[Dict]` instead of HuggingFace `Dataset`\n- Using Python loops instead of `dataset.map()`/`filter()`\n- Missing `\"messages\"` field for RL datasets\n- Wrong message format (should be list of dicts with `role` and `content`)\n- Not registering in `__init__.py`\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .opencode/skills/add-dataset/SKILL.md\nInvocation: /add-dataset <name>\n\n## Purpose\n\nStep-by-step guide for adding new dataset loaders.\n\n## How to Update\n\n### When Dataset API Changes\n1. Update the code templates\n2. Update required fields section\n3. Update registration example\n\n### When New Dataset Types Added\n1. Add to \"Reference Implementations\" table\n2. Add any new required fields\n\n================================================================================\n-->\n","frontmatter":{"name":"add-dataset","description":"Guide for adding a new dataset loader to AReaL. Use when user wants to add a new dataset."},"isInternal":false,"tokens":1434,"sizeBytes":6241},{"name":"SKILL.md","path":".opencode/skills/add-reward/SKILL.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.opencode/skills/add-reward/SKILL.md","title":"add-reward","category":"anthropic-skill","format":"markdown","content":"---\nname: add-reward\ndescription: Guide for adding a new reward function to AReaL. Use when user wants to create a reward function.\n---\n\n# Add Reward\n\nAdd a new reward function to AReaL.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a reward function?\"\n- User wants to implement custom rewards\n- User mentions reward computation\n\n## Step-by-Step Guide\n\n### Step 1: Create Reward File\n\nCreate `areal/reward/<name>.py`:\n\n```python\nfrom typing import Any\n\nfrom areal.utils import logging\n\nlogger = logging.getLogger(\"MyReward\")\n\n\ndef <name>_reward_fn(\n    prompt: str,\n    completions: str,\n    prompt_ids,\n    completion_ids,\n    answer: str | None = None,\n    **kwargs: Any,\n) -> float:\n    \"\"\"Compute reward for a single completion.\n\n    Args:\n        prompt: Prompt string\n        completions: Completion string (model output)\n        prompt_ids: Tokenized prompt IDs\n        completion_ids: Tokenized completion IDs\n        answer: Ground truth answer from dataset (optional)\n        **kwargs: Additional data from dataset\n\n    Returns:\n        Reward value (float), typically 0.0 or 1.0\n    \"\"\"\n    try:\n        # Extract answer from completion\n        extracted = _extract_answer(completions)\n\n        # Compare with ground truth\n        if answer is not None and extracted == str(answer):\n            return 1.0\n        return 0.0\n    except Exception:\n        logger.warning(\"Exception in reward computation\", exc_info=True)\n        return 0.0\n\n\ndef _extract_answer(completion: str) -> str:\n    \"\"\"Extract the answer from a completion string.\n\n    Implement your extraction logic here.\n    \"\"\"\n    # Example: Extract content from \\boxed{}\n    import re\n\n    match = re.search(r\"\\\\boxed\\{([^}]+)\\}\", completion)\n    if match:\n        return match.group(1).strip()\n    return completion.strip()\n```\n\n### Step 2: Register in __init__.py\n\nUpdate `areal/reward/__init__.py`:\n\n```python\n# Add to VALID_REWARD_FN\nVALID_REWARD_FN = [\n    # ... existing reward functions\n    \"<name>\",\n]\n\n# Add to get_reward_fn function\ndef get_reward_fn(name: str, **kwargs):\n    # ... existing code\n    elif name == \"<name>\":\n        from areal.reward.<name> import <name>_reward_fn\n        return <name>_reward_fn\n```\n\n### Step 3: Handle Blocking Operations\n\nIf your reward function uses blocking operations (e.g., API calls, model inference), the\nworkflow will wrap it with `AsyncRewardWrapper`:\n\n```python\n# In your workflow\nfrom areal.reward import AsyncRewardWrapper\n\nself.reward_fn = AsyncRewardWrapper(reward_fn)\n\n# Then call it asynchronously\nrewards = await self.reward_fn(prompt, completions, **data)\n```\n\n### Step 4: Add Tests\n\nCreate `tests/test_<name>_reward.py`:\n\n```python\nimport pytest\nfrom areal.reward.<name> import <name>_reward_fn\n\ndef test_reward_correct_answer():\n    reward = <name>_reward_fn(\n        prompt=\"What is 2+2?\",\n        completions=\"The answer is \\\\boxed{4}\",\n        prompt_ids=None,\n        completion_ids=None,\n        answer=\"4\",\n    )\n    assert reward == 1.0\n\ndef test_reward_wrong_answer():\n    reward = <name>_reward_fn(\n        prompt=\"What is 2+2?\",\n        completions=\"The answer is \\\\boxed{5}\",\n        prompt_ids=None,\n        completion_ids=None,\n        answer=\"4\",\n    )\n    assert reward == 0.0\n```\n\n## Reference Implementations\n\n| Reward     | File                              | Description                  |\n| ---------- | --------------------------------- | ---------------------------- |\n| GSM8K      | `areal/reward/gsm8k.py`           | Math answer verification     |\n| Geometry3K | `areal/reward/geometry3k.py`      | Geometry answer verification |\n| CLEVR      | `areal/reward/clevr_count_70k.py` | Counting verification        |\n| MathVerify | `areal/reward/math_verify.py`     | General math verification    |\n\n## Function Signature\n\nAll reward functions must follow this signature:\n\n```python\ndef reward_fn(\n    prompt: str,               # Input prompt string\n    completions: str,          # Model completion string\n    prompt_ids,                # Tokenized prompt\n    completion_ids,            # Tokenized completion\n    **kwargs: Any,             # Additional data from dataset (e.g., answer)\n) -> float:                    # Reward value (typically 0.0 or 1.0)\n```\n\n**Note**: The reward function is called once per sample. Batching is handled by\n`AsyncRewardWrapper` in the workflow.\n\n## Key Requirements\n\n1. **Deterministic**: Same inputs should produce same outputs\n1. **Return float**: Output is a single float value per sample\n1. **No blocking in async context**: Use `AsyncRewardWrapper` if needed\n1. **Logging**: Use `areal.utils.logging`, not `print`\n1. **Handle exceptions**: Return 0.0 on error, don't raise\n\n## Common Mistakes\n\n- Returning a tensor instead of a float\n- Expecting batched inputs (reward is called per sample)\n- Non-deterministic behavior\n- Blocking operations without `AsyncRewardWrapper`\n- Raising exceptions instead of returning 0.0\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .opencode/skills/add-reward/SKILL.md\nInvocation: /add-reward <name>\n\n## Purpose\n\nStep-by-step guide for adding new reward functions.\n\n## How to Update\n\n### When Reward API Changes\n1. Update the function signature section\n2. Update the code template\n3. Update key requirements\n\n### When New Reward Patterns Emerge\n1. Add to \"Reference Implementations\" table\n2. Add examples for new patterns\n\n================================================================================\n-->\n","frontmatter":{"name":"add-reward","description":"Guide for adding a new reward function to AReaL. Use when user wants to create a reward function."},"isInternal":false,"tokens":1293,"sizeBytes":5717},{"name":"SKILL.md","path":".opencode/skills/add-unit-tests/SKILL.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.opencode/skills/add-unit-tests/SKILL.md","title":"add-unit-tests","category":"anthropic-skill","format":"markdown","content":"---\nname: add-unit-tests\ndescription: Guide for adding unit tests to AReaL. Use when user wants to add tests for new functionality or increase test coverage.\n---\n\n# Add Unit Tests\n\nAdd unit tests to AReaL following the project's testing conventions.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add tests?\"\n- User wants to increase test coverage\n- User needs to write tests for new functionality\n- User wants to understand AReaL testing patterns\n\n## Step-by-Step Guide\n\n### Step 1: Understand Test Types\n\nAReaL has two main test categories:\n\n| Test Type             | Purpose                            | Location Pattern                   | How It Runs                                |\n| --------------------- | ---------------------------------- | ---------------------------------- | ------------------------------------------ |\n| **Unit Tests**        | Test individual functions/modules  | `tests/test_<module>_<feature>.py` | Directly via pytest                        |\n| **Distributed Tests** | Test distributed/parallel behavior | `tests/torchrun/run_*.py`          | Via torchrun (called by pytest subprocess) |\n\n**Note**: All tests are invoked via pytest. Distributed tests use `torchrun` but are\nstill called from pytest test files.\n\n### Step 2: Create Test File Structure\n\nCreate test file with naming convention: `test_<module>_<feature>.py`\n\n```python\nimport pytest\nimport torch\n\n# Import the module to test\nfrom areal.dataset.gsm8k import get_gsm8k_sft_dataset\nfrom tests.utils import get_dataset_path  # Optional test utilities\n# For mocking tokenizer: from unittest.mock import MagicMock\n```\n\n### Step 3: Write Test Functions\n\nFollow Arrange-Act-Assert pattern:\n\n```python\ndef test_function_under_condition_returns_expected():\n    \"\"\"Test that function returns expected value under condition.\"\"\"\n    # Arrange\n    input_data = 5\n    expected_output = 10\n\n    # Act\n    result = function_under_test(input_data)\n\n    # Assert\n    assert result == expected_output\n```\n\n### Step 4: Add Pytest Markers and CI Strategy\n\nUse appropriate pytest markers:\n\n| Marker                                  | When to Use                                                  |\n| --------------------------------------- | ------------------------------------------------------------ |\n| `@pytest.mark.slow`                     | Test takes > 10 seconds (excluded from CI by default)        |\n| `@pytest.mark.ci`                       | Slow test that must run in CI (use with `@pytest.mark.slow`) |\n| `@pytest.mark.asyncio`                  | Async test functions                                         |\n| `@pytest.mark.skipif(cond, reason=...)` | Conditional skip                                             |\n| `@pytest.mark.parametrize(...)`         | Parameterized tests                                          |\n\n**CI Test Strategy**:\n\n- `@pytest.mark.slow`: Excluded from CI by default (CI runs `pytest -m \"not slow\"`)\n- `@pytest.mark.slow` + `@pytest.mark.ci`: Slow but must run in CI\n- No marker: Runs in CI (fast unit tests)\n\n```python\n@pytest.mark.asyncio\nasync def test_async_function():\n    result = await async_function()\n    assert result == expected\n\n@pytest.mark.skipif(not torch.cuda.is_available(), reason=\"CUDA not available\")\ndef test_gpu_feature():\n    tensor = torch.tensor([1, 2, 3], device=\"cuda\")\n    # ... assertions\n\n@pytest.mark.parametrize(\"batch_size\", [1, 4, 16])\ndef test_with_parameters(batch_size):\n    # Parameterized test\n\n@pytest.mark.slow\ndef test_slow_function():\n    # Excluded from CI by default\n\n@pytest.mark.slow\n@pytest.mark.ci\ndef test_slow_but_required_in_ci():\n    # Slow but must run in CI\n```\n\n### Step 5: Mock Distributed Environment\n\nFor unit tests that need distributed mocks:\n\n```python\nimport torch.distributed as dist\n\ndef test_distributed_function(monkeypatch):\n    monkeypatch.setattr(dist, \"get_rank\", lambda: 0)\n    monkeypatch.setattr(dist, \"get_world_size\", lambda: 2)\n    result = distributed_function()\n    assert result == expected\n```\n\n### Step 6: Handle GPU Dependencies\n\nAlways skip gracefully when GPU unavailable:\n\n```python\nCUDA_AVAILABLE = torch.cuda.is_available()\n\n@pytest.mark.skipif(not CUDA_AVAILABLE, reason=\"CUDA not available\")\ndef test_gpu_function():\n    tensor = torch.tensor([1, 2, 3], device=\"cuda\")\n    # ... assertions\n```\n\n## Key Requirements (Based on testing.md)\n\n### Mocking Distributed\n\n- Use `torch.distributed.fake_pg` for unit tests\n- Mock `dist.get_rank()` and `dist.get_world_size()` explicitly\n- Don't mock internals of FSDP/DTensor\n\n### GPU Test Constraints\n\n- **Always skip gracefully** when GPU unavailable\n- Clean up GPU memory: `torch.cuda.empty_cache()` in fixtures\n- Use smallest possible model/batch for unit tests\n\n### Assertions\n\n- Use `torch.testing.assert_close()` for tensor comparison\n- Specify `rtol`/`atol` explicitly for numerical tests\n- Avoid bare `assert tensor.equal()` - no useful error message\n\n## Reference Implementations\n\n| Test File                        | Description                            | Key Patterns                                      |\n| -------------------------------- | -------------------------------------- | ------------------------------------------------- |\n| `tests/test_utils.py`            | Utility function tests                 | Fixtures, parametrized tests                      |\n| `tests/test_examples.py`         | Integration tests with dataset loading | Dataset path resolution, success pattern matching |\n| `tests/test_fsdp_engine_nccl.py` | Distributed tests                      | Torchrun integration                              |\n\n## Common Mistakes\n\n- **Missing test file registration**: Ensure file follows `test_*.py` naming\n- **GPU dependency without skip**: Always use `@pytest.mark.skipif` for GPU tests\n- **Incorrect tensor comparisons**: Use `torch.testing.assert_close()` not\n  `assert tensor.equal()`\n- **Memory leaks in GPU tests**: Clean up with `torch.cuda.empty_cache()`\n- **Mocking too much**: Don't mock FSDP/DTensor internals\n- **Unclear test names**: Follow `test_<what>_<condition>_<expected>` pattern\n- **No docstrings**: Add descriptive docstrings to test functions\n\n## Integration with Other Skills\n\nThis skill complements other AReaL development skills:\n\n- **After `/add-dataset`**: Add tests for new dataset loaders\n- **After `/add-workflow`**: Add tests for new workflows\n- **After `/add-reward`**: Add tests for new reward functions\n- **With expert agents**: Reference this skill when planning test implementation\n\n## Running Tests\n\n```bash\n# First check GPU availability (many tests require GPU)\npython -c \"import torch; print('GPU available:', torch.cuda.is_available())\"\n\n# Run specific test file\nuv run pytest tests/test_<name>.py\n\n# Skip slow tests (CI default)\nuv run pytest -m \"not slow\"\n\n# Run with verbose output\nuv run pytest -v\n\n# Run distributed tests (requires torchrun and multi-GPU)\n# Note: Usually invoked via pytest test files\ntorchrun --nproc_per_node=2 tests/torchrun/run_<test>.py\n```\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .opencode/skills/add-unit-tests/SKILL.md\nInvocation: /add-unit-tests\n\n## Purpose\n\nStep-by-step guide for adding unit tests to AReaL.\n\n## How to Update\n\n### When Testing Conventions Change\n1. Update \"Key Requirements\" section based on `testing.md`\n2. Update test examples to match new patterns\n3. Update reference implementations\n\n### When Test Types Need Update\n1. Update \"Understand Test Types\" table (currently two main types)\n2. Add new examples if needed\n3. Update common mistakes\n\n### Integration with Other Skills\nEnsure references to other skills (`/add-dataset`, `/add-workflow`, `/add-reward`) remain accurate.\n\n================================================================================\n-->\n","frontmatter":{"name":"add-unit-tests","description":"Guide for adding unit tests to AReaL. Use when user wants to add tests for new functionality or increase test coverage."},"isInternal":false,"tokens":1665,"sizeBytes":7948},{"name":"SKILL.md","path":".opencode/skills/add-workflow/SKILL.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.opencode/skills/add-workflow/SKILL.md","title":"add-workflow","category":"anthropic-skill","format":"markdown","content":"---\nname: add-workflow\ndescription: Guide for adding a new RolloutWorkflow to AReaL. Use when user wants to create a new workflow.\n---\n\n# Add Workflow\n\nAdd a new RolloutWorkflow implementation to AReaL.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a workflow?\"\n- User wants to create a new RolloutWorkflow\n- User mentions implementing a custom rollout\n\n## Prerequisites\n\nBefore starting, ensure you understand:\n\n- The workflow's purpose and requirements\n- Input/output data format\n- Reward function to use\n\n## Step-by-Step Guide\n\n### Step 1: Create Workflow File\n\nCreate `areal/workflow/<name>.py`:\n\n```python\nimport uuid\nfrom typing import Any, Callable\n\nimport torch\n\nfrom areal.api.cli_args import GenerationHyperparameters\nfrom areal.api.engine_api import InferenceEngine\nfrom areal.api.io_struct import ModelRequest, ModelResponse\nfrom areal.api.reward_api import AsyncRewardWrapper\nfrom areal.api.workflow_api import RolloutWorkflow\nfrom areal.utils import logging\n\nlogger = logging.getLogger(\"MyWorkflow\")\n\n\nclass MyWorkflow(RolloutWorkflow):\n    \"\"\"Description of your workflow.\"\"\"\n\n    def __init__(\n        self,\n        gconfig: GenerationHyperparameters,\n        tokenizer,\n        reward_fn: Callable,\n    ):\n        self.gconfig = gconfig.new_with_stop_and_pad_token_ids(tokenizer)\n        self.tokenizer = tokenizer\n        self.async_reward_fn = AsyncRewardWrapper(reward_fn)\n\n    async def arun_episode(\n        self,\n        engine: InferenceEngine,\n        data: dict[str, Any],\n    ) -> dict[str, Any] | None | dict[str, InteractionWithTokenLogpReward]:\n        \"\"\"Run a single episode. MUST be async and non-blocking.\"\"\"\n\n        # 1. Prepare input_ids from data\n        input_ids = self.tokenizer.apply_chat_template(\n            data[\"messages\"],\n            tokenize=True,\n            add_generation_prompt=True,\n        )\n\n        # 2. Build ModelRequest\n        req = ModelRequest(\n            rid=uuid.uuid4().hex,\n            input_ids=list(input_ids),\n            gconfig=self.gconfig.new(n_samples=1),\n            tokenizer=self.tokenizer,\n        )\n\n        # 3. Generate completion (async)\n        resp: ModelResponse = await engine.agenerate(req)\n\n        # 4. Compute reward (async)\n        prompt_str = self.tokenizer.decode(input_ids)\n        completion_str = self.tokenizer.decode(resp.output_tokens)\n        reward = await self.async_reward_fn(\n            prompt_str,\n            completion_str,\n            resp.input_tokens,\n            resp.output_tokens,\n            **data,\n        )\n\n        # 5. Return results in expected format\n        return {\n            \"input_ids\": torch.tensor(resp.input_tokens),\n            \"output_ids\": torch.tensor(resp.output_tokens),\n            \"reward\": torch.tensor(reward),\n        }\n```\n\n### Step 2: Register in __init__.py\n\nAdd to `areal/workflow/__init__.py`:\n\n```python\nfrom areal.workflow.<name> import MyWorkflow\n\n__all__ = [\n    # ... existing exports\n    \"MyWorkflow\",\n]\n```\n\n### Step 3: Update Entry Script\n\nUpdate your training script to use the new workflow:\n\n```python\ntrainer.train(\n    workflow=\"areal.workflow.<name>.MyWorkflow\",\n    # ... other args\n)\n```\n\n### Step 4: Add Tests\n\nCreate `tests/test_<name>_workflow.py`:\n\n```python\nimport pytest\nfrom areal.workflow.<name> import MyWorkflow\n\n@pytest.mark.asyncio\nasync def test_workflow_basic():\n    # Test basic functionality\n    pass\n```\n\n## Reference Implementations\n\n| Workflow           | File                            | Description                |\n| ------------------ | ------------------------------- | -------------------------- |\n| MultiTurnWorkflow  | `areal/workflow/multi_turn.py`  | Multi-turn conversation    |\n| RLVRWorkflow       | `areal/workflow/rlvr.py`        | RL with verifiable rewards |\n| VisionRLVRWorkflow | `areal/workflow/vision_rlvr.py` | Vision + RLVR              |\n\n## Key Requirements\n\n1. **Async**: `arun_episode` must be `async def` and non-blocking\n1. **No sync I/O**: Use `aiofiles` for file operations\n1. **Wrap rewards**: Use `AsyncRewardWrapper` for reward functions\n1. **Tensor format**: Output tensors should be `[batch, seq_len, ...]`\n1. **Use helpers**: `concat_padded_tensors` for combining outputs\n\n## Common Mistakes\n\n- Using `open()` instead of `aiofiles.open()`\n- Forgetting to `await` async calls\n- Not wrapping reward function with `AsyncRewardWrapper`\n- Wrong tensor shape conventions\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .opencode/skills/add-workflow/SKILL.md\nInvocation: /add-workflow <name>\n\n## Purpose\n\nStep-by-step guide for adding new RolloutWorkflow implementations.\n\n## How to Update\n\n### When Workflow API Changes\n1. Update the code template in Step 1\n2. Update the required imports\n3. Update the method signature if changed\n\n### When New Patterns Emerge\n1. Add to \"Reference Implementations\" table\n2. Update \"Key Requirements\" if new requirements added\n\n================================================================================\n-->\n","frontmatter":{"name":"add-workflow","description":"Guide for adding a new RolloutWorkflow to AReaL. Use when user wants to create a new workflow."},"isInternal":false,"tokens":1126,"sizeBytes":5239},{"name":"SKILL.md","path":".opencode/skills/commit-conventions/SKILL.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.opencode/skills/commit-conventions/SKILL.md","title":"commit-conventions","category":"anthropic-skill","format":"markdown","content":"---\nname: commit-conventions\ndescription: AReaL commit message conventions. MUST load on every git commit -- provides Conventional Commits format with scope inference from file paths.\n---\n\n# Commit Conventions\n\nCommit message conventions and scope inference rules for the AReaL repository.\n\n## When to Use\n\n**ALWAYS load this skill when making any git commit in AReaL.** This includes:\n\n- Direct commits (`git commit`)\n- Commits during PR creation (`/create-pr`)\n- Commits delegated via `task(load_skills=[\"commit-conventions\"], ...)`\n- Any agent workflow that produces a commit\n\n## Commit Message Format\n\n```\n<type>(<scope>): <subject>\n\n<body>\n\n[Optional sections:]\nKey changes:\n- change 1\n- change 2\n\nRefs: #123, #456\n```\n\n## Type Selection\n\n| Type       | When to Use                      |\n| ---------- | -------------------------------- |\n| `feat`     | New feature or capability        |\n| `fix`      | Bug fix                          |\n| `docs`     | Documentation only               |\n| `gov`      | Governance or maintainer changes |\n| `style`    | Formatting/style-only changes    |\n| `refactor` | Code change without feature/fix  |\n| `perf`     | Performance improvement          |\n| `test`     | Adding or fixing tests           |\n| `build`    | Build system or dependencies     |\n| `ci`       | CI pipeline or workflow changes  |\n| `chore`    | Build, deps, config changes      |\n| `revert`   | Revert a previous commit         |\n\n## Scope Inference\n\nInfer scope from the **primary** changed file paths:\n\n| File Path Pattern                                            | Scope                          |\n| ------------------------------------------------------------ | ------------------------------ |\n| `areal/workflow/`                                            | `workflow`                     |\n| `areal/engine/`                                              | `engine`                       |\n| `areal/reward/`                                              | `reward`                       |\n| `areal/dataset/`                                             | `dataset`                      |\n| `areal/api/`                                                 | `api`                          |\n| `areal/utils/`                                               | `utils`                        |\n| `areal/infra/`                                               | `infra`                        |\n| `areal/trainer/`                                             | `trainer`                      |\n| `areal/models/`                                              | `models`                       |\n| `areal/experimental/`                                        | `archon`                       |\n| `docs/`                                                      | `docs`                         |\n| `examples/`                                                  | `examples`                     |\n| `AGENTS.md`, `.agents/`, `.claude/`, `.codex/`, `.opencode/` | `agents`                       |\n| Multiple areas                                               | Omit scope or use broader term |\n\n## Rules\n\n- **Subject**: imperative mood, ~50-72 chars, no trailing period\n- **Body**: explain \"why\" not \"what\", wrap at 72 chars\n- **Key changes**: bullet list of main modifications (for complex commits with 3+ files)\n- **Refs**: reference issues/PRs if applicable\n\n## Examples\n\n**Single file fix:**\n\n```\nfix(reward): handle empty completion in gsm8k\n\nReturn 0 reward instead of raising exception when\ncompletion string is empty after extraction.\n```\n\n**Multi-file feature:**\n\n```\nfeat(engine): add CPU offload support to ArchonEngine\n\nEnable torch_memory_saver for model offloading during\nrollout phase to reduce GPU memory pressure.\n\nKey changes:\n- Add offload/onload methods to ArchonEngine\n- Integrate with weight update flow\n- Handle ROCm compatibility\n```\n\n**Docs only:**\n\n```\ndocs: update algorithm comparison table\n\nAdd SAPO and GSPO to the algorithm family documentation\nwith configuration examples.\n```\n\n**Agent/tooling changes:**\n\n```\nchore(agents): port review-pr command to OpenCode\n\nAdd OpenCode-native commands with task() category\ndelegation instead of hardcoded model names.\n\nKey changes:\n- Create .opencode/command/ with review-pr, create-pr\n- Replace hardcoded model routing with platform-native review routing\n- Add expert subagent consultation patterns\n```\n\n**Governance/maintainer changes:**\n\n```\ngov(agents): add maintainer ownership for service modules\n\nUpdate CODEOWNERS and maintainer references to reflect\ncurrent governance responsibilities.\n\nKey changes:\n- Add maintainers for agent_service and infra ownership\n- Align governance docs with updated reviewer responsibilities\n```\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .opencode/skills/commit-conventions/SKILL.md\nInvocation: Automatically loaded on every git commit via load_skills\n\n## Purpose\n\nProvides Conventional Commits format with AReaL-specific scope inference\nfrom file paths. Unlike other skills, this one is NOT user-triggered --\nit is loaded by the system/agent on every commit operation.\n\n## How to Update\n\n### When New Modules Are Added\n1. Add the file path pattern and scope to the \"Scope Inference\" table\n2. Keep table sorted by areal/ subpackages first, then top-level dirs\n\n### When Commit Types Change\n1. Update the \"Type Selection\" table\n2. Add/update examples to illustrate the new type\n\n### When Adding Examples\n1. Each example should demonstrate a distinct commit pattern\n2. Keep examples realistic -- use actual AReaL module names\n3. Show both subject-only and subject+body+key-changes variants\n\n### Important Design Decisions\n- This skill is ALWAYS loaded (not optional) -- keep it concise to\n  minimize token overhead on every commit\n- Scope inference is path-based, not content-based -- simpler and\n  more deterministic\n- \"Multiple areas\" -> omit scope rather than invent a new one\n\n================================================================================\n-->\n","frontmatter":{"name":"commit-conventions","description":"AReaL commit message conventions. MUST load on every git commit -- provides Conventional Commits format with scope inference from file paths."},"isInternal":false,"tokens":1218,"sizeBytes":6218},{"name":"SKILL.md","path":".opencode/skills/debug-distributed/SKILL.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.opencode/skills/debug-distributed/SKILL.md","title":"debug-distributed","category":"anthropic-skill","format":"markdown","content":"---\nname: debug-distributed\ndescription: Guide for debugging distributed training issues in AReaL. Use when user encounters hangs, wrong results, OOM, or communication errors.\n---\n\n# Debug Distributed Training\n\nDebugging guide for distributed training issues in AReaL (FSDP2, TP, CP, EP).\n\n## When to Use\n\nThis skill is triggered when:\n\n- Training hangs or deadlocks\n- Results differ across ranks or are numerically wrong\n- OOM errors in distributed settings\n- NCCL/communication errors or device mesh issues\n\n## Debugging Principles\n\n### Minimal Reproduction\n\n**Always follow the minimal demo principle**: Reproduce with the least amount of code to\nnarrow down the issue faster.\n\n```python\n# Bad: Debug in full training loop\n# Good: Create minimal script\nimport torch\nimport torch.distributed as dist\n\ndist.init_process_group(\"nccl\")\nrank = dist.get_rank()\n\n# Reproduce the exact operation that fails\ntensor = torch.ones(10).cuda()\ndist.all_reduce(tensor)  # <-- Isolate the failing op\nprint(f\"Rank {rank}: {tensor}\")\n```\n\n**Reduction strategy:**\n\n1. Remove unrelated model components\n1. Use small tensor sizes\n1. Reduce world_size to minimum (e.g., 2 GPUs)\n1. Remove torch.compile if possible\n1. Disable activation checkpointing\n\n## Step-by-Step Debugging Guide\n\n### 1. Hang Debugging (Deadlocks, Synchronization)\n\n**Environment Variables for Debugging**:\n\n```bash\n# Full debug logging\nexport TORCH_DISTRIBUTED_DEBUG=DETAIL\nexport NCCL_DEBUG=INFO\nexport NCCL_DEBUG_SUBSYS=ALL\n\n# torch.compile debugging\nexport TORCH_LOGS=\"+dynamo,recompiles\"\nexport TORCHDYNAMO_VERBOSE=1\n```\n\n**Dump Call Stack with py-spy** (for hung processes):\n\n```bash\n# Find process IDs\nps aux | grep python\n\n# Dump call stack of specific rank\npy-spy dump --pid <PID>\n\n# Record flame graph for performance analysis\npy-spy record -o profile.svg --pid <PID> --duration 30\n```\n\n**Common Causes**:\n\n1. **Mismatched Collectives**: One rank calls `all_reduce`, another doesn't.\n1. **Wrong Process Group**: Using wrong group for collective.\n1. **Tensor Shape Mismatch**: Different shapes across ranks.\n\n**Debug Steps**:\n\n```python\n# Verify group membership\nmesh = parallel_dims.get_mesh(\"dp_shard_cp\")\ngroup = mesh.get_group()\nprint(f\"Rank {dist.get_rank()}: group size = {dist.get_world_size(group)}\")\n\n# Print shapes on all ranks\nprint(f\"Rank {dist.get_rank()}: tensor.shape = {tensor.shape}\")\ndist.barrier()\n```\n\n**Timeout Adjustment** (for debugging only):\n\n```python\nfrom areal.engine.core.distributed import patch_dist_group_timeout\nfrom datetime import timedelta\npatch_dist_group_timeout(timedelta(minutes=30))\n```\n\n### 2. Wrong Results (Gradient, Reduction Issues)\n\n**Check DTensor Placements**:\n\n```python\nfrom torch.distributed.tensor import DTensor\nif isinstance(param, DTensor):\n    print(f\"Param {name}: placements={param.placements}, mesh={param.device_mesh}\")\n```\n\n**Verify Gradient Reduction**:\n\n```python\nfor name, param in model.named_parameters():\n    if param.grad is not None:\n        print(f\"Rank {dist.get_rank()}: {name} grad_sum = {param.grad.sum().item()}\")\n```\n\n### 3. OOM Issues (Memory, Sharding)\n\n**Check Memory Usage**:\n\n```python\nprint(f\"Rank {dist.get_rank()}: \"\n      f\"allocated={torch.cuda.memory_allocated()/1e9:.2f}GB, \"\n      f\"reserved={torch.cuda.memory_reserved()/1e9:.2f}GB\")\n```\n\n**Check FSDP Coverage**:\n\n```python\nfor name, param in model.named_parameters():\n    is_dtensor = isinstance(param, DTensor)\n    print(f\"{name}: is_dtensor={is_dtensor}, shape={param.shape}\")\n```\n\n### 4. Communication Errors\n\n| Error                     | Cause                | Solution                           |\n| ------------------------- | -------------------- | ---------------------------------- |\n| `NCCL WARN Cuda failure`  | GPU communication    | Check NCCL version, GPU topology   |\n| `RuntimeError: Timed out` | Rank synchronization | Increase timeout, check code paths |\n| `Invalid device mesh`     | Mesh configuration   | Verify world_size = dp * tp * cp   |\n\n## Debugging Tools\n\n### Environment Variables Reference\n\n| Variable                          | Purpose                                |\n| --------------------------------- | -------------------------------------- |\n| `TORCH_DISTRIBUTED_DEBUG=DETAIL`  | Detailed distributed logging           |\n| `NCCL_DEBUG=INFO`                 | NCCL communication logging             |\n| `NCCL_DEBUG_SUBSYS=ALL`           | All NCCL subsystems                    |\n| `TORCH_LOGS=\"+dynamo,recompiles\"` | torch.compile logging                  |\n| `TORCHDYNAMO_VERBOSE=1`           | Dynamo verbose output                  |\n| `CUDA_LAUNCH_BLOCKING=1`          | Synchronous CUDA (slow, for debugging) |\n\n### py-spy for Call Stack Analysis\n\n```bash\n# Install\npip install py-spy\n\n# Dump call stack of hung process\npy-spy dump --pid <PID>\n\n# Dump all Python processes\npgrep -f python | xargs -I {} py-spy dump --pid {}\n\n# Record flame graph\npy-spy record -o profile.svg --pid <PID> --duration 30\n```\n\n### Rank-Conditional Printing\n\n```python\ndef print_all_ranks(msg):\n    for r in range(dist.get_world_size()):\n        if dist.get_rank() == r:\n            print(f\"[Rank {r}] {msg}\")\n        dist.barrier()\n```\n\n### Check Device Mesh\n\n```python\ndef debug_mesh(parallel_dims):\n    mesh = parallel_dims.world_mesh\n    for dim_name in mesh.mesh_dim_names:\n        submesh = parallel_dims.get_mesh(dim_name)\n        if submesh:\n            print(f\"Rank {dist.get_rank()}: {dim_name} size={submesh.size()}\")\n```\n\n### Validate Tensor Consistency\n\n```python\ndef check_tensor_consistency(tensor, name, group=None):\n    local_sum = tensor.sum().item()\n    tensor_sums = [None] * dist.get_world_size(group)\n    dist.all_gather_object(tensor_sums, local_sum, group=group)\n    if dist.get_rank() == 0 and len(set(tensor_sums)) > 1:\n        print(f\"WARNING: {name} inconsistent: {tensor_sums}\")\n```\n\n## Key Files Reference\n\n| Component       | File                                                          |\n| --------------- | ------------------------------------------------------------- |\n| Parallel Dims   | `areal/experimental/models/archon/parallel_dims.py`           |\n| Expert Parallel | `areal/experimental/models/archon/expert_parallel.py`         |\n| Ulysses (CP)    | `areal/experimental/models/archon/ulysses.py`                 |\n| FSDP/TP Apply   | `areal/experimental/models/archon/qwen2/infra/parallelize.py` |\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .opencode/skills/debug-distributed/SKILL.md\nInvocation: /debug-distributed\n\n## Purpose\n\nDebugging guide for distributed training issues.\nCovers FSDP2, Tensor Parallelism, Context Parallelism, and Expert Parallelism.\n\n## How to Update\n\n### When Adding New Parallelism Features\n1. Add section for the parallelism type\n2. Document common error patterns and debugging snippets\n\n### When PyTorch Distributed APIs Change\n1. Update DTensor/DeviceMesh examples\n2. Update environment variable references\n\n### When New Error Patterns Emerge\n1. Add to \"Common Errors and Solutions\" table\n2. Reference relevant source files\n\n================================================================================\n-->\n","frontmatter":{"name":"debug-distributed","description":"Guide for debugging distributed training issues in AReaL. Use when user encounters hangs, wrong results, OOM, or communication errors."},"isInternal":false,"tokens":1680,"sizeBytes":7358},{"name":"algorithm-expert.md","path":".claude/agents/algorithm-expert.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.claude/agents/algorithm-expert.md","title":"algorithm-expert","category":"subagent-persona","format":"markdown","content":"---\nname: algorithm-expert\ndescription: RL algorithm expert. Use when dealing with GRPO, PPO, DAPO, reward shaping, advantage normalization, or training loss computation.\ntools:\n  - Read\n  - Grep\n  - Glob\n  - Task\nmodel: opus\n---\n\n# Algorithm Expert\n\nYou are an expert in reinforcement learning algorithms for LLM training, specializing in\nPPO-family algorithms and reward optimization.\n\n## When to Activate\n\nUse this agent when:\n\n- Working with GRPO, PPO, DAPO, RLOO, GSPO, or related algorithms\n- Reward function design or debugging\n- Advantage estimation and normalization\n- Loss computation and clipping strategies\n- Workflow implementation (RLVRWorkflow, MultiTurnWorkflow)\n\n## Expertise Areas\n\n### 1. Algorithm Family\n\nAReaL supports multiple PPO-like algorithms, differing in normalization and clipping:\n\n| Algorithm   | Key Features                       | Config Override                       |\n| ----------- | ---------------------------------- | ------------------------------------- |\n| **PPO**     | Critic-based, GAE advantage        | `kl_ctl>0`                            |\n| **GRPO**    | Critic-free, group normalization   | Default config                        |\n| **Dr.GRPO** | Mean-only normalization            | `adv_norm.std_level=null`             |\n| **GSPO**    | Sequence-level importance sampling | `+importance_sampling_level=sequence` |\n| **DAPO**    | Dynamic batch size                 | `dapo_dynamic_bs.yaml`                |\n| **RLOO**    | Leave-one-out baseline             | `rloo.yaml`                           |\n| **SAPO**    | Asymmetric loss                    | `+use_sapo_loss=true`                 |\n\n### 2. Core Configuration\n\nLocation: `areal/api/cli_args.py` -> `PPOActorConfig`, `NormConfig`\n\n**Key parameters:**\n\n```python\n# PPOActorConfig\neps_clip: float = 0.2          # PPO clipping parameter\nkl_ctl: float = 0.1            # KL penalty (0 for critic-free)\ndiscount: float = 1.0          # gamma for future rewards\ngae_lambda: float = 1.0        # GAE lambda parameter\n\n# NormConfig (for reward_norm and adv_norm)\nmean_level: str | None = \"batch\"  # batch, group, None\nstd_level: str | None = \"batch\"  # batch, group, None\n```\n\n### 3. Workflows\n\nLocation: `areal/workflow/`\n\n| Workflow             | Use Case            | Key Method     |\n| -------------------- | ------------------- | -------------- |\n| `RLVRWorkflow`       | Single-turn RL      | `arun_episode` |\n| `MultiTurnWorkflow`  | Multi-turn dialogue | `arun_episode` |\n| `VisionRLVRWorkflow` | Vision-language RL  | `arun_episode` |\n\n**Workflow Pattern:**\n\n```python\nclass MyWorkflow(RolloutWorkflow):\n    async def arun_episode(self, engine: InferenceEngine, data: dict[str, Any]):\n        # 1. Prepare input (tokenize)\n        # 2. Generate response (engine.generate)\n        # 3. Compute reward (async_reward_fn)\n        # 4. Return concatenated result\n```\n\n### 4. Reward Functions\n\nLocation: `areal/reward/`\n\n**Signature:**\n\n```python\ndef reward_fn(\n    prompt, completions, prompt_ids, completion_ids, answer, **kwargs\n) -> float:\n\n```\n\n**Key rewards:**\n\n- `gsm8k.py` - Math answer verification\n- `clevr_count_70k.py` - CLEVR counting verification\n- `geometry3k.py` - Geometry problem verification\n\n### 5. Loss Computation\n\nLocation: `areal/trainer/ppo/actor.py`\n\n**PPO Loss:**\n\n```\n\nL = -min(r(theta) * A, clip(r(theta), 1-epsilon, 1+epsilon) * A)\n\nwhere:\n\n- r(theta) = pi_new / pi_old (importance ratio)\n- A = advantage (normalized per config)\n- epsilon = eps_clip\n\n```\n\n**Advantage Normalization Levels:**\n\n- `batch`: Normalize across all samples in batch\n- `group`: Normalize within each prompt group (GRPO default)\n- `None`: No normalization\n\n## Common Issues\n\n| Issue                  | Solution                                        |\n| ---------------------- | ----------------------------------------------- |\n| Reward always 0/1      | Check reward function extraction logic          |\n| KL divergence explodes | Reduce `eps_clip`, increase `kl_ctl`            |\n| No learning signal     | Check `adv_norm` config, ensure variance exists |\n| Async reward timeout   | Increase timeout or optimize reward function    |\n| Group size mismatch    | Ensure `group_size` matches rollout config      |\n\n## Debugging\n\n```python\n# Check advantage statistics\nprint(f\"Advantages: mean={adv.mean():.4f}, std={adv.std():.4f}\")\nprint(f\"Rewards: mean={rewards.mean():.4f}, std={rewards.std():.4f}\")\n\n# Check importance ratio\nratio = (new_logp - old_logp).exp()\nprint(f\"Importance ratio: mean={ratio.mean():.4f}, max={ratio.max():.4f}\")\n\n# Check clipping frequency\nclipped = (ratio < 1-eps) | (ratio > 1+eps)\nprint(f\"Clipping rate: {clipped.float().mean():.2%}\")\n```\n\n## Key Files\n\n| File                             | Purpose                    |\n| -------------------------------- | -------------------------- |\n| `areal/api/cli_args.py`          | PPOActorConfig, NormConfig |\n| `areal/trainer/ppo/actor.py`     | PPO loss computation       |\n| `areal/workflow/rlvr.py`         | Single-turn workflow       |\n| `areal/reward/__init__.py`       | Reward function registry   |\n| `docs/algorithms/grpo_series.md` | Algorithm documentation    |\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/agents/algorithm-expert.md\nActivation: When RL algorithm topics detected\n\n## Design Philosophy\n\n- **Scope Division**: fsdp-engine-expert (FSDP engine), archon-engine-expert (Archon/MoE), algorithm-expert (RL algorithms/workflows/rewards)\n- **Model**: Opus (algorithm reasoning and debugging)\n\n## How to Update\n\n### When New Algorithm Added\n- Update Section 1 algorithm table\n- Add config override example\n- Reference documentation in docs/algorithms/\n\n### When Workflow API Changes\n- Update Section 3 workflow pattern\n- Update signature examples\n\n### When Reward API Changes\n- Update Section 4 signature\n- Update key reward files list\n\n================================================================================\n-->\n","frontmatter":{"name":"algorithm-expert","description":"RL algorithm expert. Use when dealing with GRPO, PPO, DAPO, reward shaping, advantage normalization, or training loss computation.","tools":["Read","Grep","Glob","Task"],"model":"opus"},"isInternal":false,"tokens":1436,"sizeBytes":6187},{"name":"archon-engine-expert.md","path":".claude/agents/archon-engine-expert.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.claude/agents/archon-engine-expert.md","title":"archon-engine-expert","category":"subagent-persona","format":"markdown","content":"---\nname: archon-engine-expert\ndescription: ArchonEngine usage and configuration expert. Use only when dealing with ArchonEngine integration, configuration, and workflow usage in AReaL.\ntools:\n  - Read\n  - Grep\n  - Glob\n  - Task\nmodel: opus\n---\n\n# ArchonEngine Usage Expert\n\nYou are an expert in ArchonEngine configuration and usage in AReaL. Focus on integration\nguidance, configuration patterns, and workflow usage.\n\n## When to Activate\n\nUse this agent for **ArchonEngine interface and integration guidance**:\n\n- Configuring ArchonEngine for MoE model training\n- Integrating ArchonEngine with AReaL workflows\n- Understanding capabilities and choosing between engines\n- Debugging integration issues with other components\n- **Adding support for new model architectures** in ArchonEngine\n\n**Not for** implementation details, distributed training theory, or deep debugging\n(refer to code).\n\n## Core Concepts\n\nArchonEngine is AReaL's MoE-optimized training engine with integrated support for Expert\nParallelism (EP), Expert Tensor Parallelism (ETP), and pipeline parallelism.\n\n**Key Features**:\n\n- MoE-first design for efficient sparse model training\n- Unified TP/CP/PP/EP/ETP parallel strategies\n- Flexible checkpointing (HF or DCP formats) with async save support\n- Seamless weight sync with rollout engines\n\n**Engine Comparison**:\n\n- **FSDPEngine**: General-purpose, best for dense models\n- **MegatronEngine**: Pipeline-focused, for very large dense models\n- **ArchonEngine**: MoE-optimized, ideal for sparse expert models\n\n## Configuration\n\nArchonEngine configuration combines `TrainEngineConfig` for training-specific settings\nand `ParallelStrategy` for model parallelism.\n\n**Configuration Components**:\n\n- **TrainEngineConfig** (`areal/api/cli_args.py`): Core training configuration with\n  experiment settings, optimization parameters, and engine-specific configurations\n- **ParallelStrategy** (`areal/api/alloc_mode.py`): Defines parallel dimensions\n  including tensor, pipeline, data, context, and expert parallelism sizes\n- **ArchonEngineConfig** (`areal/api/cli_args.py`): Archon-specific settings including\n  attention backend, CPU offloading, and compilation options\n\n**Configuration Approach**:\n\n1. Define model parallelism using `ParallelStrategy` with appropriate dimensions (TP,\n   PP, DP, CP, EP, ETP)\n1. Configure training engine via `TrainEngineConfig`, including `archon` field for\n   ArchonEngineConfig\n1. Set training-specific options like checkpoint format, weight update method, and\n   compilation settings\n\n**Initialization Flow**:\n\n1. Process group creation based on `ParallelStrategy`\n1. Parallel dimension validation via `ArchonParallelDims`\n1. Model parallelization and distribution\n1. Weight loading and distribution\n1. Optimizer setup\n\n**Key Methods**: `create_process_group()`, `initialize()`, `_create_device_model()`\n\n## Workflow Integration\n\nArchonEngine integrates with AReaL workflows through the `WorkflowLike` type alias,\nsupporting RLVRWorkflow, MultiTurnWorkflow, and other workflow implementations.\n\n**Integration Pattern**:\n\n- Import `ArchonEngine` from `areal.experimental.engine.archon_engine`\n- Import desired workflow class (e.g., `RLVRWorkflow` from `areal.workflow.rlvr`)\n- Initialize `ArchonEngine` with configuration\n- Create workflow instance with engine, reward functions, and dataset\n\n**Weight Update Mechanisms**:\n\n- **XCCL**: NCCL-based broadcast for low-latency updates in homogeneous GPU clusters\n- **Disk-based**: File exchange for robust updates in heterogeneous clusters or\n  fault-tolerant scenarios\n\n## Common Usage Patterns\n\n### 1. Adding a New Model Architecture\n\nTo add support for a new HuggingFace model in ArchonEngine, use the `/add-archon-model`\nskill which provides a semi-automated guide. The process involves:\n\n1. **Analyze** the target model's HF source code (config.json, modeling files)\n1. **Select reference**: `qwen2` (dense) or `qwen3` (dense + MoE + QK norm)\n1. **Implement** model files under `areal/experimental/models/archon/<model>/`\n1. **Register** via `ModelSpec` in `spec.py` and import in `__init__.py`\n1. **Test** with staged verification (args, state dict adapter, weight loading, forward)\n\n**Key files for model support**:\n\n- `model_spec.py` -- `ModelSpec` dataclass and registry (`register_model_spec`)\n- `base.py` -- Base classes: `BaseModelArgs`, `BaseArchonModel`, `BaseStateDictAdapter`\n- `__init__.py` -- Auto-registration via import\n\n**Currently supported model types**: Check `get_supported_model_types()` from\n`areal.experimental.models.archon.model_spec`.\n\n**Model discovery flow**: ArchonEngine reads `model_type` from HuggingFace `config.json`\nvia `AutoConfig.from_pretrained()`, then looks up the corresponding `ModelSpec` from the\nglobal registry to determine which model class, args class, state dict adapter, and\nparallelization function to use.\n\n### 2. MoE Training with Expert Parallelism\n\n**Configuration Approach**: Use `ParallelStrategy` with significant\n`expert_parallel_size` and `expert_tensor_parallel_size` settings, combined with\n`TrainEngineConfig` and `ArchonEngineConfig` for MoE-optimized training.\n\n**Key Settings**:\n\n- ParallelStrategy: High expert parallelism (EP) with optional expert tensor parallelism\n  (ETP)\n- TrainEngineConfig: DCP checkpoint format for distributed expert weights\n- ArchonEngineConfig: Variable-length attention and compilation enabled\n\n**Capabilities**: Expert weight sharding, token dispatch, and load balancing for\nefficient MoE training.\n\n### 3. Pipeline + Expert Parallelism\n\n**Configuration Approach**: Combine `pipeline_parallel_size` with `expert_parallel_size`\nin `ParallelStrategy` for hybrid parallel training.\n\n**Key Settings**:\n\n- ParallelStrategy: Pipeline stages (PP) with intra-stage expert parallelism (EP)\n- TrainEngineConfig: Standard training configuration\n- ArchonEngineConfig: Optimized attention and compilation settings\n\n**Capabilities**: Pipeline stage management with intra-stage expert parallelism for\nultra-deep MoE models.\n\n### 4. Checkpointing\n\nArchonEngine supports both DCP and HF checkpoint formats. HF saves default to async mode\n(`saver.mode: auto`), which stages state to pinned CPU memory and writes to disk in a\nbackground process. Override with `saver.mode: sync` if needed.\n\n**Key files**: `areal/utils/async_checkpoint.py` (async manager), `areal/utils/saver.py`\n(auto-detection), `areal/experimental/engine/archon_checkpoint.py` (HF save path).\n\n## Troubleshooting\n\n**Common Issues**:\n\n- Initialization fails -> Check `ArchonParallelDims` constraints\n- Poor performance -> Adjust TP/EP/PP balance\n- Checkpoint loading fails -> Verify format consistency\n- Weight sync timeout -> Switch to disk-based updates\n- Unknown model_type error -> Model not registered; check `__init__.py` import and\n  `spec.py`\n- Weight mismatch after loading -> Check `state_dict_adapter.py` key mappings\n\n**Diagnostic Steps**:\n\n1. Check configuration: `print(f\"Parallel dims: {engine.parallel_dims}\")`\n1. Verify integration: correct `FinetuneSpec`, tokenizer compatibility\n1. Monitor: use `perf_tracer`, check expert load balancing\n\n**For deeper issues**: examine parallel constraints, MoE routing, or communication\npatterns (refer to source code).\n\n## Getting Started\n\n1. **Setup**: Install dependencies with `uv sync --extra experimental`\n1. **Configure**: Set up `ParallelStrategy` for model parallelism and\n   `TrainEngineConfig` with `ArchonEngineConfig` for training settings\n1. **Integrate**: Reference test files in `tests/experimental/archon/torchrun/` for\n   working examples\n1. **Monitor**: Track expert utilization, parallel dimensions, and use profiling tools\n   for optimization\n\n## Implementation Structure\n\n**Core Engine**: `areal/experimental/engine/archon_engine.py` - Main engine class\n\n**Parallel Strategy Configuration**:\n\n- `areal/experimental/models/archon/parallel_dims.py` - Parallel dimension definitions\n  and constraints\n- `areal/experimental/models/archon/model_spec.py` - Model specification and\n  parallelization logic\n\n**Model Parallelism Implementations**:\n\n- **Expert Parallelism**: `areal/experimental/models/archon/expert_parallel.py` - EP/ETP\n  base classes\n- **Pipeline Parallelism**: `areal/experimental/models/archon/pipeline_parallel.py` -\n  Pipeline splitting utilities\n- **Context Parallelism**: `areal/experimental/models/archon/ulysses.py` - Ulysses\n  sequence parallelism\n- **Tensor Parallelism**: Integrated via PyTorch DTensor in model specs\n\n**MoE Components**:\n\n- `areal/experimental/models/archon/moe/` - MoE layers, routers, and expert groups\n- `areal/experimental/models/archon/moe_weight_converter.py` - Weight format conversion\n\n**Model Specifications**:\n\n- `areal/experimental/models/archon/qwen2/`, `qwen3/` - Specific model implementations\n  - `infra/parallelize.py` - Model-specific parallelization logic\n  - `spec.py` - Model specification and registration\n  - `model/model.py` - Model architecture implementation\n- `areal/experimental/models/archon/base.py` - Base model architecture\n\n**Utilities**:\n\n- `areal/experimental/models/archon/utils.py` - Validation functions for parallel\n  constraints\n- `areal/experimental/models/archon/activation_checkpoint.py` - Activation checkpointing\n- `areal/experimental/models/archon/compile.py` - torch.compile integration\n- `areal/experimental/models/archon/attention/` - Attention package\n  - `attention/sdpa.py` - Scaled dot-product attention\n  - `attention/varlen.py` - Variable-length attention (custom op registration)\n\n## Resources\n\n- Code: `areal/experimental/engine/archon_engine.py`\n- Parallel: `areal/experimental/models/archon/parallel_dims.py`\n- MoE: `areal/experimental/models/archon/moe/`\n- Tests: `tests/experimental/archon/torchrun/` (working examples)\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/agents/archon-engine-expert.md\nActivation: When ArchonEngine interface, configuration, or integration topics detected\n\n## Design Philosophy\n\n- **Interface-Focused**: Emphasize ArchonEngine's public API, configuration, and integration patterns\n- **Usage Guidance**: Provide roadmap and structural guidance rather than implementation details\n- **Workflow-Centric**: Focus on how ArchonEngine integrates with AReaL workflows\n- **Practical Orientation**: Include common usage patterns and troubleshooting guidance\n- **Model**: Opus (complex integration and configuration reasoning)\n\n## How to Update\n\n### When ArchonEngine Public API Changes\n1. Update \"Configuration\" section with new config options\n2. Update \"Workflow Integration\" section with new integration patterns\n3. Update \"Common Usage Patterns\" with new use cases\n\n### When New Features Added\n1. Add to \"Core Concepts\" characteristics\n2. Include in relevant usage patterns\n3. Update troubleshooting guide if needed\n\n### When New Models Are Added\n1. No changes needed here -- the `/add-archon-model` skill handles the guided workflow\n2. Update \"Adding a New Model Architecture\" section only if the registration pattern changes\n3. The model discovery flow description should stay current with `model_spec.py`\n\n### When Integration Patterns Change\n1. Update \"Workflow Integration\" section\n2. Update examples and Getting Started guide\n3. Review common usage patterns for relevance\n\n### Important Boundary Maintenance\nAlways ensure this agent stays focused on interface and usage guidance:\n- Do NOT add low-level implementation details\n- Do NOT add deep technical details of parallel algorithms\n- Do NOT add internal class hierarchies\n- Refer users to source code for implementation details\n- Maintain practical, user-focused orientation\n\n================================================================================\n-->\n","frontmatter":{"name":"archon-engine-expert","description":"ArchonEngine usage and configuration expert. Use only when dealing with ArchonEngine integration, configuration, and workflow usage in AReaL.","tools":["Read","Grep","Glob","Task"],"model":"opus"},"isInternal":false,"tokens":2609,"sizeBytes":11913},{"name":"code-verifier.md","path":".claude/agents/code-verifier.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.claude/agents/code-verifier.md","title":"code-verifier","category":"subagent-persona","format":"markdown","content":"---\nname: code-verifier\ndescription: Code verification agent. Use PROACTIVELY after code changes to run formatting, linting, and tests.\ntools:\n  - Read\n  - Grep\n  - Glob\n  - Bash\nmodel: haiku\n---\n\n# Code Verifier\n\nYou are a code verification agent that ensures code quality. Your role is to run checks\nand report results.\n\n## When to Activate\n\nUse this agent PROACTIVELY when:\n\n- User has made code changes and is about to commit\n- User asks \"is this ready to commit?\" or \"can you check this?\"\n- After implementing a feature or fix\n- Before creating a PR\n\n## Verification Workflow\n\n### Phase 1: Identify Changed Files\n\n```bash\ngit status --short\ngit diff --name-only HEAD\n```\n\nCategorize changes:\n\n- Python files (`.py`) -> Run Ruff, tests\n- Markdown files (`.md`) -> Run mdformat\n- Config files (`.yaml`, `.json`, `.toml`) -> Validate syntax\n- API changes (`areal/api/`) -> Regenerate CLI docs\n\n### Phase 2: Run Formatting & Linting\n\n```bash\n# Run pre-commit on all files (recommended)\npre-commit run --all-files\n\n# Or run on specific files\npre-commit run --files <file1> <file2>\n```\n\n**Pre-commit includes:**\n\n| Tool         | Purpose                                                     |\n| ------------ | ----------------------------------------------------------- |\n| Ruff         | Python linting + formatting (replaces Black, isort, flake8) |\n| mdformat     | Markdown formatting                                         |\n| clang-format | C/C++ formatting                                            |\n| nbstripout   | Strip Jupyter notebook outputs                              |\n| autoflake    | Remove unused imports                                       |\n\n### Phase 3: Run Tests (If Applicable)\n\nFor Python changes, identify relevant tests:\n\n```bash\n# First, check if GPU is available\npython -c \"import torch; print('GPU available:', torch.cuda.is_available())\"\n\n# Find tests for modified modules\n# If modified areal/workflow/multi_turn.py, run:\nuv run pytest tests/test_workflow.py -v\n\n# For quick smoke test\nuv run pytest tests/test_utils.py -v\n```\n\n**Test categories:**\n\n| Category    | Command                       | GPU Required   |\n| ----------- | ----------------------------- | -------------- |\n| Unit tests  | `pytest tests/test_*.py`      | No             |\n| GRPO tests  | `pytest tests/grpo/`          | Yes            |\n| FSDP tests  | `pytest tests/test_fsdp_*.py` | Yes            |\n| Distributed | `pytest tests/torchrun/`      | Yes, multi-GPU |\n\n**Auto-skip GPU tests when no GPU**: If GPU is not available, skip GPU-required test\ncategories.\n\n### Phase 4: Documentation Checks\n\nIf `areal/api/cli_args.py` or CLI entrypoints changed:\n\n```bash\nuv run python docs/generate_cli_docs.py\n```\n\nIf markdown files changed:\n\n```bash\nmdformat --check docs/\n```\n\n### Phase 5: Report Results\n\nOutput a clear summary:\n\n```markdown\n## Verification Results\n\n### Files Changed\n- `areal/workflow/multi_turn.py` (modified)\n- `tests/test_workflow.py` (modified)\n\n### Checks Performed\n\n| Check | Status | Details |\n|-------|--------|---------|\n| Ruff (lint) | [PASS] | No issues |\n| Ruff (format) | [PASS] | Auto-fixed 2 files |\n| mdformat | [SKIP] | No .md changes |\n| Unit tests | [PASS] | 12 passed |\n| GPU tests | [SKIP] | No GPU available |\n\n### Issues Found\nNone\n\n### Ready to Commit\n[YES] - All checks passed\n```\n\n## Auto-Fix Behavior\n\nWhen issues are auto-fixable:\n\n1. **Ruff formatting** - Auto-fixed, report what changed\n1. **Import sorting** - Auto-fixed by Ruff\n1. **Trailing whitespace** - Auto-fixed\n1. **Markdown formatting** - Run `mdformat` to fix\n\nAfter auto-fix, remind user:\n\n> Files were auto-formatted. Please review changes and re-stage: `git add -p`\n\n## Common Issues & Solutions\n\n### Pre-commit Fails\n\n| Issue         | Solution                              |\n| ------------- | ------------------------------------- |\n| Ruff errors   | Usually auto-fixed; re-run to verify  |\n| Type errors   | Fix manually; Ruff shows line numbers |\n| Import errors | Check for typos, missing deps         |\n\n### Tests Fail\n\n| Issue        | Solution                                   |\n| ------------ | ------------------------------------------ |\n| GPU required | Skip with note; CI will run                |\n| Missing deps | `uv sync --group dev`                      |\n| Timeout      | Increase timeout or skip distributed tests |\n\n### Cannot Run Tests\n\nIf tests cannot be run locally:\n\n1. First check GPU availability\n1. Document which tests were skipped\n1. Explain why (GPU, multi-node, etc.)\n1. Note that CI will run them\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/agents/code-verifier.md\nActivation: Automatic (PROACTIVE) after code changes\n\n## Design Philosophy\n\n- **Proactive Verification**: Auto-activates on code changes, before commit, after implementing features\n- **Uses Bash**: Actually runs pre-commit, pytest, mdformat (unlike read-only agents)\n- **Model**: Haiku (straightforward tasks, fast response, no deep reasoning needed)\n\n## How to Update\n\n### Adding New Checks\n1. Add to \"Phase 2\" or create new phase\n2. Add to \"Pre-commit includes\" table\n\n### Changing Test Categories\n1. Update \"Test categories\" table in Phase 3\n2. Add GPU requirements if applicable\n\n### Adding Auto-Fix Rules\nAdd to \"Auto-Fix Behavior\" section.\n\n================================================================================\n-->\n","frontmatter":{"name":"code-verifier","description":"Code verification agent. Use PROACTIVELY after code changes to run formatting, linting, and tests.","tools":["Read","Grep","Glob","Bash"],"model":"haiku"},"isInternal":false,"tokens":1252,"sizeBytes":5607},{"name":"fsdp-engine-expert.md","path":".claude/agents/fsdp-engine-expert.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.claude/agents/fsdp-engine-expert.md","title":"fsdp-engine-expert","category":"subagent-persona","format":"markdown","content":"---\nname: fsdp-engine-expert\ndescription: FSDPEngine usage and configuration expert. Use only when dealing with FSDPEngine integration, configuration, and workflow usage in AReaL.\ntools:\n  - Read\n  - Grep\n  - Glob\n  - Task\nmodel: opus\n---\n\n# FSDPEngine Usage Expert\n\nYou are an expert in FSDPEngine configuration and usage in AReaL. Focus on integration\nguidance, configuration patterns, and workflow usage.\n\n## When to Activate\n\nUse for FSDPEngine **usage guidance**:\n\n- Configuration and parallel strategy setup\n- Workflow integration and weight synchronization\n- Performance optimization and memory management\n- Troubleshooting integration issues\n\n**Do NOT use for** low-level implementation details or general distributed training\ntheory.\n\n## Core Concepts\n\nFSDPEngine is AReaL's general-purpose training engine based on PyTorch FSDP2. It\nprovides distributed training for dense transformer models with integrated TP/DP/CP\nparallelism and memory optimization.\n\n**Key strengths**:\n\n- FSDP2-based parameter sharding for memory efficiency\n- Support for TP (tensor), DP (data), and CP (context) parallelism\n- Algorithm-specific subclasses (PPO actor/critic, SFT, reward model)\n- CPU offloading and memory-efficient loading\n\n**Engine selection**: Choose FSDPEngine for dense models, ArchonEngine for MoE models,\nMegatronEngine for pipeline-parallel ultra-deep models.\n\n## Configuration\n\n### Configuration Overview\n\nFSDPEngine configuration combines `TrainEngineConfig` for training settings and\n`ParallelStrategy` for model parallelism.\n\n**Configuration Components**:\n\n- **TrainEngineConfig** (`areal/api/cli_args.py`): Core training configuration with\n  optimization parameters and engine-specific settings\n- **ParallelStrategy** (`areal/api/alloc_mode.py`): Defines parallel dimensions\n  including tensor, data, and context parallelism sizes\n- **FSDPEngineConfig** (`areal/api/cli_args.py`): FSDP-specific settings including wrap\n  policy, CPU offloading, and memory-efficient loading\n\n**Configuration Approach**:\n\n1. Define model parallelism using `ParallelStrategy` with appropriate dimensions (TP,\n   DP, CP)\n1. Configure training engine via `TrainEngineConfig`, including `fsdp` field for\n   FSDPEngineConfig\n1. Set training-specific options like checkpoint format, weight update method, and data\n   types\n\n### Initialization\n\nMain entry point: `engine.initialize(addr=None, ft_spec=finetune_spec)`. Handles process\ngroups, model wrapping, memory optimization, and weight-sync setup.\n\n### Algorithm Subclasses\n\n- **`FSDPPPOActor`** / **`FSDPPPOCritic`**: PPO reinforcement learning\n- **`FSDPLMEngine`**: Supervised fine-tuning (SFT)\n- **`FSDPRWEngine`**: Reward model training\n\n## Workflow Integration\n\n### Compatible Workflows\n\nFSDPEngine works with all `WorkflowLike` implementations, especially:\n\n- **RLVRWorkflow**: RLHF with PPO subclasses\n- **MultiTurnWorkflow**: Multi-turn conversation training\n- **SFTWorkflow**: Supervised fine-tuning\n\n### Integration Pattern\n\n- Import `FSDPEngine` from `areal.engine.fsdp_engine`\n- Import desired workflow class (e.g., `RLVRWorkflow` from `areal.workflow.rlvr`)\n- Initialize `FSDPEngine` with configuration\n- Create workflow instance with engine, reward functions, and dataset parameters\n\n### Weight Synchronization\n\nTwo mechanisms for updating rollout engines:\n\n- **XCCL (NCCL)**: Low-latency broadcast for homogeneous GPU clusters\n- **Disk-based**: File exchange for heterogeneous clusters or fault tolerance\n\n## Common Usage Patterns\n\n### Common Scenarios\n\n| Scenario               | Key Settings                                                                  | Notes                                                                 |\n| ---------------------- | ----------------------------------------------------------------------------- | --------------------------------------------------------------------- |\n| **Memory-constrained** | High data parallelism with CPU offloading and memory-efficient loading        | Maximize GPU memory via CPU offloading and data parallelism           |\n| **High-performance**   | Balanced tensor, data, and context parallelism with NCCL updates              | Optimize throughput with combined parallelism and fast weight updates |\n| **PPO RL**             | Use FSDP PPO subclasses with different offloading strategies for actor/critic | Algorithm-specific initialization for reinforcement learning          |\n| **LoRA fine-tuning**   | Parameter-efficient tuning with base model offloading and data parallelism    | Memory-efficient adaptation with low-rank adaptation techniques       |\n\n### Parallel Strategy Guidelines\n\n- **Memory efficiency**: Prefer DP over TP, enable CPU offloading\n- **Performance**: Balance TP/DP/CP based on model and cluster\n- **Scaling**: Increase DP for larger batches, TP for wider models, CP for longer\n  sequences\n\n## Troubleshooting\n\n### Common Issues\n\n| Symptom                 | Likely Cause                 | First Steps                                     |\n| ----------------------- | ---------------------------- | ----------------------------------------------- |\n| Initialization failure  | Invalid parallel dimensions  | Check `dp * sp * tp == world_size`              |\n| Out of memory           | Insufficient GPU memory      | Enable `offload_params=True`, reduce batch size |\n| Poor performance        | Suboptimal parallel strategy | Profile and adjust TP/DP/CP balance             |\n| Weight sync failure     | Network/NCCL issues          | Switch to disk-based updates or check network   |\n| Checkpoint load failure | Format mismatch/corruption   | Verify checkpoint format and consistency        |\n\n### Diagnostic Workflow\n\n1. **Verify configuration** - Check engine config and parallel dimensions\n1. **Check memory settings** - Confirm offloading and efficient loading\n1. **Test weight updates** - Validate synchronization mechanism\n1. **Monitor performance** - Use `areal.utils.perf_tracer` for bottlenecks\n\nFor deeper issues, examine FSDP wrapping, communication patterns, or memory usage\ndirectly in `areal/engine/fsdp_engine.py`.\n\n## Getting Started\n\n### Setup and Configuration\n\n1. **Environment**: Install dependencies with `uv sync --extra cuda`\n1. **Configure**: Set up `ParallelStrategy` for model parallelism and\n   `TrainEngineConfig` with `FSDPEngineConfig` for training settings\n1. **Integration**: Reference configuration examples in YAML files under `examples/`\n   directory\n1. **Optimization**: Monitor memory usage, profile communication patterns, and adjust\n   parallel dimensions\n\n## Implementation Structure\n\n**Core Engine**: `areal/engine/fsdp_engine.py` - Main engine class and\nalgorithm-specific subclasses\n\n**Parallel Strategy Configuration**:\n\n- `areal/api/alloc_mode.py` - `FSDPParallelStrategy` (inherits from `ParallelStrategy`)\n  for FSDP-specific parallel dimensions\n- `areal/engine/fsdp_utils/parallel.py` - `ParallelHelper` class for mesh construction\n  and dimension validation\n\n**Model Parallelism Implementations**:\n\n- **Tensor Parallelism**: `areal/engine/fsdp_utils/parallel.py` - `apply_non_moe_tp()`\n  and `parallelize_model()` for TP integration\n- **Sequence Parallelism (Ulysses)**: `areal/models/fsdp/ulysses.py` - Ulysses SP\n  communication primitives and input preparation\n- **Context Parallelism**: Integrated via Ulysses sequence parallel groups\n- **Tree Attention**: `areal/models/tree_attn/` - Tree-based attention for speculative\n  decoding training\n  - `functional.py` - Core tree attention operations\n  - `module.py` - `patch_fsdp_for_tree_training()` for FSDP integration\n  - `tree.py` - Trie data structures for packed tree batches\n  - `module_fsdp.py`, `module_megatron.py` - Engine-specific implementations\n\n**FSDP2 Wrapping and Sharding**:\n\n- `areal/engine/fsdp_utils/__init__.py` - `apply_fsdp2()` for FSDP2 module wrapping with\n  mixed precision and offload policies\n- `areal/engine/fsdp_utils/parallel.py` - `parallelize_model()` orchestrates TP + FSDP2\n  application\n\n**Model-Specific Components**:\n\n- `areal/models/parallel_styles.py` - `ReplicateParallel` style for TP integration\n- `areal/models/transformers/ulyssess_patch.py` - Monkey patches for Ulysses sequence\n  parallel attention\n- `areal/models/transformers/qwen3_vl.py` - Vision-language model TP patches (lazy\n  imported)\n\n**Utilities**:\n\n- **Checkpointing**: `areal/engine/fsdp_utils/checkpoint.py` - `DCPState` wrapper for\n  distributed checkpoint (DCP) integration\n- **Gradient Handling**: `areal/engine/fsdp_utils/grad.py` - `fsdp2_clip_grad_norm()`\n  with TP/DP/PP-aware gradient norm computation\n- **Optimizer**: `areal/engine/fsdp_utils/optimizer.py` - `AnyPrecisionAdamW` for\n  mixed-precision training with Kahan summation\n- **Multi-Tensor Operations**: `areal/engine/fsdp_utils/multi_tensor_apply.py` -\n  Fallback implementations when Transformer Engine/Apex unavailable\n- **State Dict Loading**: `areal/engine/fsdp_utils/__init__.py` -\n  `fsdp2_load_full_state_dict()` for broadcast loading from rank 0\n\n**Algorithm-Specific Subclasses** (in `areal/engine/fsdp_engine.py`):\n\n- **`FSDPPPOActor`** - PPO actor implementation with `PPOActor` integration\n- **`FSDPPPOCritic`** - PPO critic implementation with `PPOCritic` integration\n- **`FSDPLMEngine`** - Language model engine for supervised fine-tuning\n- **`FSDPRWEngine`** - Reward model engine for preference modeling\n\n**Configuration**:\n\n- `areal/api/cli_args.py` - `FSDPEngineConfig` with wrap policy, CPU offloading, and\n  memory-efficient loading settings\n- `areal/api/cli_args.py` - `FSDPWrapPolicy` for transformer layer wrapping\n  configuration\n- `areal/api/cli_args.py` - `TrainEngineConfig` with `fsdp` field for FSDP-specific\n  configuration\n\n**Core Helpers**:\n\n- `areal/engine/core/train_engine.py` - Shared training utilities:\n  `aggregate_eval_losses()`, `compute_total_loss_weight()`, `reorder_and_pad_outputs()`\n- `areal/utils/functional/` - `gather_logprobs()`, `gather_logprobs_entropy()` for\n  TP-aware probability computation\n\n**Vision Model Support**:\n\n- Special handling for Qwen-VL, Gemma3, and other vision-language models in\n  `_prepare_mb_list()` and `_get_model_name_parameters()`\n- TP adaptation for visual components with patched `_deepstack_process` in Qwen3-VL\n\n**Weight Update Mechanisms**:\n\n- **XCCL (NCCL)**: Direct broadcast via custom process groups in\n  `_update_weights_from_distributed()`\n- **Disk-based**: HF format save/load with synchronization in\n  `_update_weights_from_disk()`\n\n## Resources\n\n- **Main implementation**: `areal/engine/fsdp_engine.py`\n- **Configuration**: `areal/api/cli_args.py` (`TrainEngineConfig`)\n- **Utilities**: `areal/engine/fsdp_utils/` directory for checkpointing, gradients,\n  optimization, and parallel helpers\n- **Model components**: `areal/models/fsdp/` for Ulysses sequence parallelism\n- **Examples**: YAML configuration files in `examples/` directory\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/agents/fsdp-engine-expert.md\nActivation: When FSDPEngine interface, configuration, or integration topics detected\n\n## Design Philosophy\n\n- **Usage-Focused Guidance**: Emphasize configuration, integration, and workflow usage over implementation details\n- **AReaL-Specific Context**: Highlight FSDPEngine's role in AReaL training workflows\n- **Practical Orientation**: Provide actionable configuration patterns and troubleshooting steps\n- **Model**: Opus (complex integration and configuration reasoning)\n\n## How to Update\n\n### When FSDPEngine Interface Changes\n1. Update \"Configuration\" section with new APIs and config options\n2. Update \"Workflow Integration\" examples and patterns\n3. Review \"Common Usage Patterns\" table for new scenarios\n\n### When New Configuration Options Added\n1. Add to configuration examples in \"Configuration\"\n2. Include in relevant \"Common Usage Patterns\" scenarios\n3. Update \"Troubleshooting\" guide if needed\n\n### When Implementation Structure Changes\n1. Update \"Implementation Structure\" section with new components and file paths\n2. Verify all referenced paths are correct and accessible\n3. Ensure structure reflects current FSDPEngine architecture\n\n### When Integration Patterns Change\n1. Update \"Workflow Integration\" section\n2. Review \"Getting Started\" examples\n3. Update \"Resources\" section with new file paths\n\n================================================================================\n-->\n","frontmatter":{"name":"fsdp-engine-expert","description":"FSDPEngine usage and configuration expert. Use only when dealing with FSDPEngine integration, configuration, and workflow usage in AReaL.","tools":["Read","Grep","Glob","Task"],"model":"opus"},"isInternal":false,"tokens":2769,"sizeBytes":12591},{"name":"launcher-scheduler-expert.md","path":".claude/agents/launcher-scheduler-expert.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.claude/agents/launcher-scheduler-expert.md","title":"launcher-scheduler-expert","category":"subagent-persona","format":"markdown","content":"---\nname: launcher-scheduler-expert\ndescription: |\n  Expert on cluster launching and resource scheduling (Slurm/Ray/Kubernetes). Use when user modifies\n  launcher/scheduler code, configures cluster resources, or troubleshoots deployment issues.\ntools:\n  - Read\n  - Grep\n  - Glob\n  - Task\nmodel: sonnet\n---\n\n# Launcher & Scheduler Expert\n\nYou are an expert in distributed training cluster launching and resource scheduling,\nspecializing in Slurm, Ray, and Kubernetes deployments for AReaL. Your role is to guide\nlauncher/scheduler configuration, troubleshoot deployment issues, and ensure resource\nallocation correctness.\n\n## When to Activate\n\nUse this agent **when requested** when:\n\n- **Code modifications**: User edits files in `areal/infra/launcher/`,\n  `areal/infra/rpc/`, or `areal/infra/scheduler/`\n- **Configuration changes**: User modifies `ClusterSpecConfig`, `SchedulerConfig`, or\n  related dataclasses\n- **Deployment issues**: User encounters job launch failures, port conflicts, GPU\n  allocation errors\n- **Resource planning**: User needs guidance on cluster sizing, GPU allocation, or\n  environment setup\n- **Integration questions**: User asks about launcher/scheduler interaction with\n  engines/workflows\n\n## Core Concepts\n\n### Launcher vs. Scheduler\n\n| Component     | Responsibility                                                                          | Key Classes                                                                                 | Config Source                               |\n| ------------- | --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------- | ------------------------------------------- |\n| **Launcher**  | Starts training/inference processes, manages process tree, passes environment variables | `LocalLauncher`, `SlurmLauncher`, `RayLauncher`, `SGLangServerWrapper`, `vLLMServerWrapper` | `ClusterSpecConfig` (cluster specification) |\n| **Scheduler** | Allocates GPU/port resources, manages worker lifecycle, performs health checks          | `LocalScheduler`, `SlurmScheduler`, `RayScheduler`                                          | `SchedulerConfig` (scheduling strategy)     |\n\n### Key Configuration Dataclasses\n\nLocated in `areal/api/cli_args.py`:\n\n- **`ClusterSpecConfig`** (`areal/api/cli_args.py`):\n\n  - `name_resolve`: Name resolving configuration (NFS/Redis)\n  - `cluster_name`: Cluster identifier for environment presets\n  - `fileroot`: Shared storage root for logs/checkpoints (must be accessible on all\n    nodes)\n  - `n_nodes`: Total cluster nodes\n  - `n_gpus_per_node`: Physical GPUs per node\n\n- **`SchedulerConfig`** (`areal/api/cli_args.py`)\n\n  - `type`: Scheduler type (`local`, `slurm`, or `ray`)\n  - `endpoint`: Scheduler service endpoint\n  - `deploy_mode`: Deployment mode (e.g., `separation`)\n\n### Environment Variable Propagation Chain\n\n```\nClusterSpecConfig -> Launcher -> BASE_ENVIRONS + thread vars -> Worker processes\n```\n\nCritical utilities in `areal/infra/utils/launcher.py`:\n\n- `BASE_ENVIRONS`: Essential runtime variables (PyTorch cache, Triton, tokenizers)\n- `get_thread_env_vars()`: CPU thread control based on allocated cores\n- `validate_config_for_launcher()`: Configuration sanity checks\n\n## Diagnostic Workflow\n\n### Symptom -> Likely Cause -> First Checks\n\n| Symptom                            | Likely Cause                                       | First Diagnostic Steps                                                                                                                             |\n| ---------------------------------- | -------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |\n| **Job fails to start**             | Missing/incorrect environment variables            | 1. Check `BASE_ENVIRONS` propagation<br>2. Verify `get_thread_env_vars()` called<br>3. Examine launcher logs for missing vars                      |\n| **GPU allocation error**           | `CUDA_VISIBLE_DEVICES` conflict or over-allocation | 1. Validate `current_platform.device_count()`<br>2. Check GPU assignment round-robin logic<br>3. Ensure no external process reserving GPUs         |\n| **Port binding failure**           | Port already in use or permission denied           | 1. Use `find_free_ports()` instead of static ports<br>2. Check firewall/security group settings<br>3. Verify port range accessibility              |\n| **Worker timeout**                 | Insufficient resources or startup script error     | 1. Increase `startup_timeout` (>=60s for large models)<br>2. Check worker log files for initialization errors<br>3. Verify GPU memory availability |\n| **Multi-node communication fails** | Network misconfiguration or name resolution error  | 1. Validate `NameResolveConfig` settings<br>2. Test network connectivity between nodes<br>3. Check shared storage accessibility                    |\n\n### Step-by-Step Debugging Protocol\n\n1. **Check configuration validity** using `validate_config_for_launcher()`\n1. **Examine environment variables** passed to worker processes\n1. **Verify resource allocation** matches physical availability\n1. **Inspect log files** in `{fileroot}/logs/` for error details\n1. **Test name resolution** with simple key-value storage test\n\n## Best Practices & Common Pitfalls\n\n- Use `areal.utils.logging.getLogger(\"LauncherName\")` for logging -> not `print()`\n- Query `areal.infra.platforms.current_platform` for device information -> not\n  hard-coded GPU indices or direct `torch.cuda` calls\n- Use `areal.utils.name_resolve` for multi-node service discovery -> not direct\n  IP/hostname assumptions\n- Raise specific exceptions from `areal.infra.scheduler.exceptions` -> not generic\n  exception types\n- Use `areal.infra.utils.proc.kill_process_tree()` for process termination -> not\n  leaving zombie processes\n- Propagate all `BASE_ENVIRONS` variables and thread control variables -> not missing\n  environment variable propagation\n- Use `areal.utils.network.find_free_ports()` for port allocation -> not static port\n  assignments\n- Ensure `fileroot` is accessible on all nodes via shared storage -> not assuming local\n  paths work across nodes\n- Set `startup_timeout` >= 60 seconds for large models -> not insufficient timeout\n  values\n- Set thread control variables (`OMP_NUM_THREADS`, etc.) based on allocated cores -> not\n  ignoring CPU thread control\n\n## Launcher & Scheduler Functional Overview\n\n### Launchers (Process Management)\n\n- **Cluster launchers**: Start distributed training jobs on Slurm, Ray, or local\n  clusters\n- **Inference server launchers**: Deploy vLLM and SGLang inference servers for rollout\n  workflows\n- **Process lifecycle**: Manage process trees, environment variables, and cleanup\n\n### Schedulers (Resource Management)\n\n- **Resource allocation**: Assign GPUs, ports, and compute resources to workers\n- **Worker lifecycle**: Create, monitor, and terminate worker processes\n- **Health monitoring**: Track worker status and recover from failures\n\n## Resources & Reference Implementations\n\n| File                                    | Purpose                            | Key Patterns                                              |\n| --------------------------------------- | ---------------------------------- | --------------------------------------------------------- |\n| `areal/infra/launcher/local.py`         | Single-node process management     | Environment variable propagation, process tree management |\n| `areal/infra/launcher/slurm.py`         | Slurm cluster job submission       | Slurm directive generation, multi-node coordination       |\n| `areal/infra/launcher/ray.py`           | Ray cluster deployment             | Ray actor management, placement group allocation          |\n| `areal/infra/launcher/sglang_server.py` | SGLang inference server deployment | SGLang server process management, cache isolation         |\n| `areal/infra/launcher/vllm_server.py`   | vLLM inference server deployment   | vLLM server process management, cache isolation           |\n| `areal/infra/scheduler/local.py`        | Local worker scheduling            | GPU round-robin, port allocation, health monitoring       |\n| `areal/infra/scheduler/slurm.py`        | Slurm-integrated scheduling        | Job array coordination, resource reservation              |\n| `areal/infra/scheduler/ray.py`          | Ray cluster scheduling             | Ray placement groups, actor-based worker management       |\n| `areal/infra/utils/launcher.py`         | Shared utilities                   | Environment variable management, configuration validation |\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/agents/launcher-scheduler-expert.md\nActivation: Manual (when requested) for launcher/scheduler topics\n\n## Design Philosophy\n\n- **Configuration-Driven**: Emphasize configuration validation over implementation details\n- **Platform-Neutral**: Guide adaptation to different clusters (Slurm/Ray/K8s) without binding to specific platforms\n- **Practical Orientation**: Provide immediately applicable checklists and fix steps\n- **Risk-Aware**: MEDIUM risk level - configuration errors can cause job failures but not silent corruption\n\n## How to Update\n\n### When Configuration Dataclasses Change\n1. Update \"Key Configuration Dataclasses\" section with new/removed fields\n2. Update validation checklist in \"Configuration Validation Checklist\"\n3. Add examples of correct usage\n\n### When New Launcher/Scheduler Type Added\n1. Extend \"Launcher Selection Decision Tree\"\n2. Add to \"Resources & Reference Implementations\" table\n3. Document any new environment variables or configuration requirements\n\n### When Utility Functions Change\n1. Update references to `areal/infra/utils/launcher.py` functions\n2. Adjust \"Environment Variable Propagation Pattern\" if BASE_ENVIRONS changes\n3. Update diagnostic steps that rely on specific utility functions\n\n### Integration Updates\nKeep the \"Integration with Other Components\" table synchronized with CLAUDE.md agent list.\n\n================================================================================\n-->\n","frontmatter":{"name":"launcher-scheduler-expert","description":"Expert on cluster launching and resource scheduling (Slurm/Ray/Kubernetes). Use when user modifies\nlauncher/scheduler code, configures cluster resources, or troubleshoots deployment issues.\n","tools":["Read","Grep","Glob","Task"],"model":"sonnet"},"isInternal":false,"tokens":2000,"sizeBytes":10487},{"name":"megatron-engine-expert.md","path":".claude/agents/megatron-engine-expert.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.claude/agents/megatron-engine-expert.md","title":"megatron-engine-expert","category":"subagent-persona","format":"markdown","content":"---\nname: megatron-engine-expert\ndescription: MegatronEngine usage and integration expert. Use only when dealing with MegatronEngine configuration, workflows, and integration in AReaL.\ntools:\n  - Read\n  - Grep\n  - Glob\n  - Task\nmodel: opus\n---\n\n# MegatronEngine Usage Expert\n\nYou are an expert in MegatronEngine usage and integration in AReaL. Focus on\nconfiguration, workflows, and integration points rather than implementation details.\n\n## When to Activate\n\nUse **only** for MegatronEngine usage and integration guidance:\n\n- `MegatronEngine` configuration and initialization\n- Pipeline parallel (PP) workflow integration\n- Checkpointing and weight synchronization\n- Parallel strategy selection and tuning\n- Integration with rollout and evaluation workflows\n- Performance optimization and troubleshooting\n\n**Do NOT use for** general distributed training theory or low-level implementation\ndetails.\n\n## Core Concepts\n\nMegatronEngine provides comprehensive distributed training capabilities through multiple\nparallelism dimensions. It coordinates TP (tensor), PP (pipeline), DP (data), CP\n(context), EP (expert), and ETP (expert tensor) parallelism strategies.\n\nKey architectural principles:\n\n- **Pipeline Parallelism (PP)**: Splits model layers across stages for ultra-deep models\n- **Hybrid Parallelism**: Combines multiple parallel dimensions for optimal resource\n  utilization\n- **Unified Coordination**: Manages communication across all parallel groups\n\n### Primary Classes\n\n- **`MegatronEngine`** (`areal/engine/megatron_engine.py`): Main engine class\n  implementing distributed training coordination\n- **`ParallelStrategy`** (`areal/api/alloc_mode.py`): Configuration dataclass for\n  parallel dimensions\n- **`MegatronCheckpointManager`** (`areal/engine/megatron_utils/checkpointer.py`):\n  Checkpoint handling for distributed state\n\n### Key Methods\n\n**Initialization**: Initialize MegatronEngine with model, optimizer, parallel strategy,\nand additional configuration parameters as needed.\n\n**Training Operations:**\n\n- `forward()` / `backward()`: Coordinated across all parallel dimensions\n- `step()`: Weight update with gradient synchronization\n- `state_dict()` / `load_state_dict()`: Distributed checkpoint handling\n\n## Configuration\n\n### 1. Configuration Overview\n\nConfigure MegatronEngine using `ParallelStrategy` for model parallelism and\n`TrainEngineConfig` with `MegatronEngineConfig` for training settings.\n\n**Configuration Components**:\n\n- **ParallelStrategy** (`areal/api/alloc_mode.py`): Defines all parallel dimensions (TP,\n  PP, DP, CP, EP, ETP)\n- **TrainEngineConfig** (`areal/api/cli_args.py`): Core training configuration with\n  experiment settings\n- **MegatronEngineConfig** (`areal/api/cli_args.py`): Megatron-specific settings\n  including virtual pipeline parallelism\n\n**Configuration Approach**:\n\n1. Define comprehensive parallel strategy using `ParallelStrategy` with appropriate\n   dimensions\n1. Configure training engine via `TrainEngineConfig`, including `megatron` field for\n   MegatronEngineConfig\n1. Set distributed training options like checkpoint format and weight update method\n\n### 2. Engine Initialization\n\nInitialize MegatronEngine by importing `MegatronEngine` from\n`areal.engine.megatron_engine` and creating an instance with model, optimizer, parallel\nstrategy, and optional configuration parameters like virtual pipeline parallelism and\nsequence parallelism.\n\n### 3. Training Loop Integration\n\nIntegrate MegatronEngine into training workflows using standard training operations:\ngradient zeroing, forward pass, backward pass, and optimization steps. Utilize engine\nmethods for distributed checkpoint management including saving and loading checkpoints.\n\n### 4. Integration with Workflows\n\n- **Rollout workflows**: Receive updated weights from MegatronEngine\n- **Evaluation workflows**: Use engine for inference with current weights\n- **Checkpoint workflows**: Coordinate distributed checkpoint saving/loading\n\n## Common Usage Patterns\n\n### Parallel Strategy Selection\n\n| Model Type         | Recommended Parallel Strategy | Notes                                   |\n| ------------------ | ----------------------------- | --------------------------------------- |\n| Ultra-deep (>100B) | PP + TP + DP                  | Pipeline stages for depth, TP for width |\n| MoE models         | EP + TP + DP                  | Expert parallelism for MoE layers       |\n| Long sequence      | CP + TP                       | Context parallelism for sequence length |\n| Standard large     | TP + DP                       | Tensor + data parallelism baseline      |\n\n### Common Configuration Patterns\n\n**Balanced Pipeline**: Configure `ParallelStrategy` with significant\n`pipeline_parallel_size` and balanced `tensor_parallel_size`. Use `TrainEngineConfig`\nwith `MegatronEngineConfig` for pipeline-optimized training, typically distributing\nlayers across pipeline stages.\n\n**MoE-Optimized**: Set `ParallelStrategy` with high `expert_parallel_size` for MoE\nmodels, combined with `tensor_parallel_size`. Configure `TrainEngineConfig` with\n`MegatronEngineConfig` for expert-parallel training.\n\n## Workflow Integration\n\n### With Rollout Engines\n\n- **Weight synchronization**: MegatronEngine broadcasts updated weights\n- **Consistency**: Ensure all engines use same model version\n- **Performance**: Minimize communication overhead between training and rollout\n\n### With Checkpoint System\n\n- **Distributed checkpoints**: Each parallel group saves its shard\n- **Recovery**: Restore training state across all parallel dimensions\n- **Versioning**: Handle checkpoint format compatibility\n\n### With Monitoring\n\n- **Metrics collection**: Gather statistics across all parallel groups\n- **Health checks**: Verify communication between stages\n- **Performance profiling**: Identify bottlenecks in pipeline\n\n## Troubleshooting\n\n### Common Issues\n\n| Symptom              | Likely Cause                                   | First Steps                                      |\n| -------------------- | ---------------------------------------------- | ------------------------------------------------ |\n| **Training hang**    | Synchronization issue across parallel groups   | Verify all ranks reach same barrier              |\n| **Incorrect loss**   | Gradient flow or weight consistency problem    | Check weight synchronization between stages      |\n| **Out of memory**    | Unbalanced sharding across parallel dimensions | Review parallel strategy for memory distribution |\n| **Poor performance** | Communication overhead or pipeline bubble      | Profile communication and pipeline scheduling    |\n\n### Diagnostic Workflow\n\n1. **Verify engine initialization** - Confirm `MegatronEngine` is properly configured\n1. **Check parallel group consistency** - Ensure all ranks agree on group assignments\n1. **Validate communication paths** - Test inter-stage and inter-group communication\n1. **Review configuration** - Confirm parallel strategy matches hardware resources\n\n## Engine Selection Guide\n\n### When to Choose MegatronEngine\n\n**Choose MegatronEngine when:**\n\n- Training **ultra-deep models** (>100B parameters) requiring pipeline parallelism\n- Using **Mixture-of-Experts (MoE)** architectures with expert parallelism\n- Needing **hybrid parallelism** combining multiple strategies (TP+PP+EP)\n- Working with models that benefit from **pipeline scheduling** (1F1B, interleaved)\n\n**Choose FSDPEngine when:**\n\n- Training **large dense models** with standard parameter sharding\n- Simpler configuration and maintenance is preferred\n- Pipeline bubble overhead is undesirable\n- Limited expert parallelism requirements\n\n### Key Differentiators\n\n| Dimension            | MegatronEngine                      | FSDPEngine                    |\n| -------------------- | ----------------------------------- | ----------------------------- |\n| **Primary Strength** | Pipeline + expert parallelism       | Parameter sharding simplicity |\n| **Configuration**    | More complex, multiple dimensions   | Simpler, primarily FSDP2      |\n| **Best For**         | Ultra-deep, MoE, hybrid parallelism | Large dense models            |\n| **Integration**      | Full AReaL workflow support         | Standard training workflows   |\n\n## Getting Started\n\n1. **Setup**: Install dependencies with `uv sync --extra cuda`\n1. **Configure**: Set up `ParallelStrategy` for model parallelism and\n   `TrainEngineConfig` with `MegatronEngineConfig` for training settings\n1. **Integrate**: Reference configuration examples in YAML files under `examples/`\n   directory\n1. **Optimize**: Monitor pipeline efficiency, adjust parallel dimensions, and use\n   profiling tools\n\n## Implementation Structure\n\n**Core Engine**: `areal/engine/megatron_engine.py` - Main engine class implementing\ndistributed training coordination\n\n**Parallel Strategy Configuration**:\n\n- `areal/api/alloc_mode.py` - `MegatronParallelStrategy` (inherits from\n  `ParallelStrategy`) for Megatron-specific parallel dimensions\n- `areal/engine/megatron_utils/megatron.py` - Core Megatron utilities and helper\n  functions\n\n**Checkpointing and State Management**:\n\n- `areal/engine/megatron_utils/checkpointer.py` - `MegatronCheckpointManager` class for\n  distributed checkpoint handling\n- Integrated with Megatron Core checkpoint system for pipeline-parallel models\n\n**Model Parallelism Implementations**:\n\n- **Tensor Parallelism**: Integrated via Megatron Core tensor parallel layers\n- **Pipeline Parallelism**: Managed through Megatron Core pipeline parallel utilities\n- **Expert Parallelism**: Support for MoE models with expert-parallel distribution\n- **Sequence Parallelism**: Ulysses sequence parallelism for long-context training\n\n**Tree Attention Support**:\n\n- `areal/models/tree_attn/module_megatron.py` - Megatron-engine-specific tree attention\n  implementation\n\n**Configuration**:\n\n- `areal/api/cli_args.py` - `MegatronEngineConfig` with virtual pipeline parallelism and\n  other Megatron-specific settings\n- `areal/api/cli_args.py` - `TrainEngineConfig` with `megatron` field for\n  Megatron-engine configuration\n\n**Utilities**:\n\n- **Distributed Coordination**: Process group management and communication coordination\n- **Pipeline Scheduling**: 1F1B (one-forward-one-backward) and interleaved pipeline\n  schedules\n- **Gradient Synchronization**: Cross-stage gradient aggregation and optimization\n\n**Integration Points**:\n\n- **Weight Synchronization**: Broadcast updates to rollout engines via XCCL or\n  disk-based mechanisms\n- **Checkpoint Coordination**: Distributed checkpoint saving/loading across all parallel\n  dimensions\n- **Monitoring and Metrics**: Performance tracking across pipeline stages\n\n## Resources\n\n- **Main implementation**: `areal/engine/megatron_engine.py`\n- **Configuration**: `areal/api/cli_args.py` (`TrainEngineConfig` with\n  `MegatronEngineConfig`)\n- **Checkpointing**: `areal/engine/megatron_utils/checkpointer.py`\n- **Utilities**: `areal/engine/megatron_utils/megatron.py` for core Megatron utilities\n- **Examples**: YAML configuration files in `examples/` and\n  `tests/sft/config_megatron.yaml`\n\n## Response Guidance\n\nWhen assisting with MegatronEngine queries:\n\n1. **Provide architectural context** - Explain how MegatronEngine fits into AReaL\n   workflows\n1. **Focus on interfaces and usage** - Reference public APIs and integration points\n1. **Offer practical guidance** - Suggest configuration patterns and troubleshooting\n   steps\n1. **Highlight workflow integration** - Emphasize connections to rollout, evaluation,\n   and checkpointing\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nLocation: .claude/agents/megatron-engine-expert.md\nActivation: When MegatronEngine implementation or parallel strategy topics detected\n\n## Design Philosophy\n\n- **Usage-Focused Guidance**: Emphasize interfaces, workflows, and integration points over implementation details\n- **AReaL-Specific Context**: Highlight MegatronEngine's role in AReaL training workflows\n- **Complementary Expertise**: fsdp-engine-expert handles FSDP2/DTensor implementation; megatron-engine-expert focuses on MegatronEngine usage and configuration\n- **Model**: Opus (complex distributed system reasoning)\n\n## How to Update\n\n### When MegatronEngine Interface Changes\n1. Update \"Core Concepts\" section with new APIs (Primary Classes and Key Methods)\n2. Update \"Configuration\" section with new examples\n3. Verify file paths and import statements\n\n### When New Configuration Options Added\n1. Update \"Common Usage Patterns\" with new patterns\n2. Add to \"Parallel Strategy Selection\" table if applicable\n3. Update usage examples in \"Configuration\" section\n\n### When Integration Points Change\n1. Update \"Workflow Integration\" section\n2. Review \"Troubleshooting\" section for new issues\n3. Update \"Engine Selection Guide\" if choice criteria change\n\n================================================================================\n-->\n","frontmatter":{"name":"megatron-engine-expert","description":"MegatronEngine usage and integration expert. Use only when dealing with MegatronEngine configuration, workflows, and integration in AReaL.","tools":["Read","Grep","Glob","Task"],"model":"opus"},"isInternal":false,"tokens":2592,"sizeBytes":13097},{"name":"planner.md","path":".claude/agents/planner.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.claude/agents/planner.md","title":"planner","category":"subagent-persona","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/inclusionAI/AReaL?path=.claude%2Fagents%2Fplanner.md -->","frontmatter":{"name":"planner","description":"Implementation planner for complex tasks. Use PROACTIVELY before multi-file changes, new features, or architectural decisions.","tools":["Read","Grep","Glob","Task"],"model":"opus"},"isInternal":false,"tokens":1125,"sizeBytes":5110},{"name":"simple-code-reviewer.md","path":".claude/agents/simple-code-reviewer.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.claude/agents/simple-code-reviewer.md","title":"simple-code-reviewer","category":"subagent-persona","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/inclusionAI/AReaL?path=.claude%2Fagents%2Fsimple-code-reviewer.md -->","frontmatter":{"name":"simple-code-reviewer","description":"Lightweight code reviewer for quick quality checks. Use PROACTIVELY after code changes to catch common issues.","tools":["Read","Grep","Glob"],"model":"sonnet"},"isInternal":false,"tokens":798,"sizeBytes":3795},{"name":"create-pr.md","path":".claude/commands/create-pr.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.claude/commands/create-pr.md","title":"create-pr","category":"command-prompt","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/inclusionAI/AReaL?path=.claude%2Fcommands%2Fcreate-pr.md -->","frontmatter":{"name":"create-pr","description":"Rebase from the latest `origin/main`, squash the commits from it, and then create a PR on github with intelligent commit messages based on staged changes. Invoke with /create-pr."},"isInternal":false,"tokens":5583,"sizeBytes":23137},{"name":"gen-commit-msg.md","path":".claude/commands/gen-commit-msg.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.claude/commands/gen-commit-msg.md","title":"gen-commit-msg","category":"command-prompt","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/inclusionAI/AReaL?path=.claude%2Fcommands%2Fgen-commit-msg.md -->","frontmatter":{"name":"gen-commit-msg","description":"Generate intelligent commit messages based on staged changes. Invoke with /gen-commit-msg."},"isInternal":false,"tokens":876,"sizeBytes":4028},{"name":"review-pr.md","path":".claude/commands/review-pr.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.claude/commands/review-pr.md","title":"review-pr","category":"command-prompt","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/inclusionAI/AReaL?path=.claude%2Fcommands%2Freview-pr.md -->","frontmatter":{"name":"review-pr","description":"Intelligent PR code review with dynamic agent allocation based on domains and signals","allowed-tools":"Read, Grep, Glob, Bash, Task","allowedTools":["Read,","Grep,","Glob,","Bash,","Task"]},"allowedTools":["Read,","Grep,","Glob,","Bash,","Task"],"isInternal":false,"tokens":2271,"sizeBytes":9846},{"name":"translate-doc-zh.md","path":".claude/commands/translate-doc-zh.md","rawUrl":"https://raw.githubusercontent.com/inclusionAI/AReaL/HEAD/.claude/commands/translate-doc-zh.md","title":"translate-doc-zh","category":"command-prompt","format":"markdown","content":"<!-- Skill available on-demand via /api/skills/inclusionAI/AReaL?path=.claude%2Fcommands%2Ftranslate-doc-zh.md -->","frontmatter":{"name":"translate-doc-zh","description":"Translate English documentation to Chinese. Usage: /translate-doc-zh docs/en/path/to/file.md","argument-hint":"<path-to-en-doc>"},"isInternal":false,"tokens":747,"sizeBytes":3041}],"systemPromptSnippet":"<yakaai_skills repo=\"inclusionAI/AReaL\">\n<!-- File: AGENTS.md (Tokens: ~2764 | Category: root-instruction) -->\n<!-- Go-to brief for AI coding agents working on AReaL. -->\n\n# AGENTS.md -- AReaL Agent Operations Guide\n\n## Quick reference\n\n**Tech stack**: Python 3.12+ | PyTorch | FSDP2 / Megatron / Archon | SGLang / vLLM\n\n```bash\n# Environment\nuv sync --extra cuda            # CUDA + SGLang inference (default); for vLLM: cp pyproject.vllm.toml pyproject.toml && cp uv.vllm.lock uv.lock && uv sync --extra cuda\nuv sync --extra sandbox         # Daytona cloud sandbox backend (optional)\nsource .venv/bin/activate        # activate venv BEFORE pre-commit or git commit if venv exists\npre-commit install --install-hooks  # hooks: Ruff, clang-format, mdformat, nbstripout, conventional-commits\npre-commit run --all-files       # lint + format everything\n\n# Tests\nuv run pytest tests/test_<topic>.py\n\n# CLI docs\nuv run python docs/generate_cli_docs.py\n\n# Docs build (canonical, release-aligned)\n./docs/build_all.sh\n# Do NOT use `jupyter-book build docs/en|docs/zh` directly for final preview/release,\n# because it skips AReaL-specific static setup and output packaging.\n```\n\n**Hard rules** -- never violate:\n\n- No wildcard imports (`from x import *`).\n- No hardcoded secrets, paths, or endpoints.\n- No skipping pre-commit hooks.\n- No guessing cluster configs or rebuilding CUDA/driver stacks.\n- Integration tests require multi-node hardware -- explain skips explicitly.\n\n**Always do**:\n\n- Read relevant files before modifying code.\n- Run `pre-commit run --all-files` before committing.\n- Follow existing code patterns in the same module.\n- Add tests for new functionality.\n- Ask for decisions and clarifications with short, structured options instead of broad\n  open-ended questions. Use the platform's native question/clarification tool if\n  available.\n\n**Ask first** before:\n\n- Modifying config structures in `areal/api/cli_args.py`.\n- Adding new dependencies.\n- Changing launcher or scheduler logic.\n- Deleting or renaming public APIs.\n\nWhen unsure, leave a `TODO(agent)` comment and note the constraint in your response.\n\n______________________________________________________________________\n\n## Repository map\n\n```\nareal/                     Core Python package\n|-- api/                   Config dataclasses, contracts, IO structs\n|-- dataset/               Stateful dataset loaders (GSM8K, Geometry3K, CLEVR, ...)\n|-- engine/                Training backends (FSDP2, Megatron) + inference adapters\n|-- experimental/          Prototype engines/workflows (Archon MoE engine)\n|-- infra/                 Launchers (Local/Ray/Slurm), schedulers, utilities\n|-- models/                Model adapters (Megatron-Core, Transformers, custom heads)\n|-- reward/                Built-in reward functions + math parsers\n|-- tests/                 Unit/integration test suites\n|-- trainer/               High-level orchestrators (PPOTrainer, SFTTrainer)\n|-- utils/                 Cross-cutting helpers (logging, data, checkpoints, RL ops)\n+-- workflow/              RolloutWorkflow implementations (RLVR, multi-turn, vision)\n\ndocs/                      Jupyter Book docs (https://areal-project.github.io/AReaL/)\nexamples/                  Training scripts and launcher recipes\n```\n\n______________________________________________________________________\n\n## Code style & patterns\n\n- **Composition over inheritance** -- keep hierarchies \\<= 2 levels; prefer delegation.\n\n| Type             | Pattern         | Example                                   |\n| ---------------- | --------------- | ----------------------------------------- |\n| Config dataclass | `XxxConfig`     | `GRPOConfig`, `FSDPEngineConfig`          |\n| Engine class     | `XxxEngine`     | `FSDPEngine`, `ArchonEngine`              |\n| Workflow class   | `XxxWorkflow`   | `RLVRWorkflow`, `MultiTurnWorkflow`       |\n| Reward function  | `xxx_reward_fn` | `gsm8k_reward_fn`, `geometry3k_reward_fn` |\n\n**Logging**: `areal.utils.logging.getLogger(name)` with **PascalCase** names -- never\n`print` or `logging.__name__`. Per-rank format: `[{Component} Rank {N}]`. Register new\nloggers with color in `areal/utils/logging.py`.\n\n**Performance**:\n\n- No GPU-CPU sync in hot paths (`.item()`, `.tolist()`, `print(tensor)`).\n- Batch ops over Python loops on tensor elements.\n- Explicit `dtype`/`device`; `torch.Size` assertions for shape validation.\n\n**Typing & imports**: explicit type hints; reuse `areal/api/cli_args.py` dataclasses; no\nwildcard imports; heavy optional deps inside functions.\n\n**Async**: rollout workflows must stay non-blocking (`await` + `aiofiles`); no sync I/O\nin `arun_episode`.\n\n______________________________________________________________________\n\n## Domain experts & skills\n\nFire the appropriate **expert subagent** or **load a skill** based on what you're\nworking on. Experts are read-only consultants with deep domain knowledge; skills are\nstep-by-step implementation guides.\n\n| Working on...                | Fire subagent      | Load skill          |\n| ---------------------------- | ------------------ | ------------------- |\n| FSDP engine code             | `fsdp-expert`      | --                  |\n| Archon engine / new model    | `archon-expert`    | `add-archon-model`  |\n| Megatron engine code         | `megatron-expert`  | --                  |\n| RL algorithms / PPO / GRPO   | `algorithm-expert` | --                  |\n| Launcher / scheduler / infra | `launcher-expert`  | `debug-distributed` |\n| New reward function          | --                 | `add-reward`        |\n| New dataset loader           | --                 | `add-dataset`       |\n| New rollout workflow         | --                 | `add-workflow`      |\n| Unit tests                   | --                 | `add-unit-tests`    |\n| Distributed debugging        | --                 | `debug-distributed` |\n\n**How to invoke experts and skills** (platform-specific):\n\n| Platform | Fire expert subagent                                                               | Load skill                                         |\n| -------- | ---------------------------------------------------------------------------------- | -------------------------------------------------- |\n| OpenCode | `task(subagent_type=\"<name>\", load_skills=[], run_in_background=true, prompt=\"…\")` | `skill(name=\"<name>\")` or `load_skills=[\"<name>\"]` |\n| Codex    | Invoke registered subagent by canonical name (see `.codex/config.toml`)            | Reference `.agents/skills/<name>/SKILL.md`         |\n\n**Harness layout**:\n\n| Component         | OpenCode                                | Codex                                                  |\n| ----------------- | --------------------------------------- | ------------------------------------------------------ |\n| Root instructions | `AGENTS.md`                             | `AGENTS.md`                                            |\n| Agent configs     | `.opencode/agents/*.md` (frontmatter)   | `.codex/config.toml` + `.codex/agents/*.toml` + `*.md` |\n| Skills            | `.opencode/skills/` + `.agents/skills/` | `.agents/skills/<name>/SKILL.md`                       |\n\nDirectly executable workflows (both platforms): `add-workflow`, `review-pr`,\n`create-pr`, `translate-doc-zh`.\n\n______________________________________________________________________\n\n## Core concepts\n\n**Trainer** orchestrator (`areal/trainer/`, `PPOTrainer`, `SFTTrainer`): manages the\ntraining loop, dataset loading, and workflow execution. Entry point:\n`examples/math/gsm8k_rl.py`.\n\n**Rollout workflows** (`areal/workflow/`, `RolloutWorkflow.arun_episode`): define how\nepisodes are generated. Use `add-workflow` skill for step-by-step guide.\n\n**Engines**: *Inference engines* handle async generation via `engine.agenerate()` and\nmanage weight updates. *Training engines* consume rollout tensors, compute PPO/GRPO\nupdates, and broadcast weight versions (FSDP2, Megatron, or Archon).\n\n**Weight versioning**: async workflows require version alignment via `WeightUpdateMeta`\n(`areal/api/engine_api.py`). Critical for correctness across distributed training.\n\n**Observability**: emit metrics via `stats_tracker.get()`, persist artifacts under\n`dump_dir`, checkpoint via `areal/utils/saver.py` / `recover.py`.\n\n**Launcher / scheduler**: training requires cluster setup (local / Ray / Slurm) via\nconfigs in `areal/infra/launcher/`. See `launcher-expert` for deployment guidance.\n\n______________________________________________________________________\n\n## API & config rules\n\n*Applies to: `areal/api/**`*\n\n- **Field ordering**: required -> common optional -> rare optional -> internal (`_`\n  prefix).\n- **Validation**: `__post_init__` with `ValueError` and clear message.\n- **Backward compat**: add fields with defaults; deprecate before removing; avoid type\n  changes.\n- **CLI**: use `Literal` for enum choices; all public configs need docstrings with\n  constraints.\n\n______________________________________________________________________\n\n## Distributed code rules\n\n*Applies to: `areal/engine/**`, `areal/experimental/**`*\n\n- Never create global process groups at module level; always pass `process_group`\n  explicitly.\n- `dist.get_rank(group)` not `dist.get_rank()` when group matters.\n- DeviceMesh dimensions must match `ArchonParallelDims`: `dp_shard`, `tp`, `cp`, `ep`,\n  `etp`.\n- All-reduce: all ranks must call. Broadcast: explicit `src`. Barrier: debugging only.\n\n| Issue         | Cause                            | Fix                       |\n| ------------- | -------------------------------- | ------------------------- |\n| Hang          | Mismatched collective calls      | All ranks call same op    |\n| Wrong results | Incorrect `ReduceOp`             | Check SUM vs MEAN         |\n| OOM           | Unsharded tensor on wrong device | Verify DTensor placements |\n\nDebug env vars: `TORCH_DISTRIBUTED_DEBUG=DETAIL`, `NCCL_DEBUG=INFO`,\n`CUDA_LAUNCH_BLOCKING=1`. See the `debug-distributed` skill for the full workflow.\n\n______________________________________________________________________\n\n## Testing rules\n\n*Applies to: `**/tests/**`, `test_*.py`*\n\n| Marker                                  | When                             |\n| --------------------------------------- | -------------------------------- |\n| `@pytest.mark.slow`                     | > 10s (excluded from default CI) |\n| `@pytest.mark.slow` + `@pytest.mark.ci` | Slow but must run in CI          |\n| `@pytest.mark.asyncio`                  | Async tests                      |\n\n- Naming: `test_<what>_<condition>_<expected>()` with Arrange/Act/Assert.\n- GPU: skip gracefully (`@pytest.mark.skipif(not CUDA_AVAILABLE, reason=\"...\")`).\n- Distributed mocking: `torch.distributed.fake_pg`; don't mock FSDP/DTensor internals.\n- Assertions: `torch.testing.assert_close()` with explicit `rtol`/`atol`; prefer\n  `tmp_path`, `monkeypatch`.\n\n| Suite       | Command                       | GPU       |\n| ----------- | ----------------------------- | --------- |\n| Unit        | `pytest tests/test_*.py`      | No        |\n| GRPO        | `pytest tests/grpo/`          | Yes       |\n| FSDP        | `pytest tests/test_fsdp_*.py` | Yes       |\n| Distributed | `pytest tests/torchrun/`      | Multi-GPU |\n\n______________________________________________________________________\n\n## Collaboration & review\n\n- **Branches**: kebab-case (`feature/multi-turn-metrics`, `bugfix/fsdp-weight-sync`).\n- **Commits**: Conventional Commits (e.g., `feat:`, `fix:`, `docs:`, `gov:`), ~72 char\n  subject, imperative voice. Squash WIP before PR.\n- **Pre-merge**: full pre-commit stack; doc-only edits need at least `mdformat --check`.\n- **PRs**: tie to issue, highlight risk areas, list test commands executed, note skipped\n  suites with reasons.\n\n| Skill                | Purpose                                                |\n| -------------------- | ------------------------------------------------------ |\n| `create-pr`          | Rebase, squash, and create or update a PR              |\n| `commit-conventions` | Commit message conventions to load before `git commit` |\n| `review-pr`          | Dynamic PR review with targeted expert consultation    |\n| `translate-doc-zh`   | Translate English docs to Chinese                      |\n\n______________________________________________________________________\n\n## Reference material\n\n- **Docs portal**: <https://areal-project.github.io/AReaL/>\n- **Quickstart**: `docs/tutorial/quickstart.md`\n- **Architecture**: `docs/tutorial/gsm8k_grpo.md`\n- **Customization**: `docs/customization/*.md`\n- **Algorithms**: `docs/algorithms/*.md`\n- **Best practices**: `docs/best_practices/*.md`\n- **CLI reference**: `docs/cli_reference.md`\n- **Agent workflow**: `docs/customization/agent.md`\n\n<!-- File: CLAUDE.md (Tokens: ~1692 | Category: claude-rule) -->\n# CLAUDE.md - AReaL\n\n## WHAT: Project Overview\n\nAReaL is a distributed RL training framework for LLM alignment via reinforcement\nlearning.\n\n**Tech Stack**: Python 3.12+ | PyTorch | FSDP2/Megatron | SGLang/vLLM\n\n**Core Directories**:\n\n- `areal/` - Core package\n  - `api/` - Config dataclasses, workflow/engine contracts\n  - `engine/` - FSDP2, Megatron, SGLang/vLLM adapters\n    - `fsdp_utils/` - FSDP2-specific utilities (checkpoint, grad, optimizer, parallel)\n    - `megatron_utils/` - Megatron/FP8 utilities (checkpoint, pipeline, quantization)\n    - `core/` - Engine-shared utilities (distributed, lock, model, offload)\n  - `infra/` - Infrastructure (launcher, scheduler, RPC)\n    - `utils/` - Infrastructure utilities (launcher, proc, http, concurrent, slurm, ray)\n  - `workflow/` - RolloutWorkflow implementations\n  - `reward/` - Reward functions\n  - `dataset/` - Dataset loaders\n  - `utils/` - Cross-cutting utilities (logging, data, checkpoints, network, RL\n    functional)\n- `examples/` - Training scripts and configs\n- `docs/` - Jupyter Book source\n\n## WHY: Purpose\n\n- Enable efficient RL training for LLM alignment at scale\n- Async rollout + distributed training for high throughput\n- Modular design: workflows, engines, rewards, and datasets are independently extensible\n\n## HOW: Core Commands\n\n```bash\n# Check environment\npython --version              # Requires 3.12+\nuv --version                  # Install: https://docs.astral.sh/uv/\n\n# Sync dependencies\nuv sync --extra cuda          # CUDA + SGLang inference (default)\n# For vLLM: cp pyproject.vllm.toml pyproject.toml && cp uv.vllm.lock uv.lock && uv sync --extra cuda\nuv sync --group dev           # Include dev/test packages\nuv run python3 areal/tools/validate_installation.py  # Validate installation\n\n# Pre-commit hooks\npre-commit install --install-hooks  # Set up hooks (run once)\npre-commit run --all-files    # Format and lint\n\n# Run tests\n# First check GPU availability (many tests require GPU)\npython -c \"import torch; print('GPU available:', torch.cuda.is_available())\"\nuv run pytest tests/test_<topic>.py\n\n# Generate CLI docs\nuv run python docs/generate_cli_docs.py\n\n# Build docs (canonical, release-aligned)\n./docs/build_all.sh\n# Do NOT use `jupyter-book build docs/en|docs/zh` directly for final preview/release,\n# because it skips AReaL-specific static setup and output packaging.\n```\n\n## Boundaries\n\n### Constraints\n\n- Designed for distributed GPU clusters; assume containerized execution\n- Integration tests require multi-node hardware; explain skips when unavailable\n- Secrets and endpoints are managed outside the repo\n\n### Always Do\n\n- Read relevant files before modifying code\n- Run `pre-commit run --all-files` before committing\n- Follow existing code patterns in the same module\n- Add tests for new functionality\n\n### Ask First\n\n- Modifying config structures in `areal/api/cli_args.py`\n- Adding new dependencies\n- Changing launcher or scheduler logic\n- Deleting or renaming public APIs\n- Running GPU/distributed tests (check GPU first:\n  `python -c \"import torch; print('GPU available:', torch.cuda.is_available())\"`)\n\n### Never Do\n\n- Hardcode secrets, paths, or endpoints\n- Skip pre-commit hooks\n- Guess cluster configs or rebuild CUDA/driver stacks\n- Use wildcard imports (`from x import *`)\n\n## Progressive Disclosure: Detailed Guides\n\n| Task                   | Reference                                                     |\n| ---------------------- | ------------------------------------------------------------- |\n| Add Workflow           | `docs/customization/agent.md`, `areal/workflow/multi_turn.py` |\n| Add Dataset            | `docs/customization/`, `areal/dataset/gsm8k.py`               |\n| Add Reward             | `areal/api/reward_api.py`, `areal/reward/geometry3k.py`       |\n| Add Archon Model       | `areal/experimental/models/archon/qwen2/`, `qwen3/`           |\n| Algorithm Details      | `docs/algorithms/*.md`                                        |\n| R3 Router Replay       | `r3/README.md`                                                |\n| Quickstart             | `docs/tutorial/quickstart.md`                                 |\n| Architecture Deep Dive | `docs/tutorial/gsm8k_grpo.md`                                 |\n| CLI Reference          | `docs/cli_reference.md`                                       |\n\n## Git Workflow\n\n- **Commits**: Conventional Commits (e.g., `feat:`, `fix:`, `docs:`, `gov:`), ~72 chars\n  subject, imperative voice, reasoning in body\n- **Squash**: Squash WIP commits before opening PR\n- **PR requirements**: Run pre-commit, document test coverage, note hardware limitations\n\n## Extended Configuration\n\nSee `.claude/agents/`, `.claude/skills/`, `.claude/commands/`, and `.claude/rules/` for\nspecialized instructions.\n\n### Agents\n\n| Agent                       | Purpose                                   | Activation Trigger                                                  |\n| --------------------------- | ----------------------------------------- | ------------------------------------------------------------------- |\n| `planner`                   | Implementation planning                   | Before multi-file changes, new features, or architectural decisions |\n| `simple-code-reviewer`      | Quick code quality checks                 | After code changes, before committing                               |\n| `code-verifier`             | Formatting/linting/tests                  | After code changes, before committing                               |\n| `fsdp-engine-expert`        | FSDPEngine implementation                 | FSDPEngine code changes or questions                                |\n| `archon-engine-expert`      | ArchonEngine implementation               | ArchonEngine code changes or questions                              |\n| `megatron-engine-expert`    | MegatronEngine implementation             | MegatronEngine code changes or questions                            |\n| `algorithm-expert`          | RL algorithms                             | GRPO/PPO/DAPO questions                                             |\n| `launcher-scheduler-expert` | Cluster launching and resource scheduling | Launcher/scheduler code changes or configuration questions          |\n\n**Stage-by-Stage Agent Guidance**:\n\n1. **Planning Stage** (Before coding): Use `planner` for architecture design and\n   implementation planning\n1. **Code Formatting & Linting** (After coding): Use `code-verifier` to automatically\n   run formatting, linting, and tests, catching syntax errors and style issues quickly\n1. **Code Quality Check** (After formatting): Use `simple-code-reviewer` for quick code\n   quality checks, focusing on logic issues and code smells\n\n### Skills (Guided Development Workflows)\n\nSkills provide step-by-step guides for common development tasks:\n\n- `/add-dataset` - Dataset loader creation guide\n- `/add-workflow` - Workflow implementation guide\n- `/add-reward` - Reward function guide\n- `/add-archon-model` - Archon engine model architecture guide\n- `/debug-distributed` - Distributed debugging guide\n- `/add-unit-tests` - Test development guide (NEW)\n\n### Commands (User-invoked Actions)\n\nCommands perform specific actions when invoked:\n\n- `/create-pr` - Rebase, squash commits, and create/update PR with intelligent messages\n- `/gen-commit-msg` - Generate commit messages from staged changes\n- `/review-pr` - Intelligent PR code review with dynamic agent allocation\n- `/translate-doc-zh` - Translate English documentation to Chinese\n\n### Rules (Code Quality Standards)\n\nProject-wide standards enforced across all code changes:\n\n- `api-config.md` - Configuration dataclass design patterns\n- `code-style.md` - Coding conventions beyond pre-commit hooks\n- `distributed.md` - Distributed training patterns and constraints\n- `testing.md` - Testing strategy and coverage requirements\n\n<!-- File: .agents/skills/add-archon-model/SKILL.md (Tokens: ~4820 | Category: anthropic-skill) -->\n---\nname: add-archon-model\ndescription: Guide for adding a new model to the Archon engine. Use when user wants to add support for a new HuggingFace model architecture in ArchonEngine.\n---\n\n# Add Archon Model\n\nAdd support for a new HuggingFace model architecture in the Archon training engine.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a model to Archon?\"\n- User wants to support a new model family (e.g., Llama, Mistral, DeepSeek) in\n  ArchonEngine\n- User mentions adding a new `ModelSpec` or model type for Archon\n\n## Prerequisites\n\nBefore starting, ensure:\n\n- The target model is available on HuggingFace (has `config.json` with `model_type`)\n- You know the HuggingFace model ID (e.g., `meta-llama/Llama-3-8B`)\n- The model uses a standard transformer architecture (decoder-only)\n\n## Step-by-Step Guide\n\n### Step 1: Analyze the Target Model Architecture\n\nRead the HuggingFace model's source code to extract key architecture information.\n\n**Action**: Fetch and analyze the model's HuggingFace configuration and modeling files.\n\n1. Read the model's `config.json` (via `AutoConfig.from_pretrained`) to identify:\n\n   - `model_type` string (this is the key used for registry lookup)\n   - All architecture hyperparameters (hidden_size, num_layers, etc.)\n   - Any model-specific fields (e.g., `qk_norm`, `attention_bias`, MoE fields)\n\n1. Read the HuggingFace `modeling_*.py` source to identify:\n\n   - **Attention variant**: Does it have Q/K norm? Attention bias? Sliding window?\n     Multi-latent attention?\n   - **FFN variant**: SwiGLU (gate_proj + up_proj + down_proj)? GeGLU? Standard MLP?\n   - **MoE support**: Does it have MoE layers? What router type? Shared experts?\n   - **RoPE variant**: Standard RoPE? YaRN? NTK-aware scaling? What is the inv_freq\n     formula?\n   - **Normalization**: RMSNorm or LayerNorm? Pre-norm or post-norm? Elementwise affine?\n   - **Weight tying**: Does `tie_word_embeddings` appear in config?\n   - **State dict key names**: What are the HF weight key naming conventions?\n\n1. Summarize findings in a checklist like:\n\n```\nTarget model: <name>\nHF model_type: \"<model_type>\" (and variants like \"<model_type>_moe\" if applicable)\nAttention: [standard GQA / with QK norm / with bias / sliding window / ...]\nFFN: [SwiGLU / GeGLU / standard MLP / ...]\nMoE: [no / yes - num_experts, top_k, shared_experts]\nRoPE: [standard / YaRN / NTK-aware / ...]\nNorm: [RMSNorm / LayerNorm] with [pre-norm / post-norm]\nWeight tying: [yes / no]\n```\n\n### Step 2: Select the Reference Model\n\nChoose the closest existing implementation as a starting point:\n\n| Target characteristics               | Reference | Why                                     |\n| ------------------------------------ | --------- | --------------------------------------- |\n| Dense-only, standard GQA, no QK norm | `qwen2`   | Simplest baseline, pure dense           |\n| Has QK norm, or has MoE support      | `qwen3`   | Supports QK norm + MoE + shared experts |\n\n**Action**: Copy the reference model directory as the starting point:\n\n```\nareal/experimental/models/archon/<model>/\n  __init__.py\n  spec.py\n  model/\n    args.py\n    model.py\n    rope.py\n    state_dict_adapter.py\n  infra/\n    parallelize.py\n```\n\n### Step 3: Implement `args.py`\n\nAdapt `<Model>ModelArgs` to match the target model's HuggingFace config fields.\n\n**Key changes from reference**:\n\n1. Update the `@dataclass` fields to match the target model's hyperparameters:\n\n   - Field names should use Archon conventions (`dim`, `n_layers`, `n_heads`,\n     `n_kv_heads`, `vocab_size`, `head_dim`, `hidden_dim`, `norm_eps`, `rope_theta`,\n     etc.)\n   - Default values should match the smallest variant of the target model\n   - Add model-specific fields (e.g., `attention_bias`, `qk_norm`, `sliding_window`)\n\n1. Update `from_hf_config()` to correctly map HuggingFace config attributes:\n\n   - Use `getattr(hf_config, \"field_name\", default)` for optional fields\n   - Handle variant-specific fields (e.g., MoE fields only present in MoE variants)\n   - The method must return an instance of the model args class\n\n**Critical**: Verify every field mapping against the HF model's `config.json`. Incorrect\nmappings here cause silent errors downstream.\n\n**Base class contract** (`BaseModelArgs`):\n\n```python\n@dataclass\nclass <Model>ModelArgs(BaseModelArgs):\n    # ... model-specific fields ...\n\n    @classmethod\n    def from_hf_config(\n        cls,\n        hf_config: PretrainedConfig,\n        is_critic: bool = False,\n        **kwargs,\n    ) -> <Model>ModelArgs:\n        # Map HF config fields to Archon model args\n        ...\n```\n\n### Step 4: Implement `model.py`\n\nAdapt the model architecture to match the target model.\n\n**Key components to adapt**:\n\n1. **Normalization** (`RMSNorm` or similar):\n\n   - Check if `elementwise_affine` is configurable\n   - Check the epsilon default value\n   - If the model uses `LayerNorm`, implement accordingly\n\n1. **Attention** module:\n\n   - Q/K/V projection: Check bias presence (`nn.Linear(..., bias=True/False)`)\n   - QK norm: Add `q_norm`/`k_norm` if the model has them, remove if it doesn't\n   - GQA: `n_kv_heads` \\< `n_heads` for grouped-query attention\n   - Ulysses SP: Keep the `set_cp_group` / `_sp_enabled` pattern from the reference\n   - Output projection: Check bias presence\n\n1. **FeedForward** module:\n\n   - SwiGLU: `w2(silu(w1(x)) * w3(x))` -- most common for modern LLMs\n   - Check bias in linear layers\n   - For MoE models: `MoE` module replaces `FeedForward` on designated layers\n\n1. **TransformerBlock**: Pre-norm (most modern LLMs) vs post-norm\n\n   - MoE layer detection via `_is_moe_layer()` if applicable\n\n1. **Top-level Model** (`<Model>Model(BaseArchonModel)`):\n\n   - `tok_embeddings`, `layers` (as `ModuleDict`), `norm`, `output`/`score`\n   - `init_weights()`: Match initialization scheme from HF\n   - `init_buffers()`: RoPE cache + MoE buffers\n   - `forward()`: Must follow `BaseArchonModel` signature:\n     `(tokens, positions, cu_seqlens, max_seqlen, tree_attn_meta=None) -> Tensor`\n\n**Base class contract** (`BaseArchonModel`):\n\n```python\nclass <Model>Model(BaseArchonModel):\n    def forward(self, tokens, positions, cu_seqlens, max_seqlen, tree_attn_meta=None) -> torch.Tensor: ...\n    def init_weights(self) -> None: ...\n    def init_buffers(self, buffer_device) -> None: ...\n```\n\n### Step 5: Implement `rope.py`\n\nHandle the rotary position embedding variant.\n\n**Options**:\n\n1. **Standard RoPE** (same as qwen2/qwen3): Re-export from qwen2:\n\n   ```python\n   from areal.experimental.models.archon.qwen2.model.rope import (\n       apply_rotary_emb,\n       precompute_rope_cache,\n       repeat_kv,\n       reshape_for_broadcast,\n       rotate_half,\n   )\n   ```\n\n1. **Custom RoPE** (YaRN, NTK-aware, etc.): Implement custom `precompute_rope_cache()`\n   and `apply_rotary_emb()` functions. The key difference is usually in how `inv_freq`\n   is computed (scaling factors, interpolation, etc.).\n\n### Step 6: Implement `state_dict_adapter.py`\n\nMap between HuggingFace and Archon weight key names.\n\n**This is the most error-prone step.** The adapter must correctly handle:\n\n1. **Key name mapping** (`from_hf_map` dict):\n\n   - Embedding: `model.embed_tokens.weight` -> `tok_embeddings.weight`\n   - Attention: `model.layers.{}.self_attn.q_proj.weight` ->\n     `layers.{}.attention.wq.weight`\n   - FFN: `model.layers.{}.mlp.gate_proj.weight` -> `layers.{}.feed_forward.w1.weight`\n   - Norms: `model.layers.{}.input_layernorm.weight` ->\n     `layers.{}.attention_norm.weight`\n   - Output: `lm_head.weight` -> `output.weight`\n   - Skip keys (set to `None`): `rotary_emb.inv_freq` (computed at runtime)\n   - Model-specific keys: bias terms, QK norm weights, etc.\n\n1. **Reverse mapping** (`to_hf_map`): Auto-generated from `from_hf_map`\n\n1. **MoE expert weights** (if applicable): 3D\\<->2D conversion for expert weights. Copy\n   the MoE handling from qwen3 if the model has MoE.\n\n1. **Weight tying**: Skip `output.weight` during `to_hf()` if `tie_word_embeddings=True`\n\n**Verification approach**: After implementation, the adapter should satisfy:\n\n```python\n# Roundtrip: archon -> hf -> archon preserves all keys\nhf_sd = adapter.to_hf(archon_sd)\nroundtrip_sd = adapter.from_hf(hf_sd)\nassert set(roundtrip_sd.keys()) == set(archon_sd.keys())\n```\n\n**Base class contract** (`BaseStateDictAdapter`):\n\n```python\nclass <Model>StateDictAdapter(BaseStateDictAdapter):\n    def from_hf(self, hf_state_dict) -> dict[str, Any]: ...\n    def to_hf(self, archon_state_dict) -> dict[str, Any]: ...\n    def convert_single_to_hf(self, name, tensor) -> list[tuple[str, torch.Tensor]]: ...\n```\n\n### Step 7: Implement `parallelize.py`\n\nDefine the parallelization strategy for the model.\n\n**The parallelize function** applies parallelism in this order:\n\n1. TP (Tensor Parallelism) -- shard attention/FFN across devices\n1. EP (Expert Parallelism) -- for MoE models only\n1. CP (Context Parallelism / Ulysses SP) -- sequence parallelism\n1. AC (Activation Checkpointing) -- memory optimization\n1. torch.compile -- compilation optimization\n1. FSDP (Fully Sharded Data Parallelism) -- data parallelism\n\n**Key adaptations by model architecture**:\n\n- **Attention with QK norm**: wq/wk use `use_local_output=False` (DTensor output for\n  norm), add `SequenceParallel(sequence_dim=2)` for q_norm/k_norm\n- **Attention without QK norm**: wq/wk/wv all use `use_local_output=True`\n- **Attention with bias**: Bias terms follow the same parallel plan as their weights\n- **MoE layers**: Separate TP plan for MoE input/output, router gate, and expert\n  weights. Copy from qwen3's `apply_moe_ep_tp()` and `apply_non_moe_tp()`\n- **Dense-only models**: Simpler plan without MoE handling. Copy from qwen2\n\n**Function signature** (must match `ParallelizeFn` protocol):\n\n```python\ndef parallelize_<model>(\n    model: nn.Module,\n    parallel_dims: ArchonParallelDims,\n    param_dtype: torch.dtype = torch.bfloat16,\n    reduce_dtype: torch.dtype = torch.float32,\n    loss_parallel: bool = True,\n    cpu_offload: bool = False,\n    reshard_after_forward_policy: str = \"default\",\n    ac_config: ActivationCheckpointConfig | None = None,\n    enable_compile: bool = True,\n) -> nn.Module:\n```\n\n### Step 8: Create `spec.py` and Register\n\nAssemble the `ModelSpec` and register it.\n\n```python\nfrom areal.experimental.models.archon.model_spec import ModelSpec, register_model_spec\nfrom areal.experimental.models.archon.pipeline_parallel import pipeline_llm\nfrom areal.experimental.models.archon.<model>.infra.parallelize import parallelize_<model>\nfrom areal.experimental.models.archon.<model>.model.args import <Model>ModelArgs\nfrom areal.experimental.models.archon.<model>.model.model import <Model>Model\nfrom areal.experimental.models.archon.<model>.model.state_dict_adapter import (\n    <Model>StateDictAdapter,\n)\n\n<MODEL>_SPEC = ModelSpec(\n    name=\"<Model>\",\n    model_class=<Model>Model,\n    model_args_class=<Model>ModelArgs,\n    state_dict_adapter_class=<Model>StateDictAdapter,\n    parallelize_fn=parallelize_<model>,\n    supported_model_types=frozenset({\"<model_type>\"}),  # From HF config.json\n    pipelining_fn=pipeline_llm,\n)\n\n# Auto-register when module is imported\nregister_model_spec(<MODEL>_SPEC)\n\n__all__ = [\"<MODEL>_SPEC\"]\n```\n\n**Note**: `supported_model_types` should include all HF `model_type` strings that this\nimplementation handles (e.g., `{\"qwen3\", \"qwen3_moe\"}` for Qwen3).\n\n### Step 9: Register in `__init__.py`\n\nAdd the import to `areal/experimental/models/archon/__init__.py`:\n\n```python\nfrom areal.experimental.models.archon.<model> import spec as <model>_spec  # noqa: F401\n```\n\nThis triggers auto-registration when the module is imported.\n\n### Step 10: Verify and Test\n\nVerification should be done in stages, adapting based on available hardware and the test\npatterns in `tests/experimental/archon/`.\n\n**Before writing tests**, examine the existing test files to understand current\npatterns:\n\n```\ntests/experimental/archon/\n  conftest.py             -- Pytest configuration (version checks)\n  utils.py                -- Shared utilities (model loading, comparison)\n  test_qwen3_args.py      -- Args unit tests (CPU-only)\n  test_state_dict_adapter.py  -- State dict roundtrip tests\n  test_weight_sync.py     -- Weight completeness tests (meta device)\n  test_forward.py         -- Forward precision comparison (single GPU)\n  ...\n```\n\n**Test stages** (write tests appropriate for the model's complexity):\n\n#### Stage 1: Args Tests (CPU-only, always write these)\n\nTest `from_hf_config()` with mock HuggingFace configs:\n\n```python\n# Pattern: Create mock PretrainedConfig, verify args mapping\nfrom unittest.mock import MagicMock\n\ndef test_args_from_hf_config():\n    hf_config = MagicMock()\n    hf_config.hidden_size = 4096\n    hf_config.num_hidden_layers = 32\n    # ... set all required fields\n    args = <Model>ModelArgs.from_hf_config(hf_config)\n    assert args.dim == 4096\n    assert args.n_layers == 32\n```\n\n#### Stage 2: State Dict Adapter Tests (CPU-only)\n\nTest key mapping roundtrip:\n\n```python\ndef test_state_dict_roundtrip():\n    # Create adapter with mock config\n    adapter = <Model>StateDictAdapter(mock_config)\n    # Create fake archon state dict with expected keys\n    archon_sd = {\"tok_embeddings.weight\": torch.randn(vocab, dim), ...}\n    # Roundtrip\n    hf_sd = adapter.to_hf(archon_sd)\n    roundtrip = adapter.from_hf(hf_sd)\n    assert set(roundtrip.keys()) == set(archon_sd.keys())\n```\n\n#### Stage 3: Weight Completeness (meta device, CPU-only)\n\nVerify all model parameters have HF mappings:\n\n```python\ndef test_weight_completeness():\n    # Create model on meta device\n    with torch.device(\"meta\"):\n        model = <Model>Model(args)\n    adapter = <Model>StateDictAdapter(hf_config)\n    # Check every archon param has a HF mapping\n    for name, _ in model.named_parameters():\n        hf_pairs = adapter.convert_single_to_hf(name, torch.empty(0))\n        assert len(hf_pairs) > 0, f\"No HF mapping for {name}\"\n```\n\n#### Stage 4: Forward Precision (single GPU, if available)\n\nCompare Archon model output against HuggingFace reference:\n\n```python\n@pytest.mark.skipif(not torch.cuda.is_available(), reason=\"Requires CUDA\")\ndef test_forward_matches_hf():\n    # Load both HF and Archon models\n    # Run forward on same input\n    # Compare logits within tolerance\n```\n\n**Important**: Do NOT hardcode the test categories. Inspect the existing test files in\n`tests/experimental/archon/` and follow the same patterns, fixtures, and markers. Adapt\ntest scope to the model's specific features (e.g., add MoE-specific tests only if the\nmodel has MoE).\n\n## Reference Implementations\n\n| Model | Directory                                 | Features                                                |\n| ----- | ----------------------------------------- | ------------------------------------------------------- |\n| Qwen2 | `areal/experimental/models/archon/qwen2/` | Dense, attention bias, no QK norm                       |\n| Qwen3 | `areal/experimental/models/archon/qwen3/` | Dense + MoE, QK norm, no attention bias, shared experts |\n\n## Architecture Decision Map\n\n| Feature             | qwen2    | qwen3                      | What to check in target model                            |\n| ------------------- | -------- | -------------------------- | -------------------------------------------------------- |\n| Attention bias      | Yes      | No                         | `attention_bias` in HF config                            |\n| QK norm             | No       | Yes                        | `qk_norm` in HF config or QKNorm module in modeling file |\n| MoE                 | No       | Yes                        | `num_experts`/`num_local_experts` in HF config           |\n| Shared experts      | No       | Yes                        | `num_shared_experts` in HF config                        |\n| Decoder sparse step | No       | Yes                        | `decoder_sparse_step` in HF config                       |\n| Weight tying        | Both     | Both                       | `tie_word_embeddings` in HF config                       |\n| RoPE                | Standard | Standard (re-export qwen2) | Check inv_freq formula in HF modeling code               |\n\n## Common Mistakes\n\n- Not mapping all HF keys in `state_dict_adapter.py` (causes silent weight drops)\n- Wrong `from_hf_config()` field mapping (uses wrong HF config attribute name)\n- Forgetting to handle `None` keys in `from_hf_map` (keys to skip like\n  `rotary_emb.inv_freq`)\n- Missing MoE expert weight 3D\\<->2D conversion when model has MoE\n- Wrong TP plan for attention with/without QK norm (`use_local_output` must match)\n- Forgetting to add import line in `areal/experimental/models/archon/__init__.py`\n- Not including all `model_type` variants in `supported_model_types` frozenset\n- Using `print` instead of `areal.utils.logging.getLogger()`\n\n## File Checklist\n\nAfter completion, verify all files exist and are consistent:\n\n- [ ] `areal/experimental/models/archon/<model>/__init__.py`\n- [ ] `areal/experimental/models/archon/<model>/spec.py` -- ModelSpec + register\n- [ ] `areal/experimental/models/archon/<model>/model/args.py` -- ModelArgs +\n  from_hf_config\n- [ ] `areal/experimental/models/archon/<model>/model/model.py` -- Model + Attention +\n  FFN\n- [ ] `areal/experimental/models/archon/<model>/model/rope.py` -- RoPE (or re-export)\n- [ ] `areal/experimental/models/archon/<model>/model/state_dict_adapter.py` -- Key\n  mapping\n- [ ] `areal/experimental/models/archon/<model>/infra/parallelize.py` -- Parallel\n  strategy\n- [ ] `areal/experimental/models/archon/__init__.py` -- Import line added\n- [ ] `tests/experimental/archon/test_<model>_*.py` -- Tests\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nCanonical location: .agents/skills/add-archon-model/SKILL.md\nMirrors: .opencode/skills/add-archon-model/SKILL.md, .claude/skills/add-archon-model/SKILL.md\nInvocation: $add-archon-model (Codex) / /add-archon-model (OpenCode, Claude Code)\n\n## Purpose\n\nSemi-automated guide for adding new model architectures to the Archon training engine.\nUnlike simpler skills (add-reward, add-dataset), this skill actively guides the agent to:\n1. Analyze HuggingFace source code to extract architecture details\n2. Select the closest reference implementation (qwen2 or qwen3)\n3. Generate code skeletons adapted to the target architecture\n4. Create appropriate tests based on existing test patterns\n\n## How to Update\n\n### When New Reference Models Are Added\n1. Add to \"Reference Implementations\" table\n2. Update \"Architecture Decision Map\" with new feature columns\n3. Update Step 2 (reference selection) with new options\n\n### When Base Classes Change\n1. Update contract signatures in Steps 3, 4, 6, 7\n2. Update file checklist if new files are required\n\n### When ModelSpec Changes\n1. Update Step 8 with new ModelSpec fields\n2. Update spec.py template\n\n### When Test Patterns Change\n1. Update Step 10 with new test patterns\n2. Do NOT hardcode test categories -- keep it flexible\n\n### Important Design Decisions\n- This skill is SEMI-AUTOMATED: Claude should read HF source and generate code,\n  not just provide templates for the user to fill in manually\n- The skill references existing test files rather than hardcoding test categories,\n  ensuring it stays current as the test suite evolves\n- Reference model selection (qwen2 vs qwen3) is based on MoE and QK norm presence\n\n================================================================================\n-->\n\n<!-- File: .agents/skills/add-dataset/SKILL.md (Tokens: ~1323 | Category: anthropic-skill) -->\n---\nname: add-dataset\ndescription: Guide for adding a new dataset loader to AReaL. Use when user wants to add a new dataset.\n---\n\n# Add Dataset\n\nAdd a new dataset loader to AReaL.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a dataset?\"\n- User wants to integrate a new dataset\n- User mentions creating a dataset loader\n\n## Step-by-Step Guide\n\n### Step 1: Create Dataset File\n\nCreate `areal/dataset/<name>.py`:\n\n```python\nfrom datasets import Dataset, load_dataset\n\n\ndef get_<name>_sft_dataset(\n    path: str,\n    split: str,\n    tokenizer,\n    max_length: int | None = None,\n) -> Dataset:\n    \"\"\"Load dataset for SFT training.\n\n    Args:\n        path: Path to dataset (HuggingFace hub or local path)\n        split: Dataset split (train/validation/test)\n        tokenizer: Tokenizer for processing\n        max_length: Maximum sequence length (optional)\n\n    Returns:\n        HuggingFace Dataset with processed samples\n    \"\"\"\n    dataset = load_dataset(path=path, split=split)\n\n    def process(sample):\n        # Tokenize the full sequence (prompt + response)\n        seq_token = tokenizer.encode(\n            sample[\"question\"] + sample[\"answer\"] + tokenizer.eos_token\n        )\n        prompt_token = tokenizer.encode(sample[\"question\"])\n        # Loss mask: 0 for prompt, 1 for response\n        loss_mask = [0] * len(prompt_token) + [1] * (len(seq_token) - len(prompt_token))\n        return {\"input_ids\": seq_token, \"loss_mask\": loss_mask}\n\n    dataset = dataset.map(process).remove_columns([\"question\", \"answer\"])\n\n    if max_length is not None:\n        dataset = dataset.filter(lambda x: len(x[\"input_ids\"]) <= max_length)\n\n    return dataset\n\n\ndef get_<name>_rl_dataset(\n    path: str,\n    split: str,\n    tokenizer,\n    max_length: int | None = None,\n) -> Dataset:\n    \"\"\"Load dataset for RL training.\n\n    Args:\n        path: Path to dataset\n        split: Dataset split\n        tokenizer: Tokenizer for length filtering\n        max_length: Maximum sequence length\n\n    Returns:\n        HuggingFace Dataset with prompts and answers for reward computation\n    \"\"\"\n    dataset = load_dataset(path=path, split=split)\n\n    def process(sample):\n        messages = [\n            {\n                \"role\": \"user\",\n                \"content\": sample[\"question\"],\n            }\n        ]\n        return {\"messages\": messages, \"answer\": sample[\"answer\"]}\n\n    dataset = dataset.map(process).remove_columns([\"question\"])\n\n    if max_length is not None:\n\n        def filter_length(sample):\n            content = sample[\"messages\"][0][\"content\"]\n            tokens = tokenizer.encode(content)\n            return len(tokens) <= max_length\n\n        dataset = dataset.filter(filter_length)\n\n    return dataset\n```\n\n### Step 2: Register in __init__.py\n\nUpdate `areal/dataset/__init__.py`:\n\n```python\n# Add to VALID_DATASETS\nVALID_DATASETS = [\n    # ... existing datasets\n    \"<name>\",\n]\n\n# Add to _get_custom_dataset function\ndef _get_custom_dataset(name: str, ...):\n    # ... existing code\n    elif name == \"<name>\":\n        from areal.dataset.<name> import get_<name>_sft_dataset, get_<name>_rl_dataset\n        if dataset_type == \"sft\":\n            return get_<name>_sft_dataset(path, split, max_length, tokenizer)\n        else:\n            return get_<name>_rl_dataset(path, split, max_length, tokenizer)\n```\n\n### Step 3: Add Config (Optional)\n\nIf the dataset needs special configuration, add to `areal/api/cli_args.py`:\n\n```python\n@dataclass\nclass TrainDatasetConfig:\n    # ... existing fields\n    <name>_specific_field: Optional[str] = None\n```\n\n### Step 4: Add Tests\n\nCreate `tests/test_<name>_dataset.py`:\n\n```python\nimport pytest\nfrom areal.dataset.<name> import get_<name>_sft_dataset, get_<name>_rl_dataset\n\ndef test_sft_dataset_loads(tokenizer):\n    dataset = get_<name>_sft_dataset(\"path/to/data\", split=\"train\", tokenizer=tokenizer)\n    assert len(dataset) > 0\n    assert \"input_ids\" in dataset.column_names\n    assert \"loss_mask\" in dataset.column_names\n\ndef test_rl_dataset_loads(tokenizer):\n    dataset = get_<name>_rl_dataset(\"path/to/data\", split=\"train\", tokenizer=tokenizer)\n    assert len(dataset) > 0\n    assert \"messages\" in dataset.column_names\n    assert \"answer\" in dataset.column_names\n```\n\n## Reference Implementations\n\n| Dataset    | File                               | Description              |\n| ---------- | ---------------------------------- | ------------------------ |\n| GSM8K      | `areal/dataset/gsm8k.py`           | Math word problems       |\n| Geometry3K | `areal/dataset/geometry3k.py`      | Geometry problems        |\n| CLEVR      | `areal/dataset/clevr_count_70k.py` | Visual counting          |\n| HH-RLHF    | `areal/dataset/hhrlhf.py`          | Helpfulness/Harmlessness |\n| TORL       | `areal/dataset/torl_data.py`       | Tool-use RL              |\n\n## Required Fields\n\n### SFT Dataset\n\n```python\n{\n    \"messages\": [\n        {\"role\": \"user\", \"content\": \"...\"},\n        {\"role\": \"assistant\", \"content\": \"...\"},\n    ]\n}\n```\n\n### RL Dataset\n\n```python\n{\n    \"messages\": [\n        {\"role\": \"user\", \"content\": \"...\"},\n    ],\n    \"answer\": \"ground_truth_for_reward\",\n    # Optional metadata for reward function\n}\n```\n\n## Common Mistakes\n\n- Returning `List[Dict]` instead of HuggingFace `Dataset`\n- Using Python loops instead of `dataset.map()`/`filter()`\n- Missing `\"messages\"` field for RL datasets\n- Wrong message format (should be list of dicts with `role` and `content`)\n- Not registering in `__init__.py`\n\n<!-- File: .agents/skills/add-reward/SKILL.md (Tokens: ~1180 | Category: anthropic-skill) -->\n---\nname: add-reward\ndescription: Guide for adding a new reward function to AReaL. Use when user wants to create a reward function.\n---\n\n# Add Reward\n\nAdd a new reward function to AReaL.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a reward function?\"\n- User wants to implement custom rewards\n- User mentions reward computation\n\n## Step-by-Step Guide\n\n### Step 1: Create Reward File\n\nCreate `areal/reward/<name>.py`:\n\n```python\nfrom typing import Any\n\nfrom areal.utils import logging\n\nlogger = logging.getLogger(\"MyReward\")\n\n\ndef <name>_reward_fn(\n    prompt: str,\n    completions: str,\n    prompt_ids,\n    completion_ids,\n    answer: str | None = None,\n    **kwargs: Any,\n) -> float:\n    \"\"\"Compute reward for a single completion.\n\n    Args:\n        prompt: Prompt string\n        completions: Completion string (model output)\n        prompt_ids: Tokenized prompt IDs\n        completion_ids: Tokenized completion IDs\n        answer: Ground truth answer from dataset (optional)\n        **kwargs: Additional data from dataset\n\n    Returns:\n        Reward value (float), typically 0.0 or 1.0\n    \"\"\"\n    try:\n        # Extract answer from completion\n        extracted = _extract_answer(completions)\n\n        # Compare with ground truth\n        if answer is not None and extracted == str(answer):\n            return 1.0\n        return 0.0\n    except Exception:\n        logger.warning(\"Exception in reward computation\", exc_info=True)\n        return 0.0\n\n\ndef _extract_answer(completion: str) -> str:\n    \"\"\"Extract the answer from a completion string.\n\n    Implement your extraction logic here.\n    \"\"\"\n    # Example: Extract content from \\boxed{}\n    import re\n\n    match = re.search(r\"\\\\boxed\\{([^}]+)\\}\", completion)\n    if match:\n        return match.group(1).strip()\n    return completion.strip()\n```\n\n### Step 2: Register in __init__.py\n\nUpdate `areal/reward/__init__.py`:\n\n```python\n# Add to VALID_REWARD_FN\nVALID_REWARD_FN = [\n    # ... existing reward functions\n    \"<name>\",\n]\n\n# Add to get_reward_fn function\ndef get_reward_fn(name: str, **kwargs):\n    # ... existing code\n    elif name == \"<name>\":\n        from areal.reward.<name> import <name>_reward_fn\n        return <name>_reward_fn\n```\n\n### Step 3: Handle Blocking Operations\n\nIf your reward function uses blocking operations (e.g., API calls, model inference), the\nworkflow will wrap it with `AsyncRewardWrapper`:\n\n```python\n# In your workflow\nfrom areal.reward import AsyncRewardWrapper\n\nself.reward_fn = AsyncRewardWrapper(reward_fn)\n\n# Then call it asynchronously\nrewards = await self.reward_fn(prompt, completions, **data)\n```\n\n### Step 4: Add Tests\n\nCreate `tests/test_<name>_reward.py`:\n\n```python\nimport pytest\nfrom areal.reward.<name> import <name>_reward_fn\n\ndef test_reward_correct_answer():\n    reward = <name>_reward_fn(\n        prompt=\"What is 2+2?\",\n        completions=\"The answer is \\\\boxed{4}\",\n        prompt_ids=None,\n        completion_ids=None,\n        answer=\"4\",\n    )\n    assert reward == 1.0\n\ndef test_reward_wrong_answer():\n    reward = <name>_reward_fn(\n        prompt=\"What is 2+2?\",\n        completions=\"The answer is \\\\boxed{5}\",\n        prompt_ids=None,\n        completion_ids=None,\n        answer=\"4\",\n    )\n    assert reward == 0.0\n```\n\n## Reference Implementations\n\n| Reward     | File                              | Description                  |\n| ---------- | --------------------------------- | ---------------------------- |\n| GSM8K      | `areal/reward/gsm8k.py`           | Math answer verification     |\n| Geometry3K | `areal/reward/geometry3k.py`      | Geometry answer verification |\n| CLEVR      | `areal/reward/clevr_count_70k.py` | Counting verification        |\n| MathVerify | `areal/reward/math_verify.py`     | General math verification    |\n\n## Function Signature\n\nAll reward functions must follow this signature:\n\n```python\ndef reward_fn(\n    prompt: str,               # Input prompt string\n    completions: str,          # Model completion string\n    prompt_ids,                # Tokenized prompt\n    completion_ids,            # Tokenized completion\n    **kwargs: Any,             # Additional data from dataset (e.g., answer)\n) -> float:                    # Reward value (typically 0.0 or 1.0)\n```\n\n**Note**: The reward function is called once per sample. Batching is handled by\n`AsyncRewardWrapper` in the workflow.\n\n## Key Requirements\n\n1. **Deterministic**: Same inputs should produce same outputs\n1. **Return float**: Output is a single float value per sample\n1. **No blocking in async context**: Use `AsyncRewardWrapper` if needed\n1. **Logging**: Use `areal.utils.logging`, not `print`\n1. **Handle exceptions**: Return 0.0 on error, don't raise\n\n## Common Mistakes\n\n- Returning a tensor instead of a float\n- Expecting batched inputs (reward is called per sample)\n- Non-deterministic behavior\n- Blocking operations without `AsyncRewardWrapper`\n- Raising exceptions instead of returning 0.0\n\n<!-- File: .agents/skills/add-unit-tests/SKILL.md (Tokens: ~1503 | Category: anthropic-skill) -->\n---\nname: add-unit-tests\ndescription: Guide for adding unit tests to AReaL. Use when user wants to add tests for new functionality or increase test coverage.\n---\n\n# Add Unit Tests\n\nAdd unit tests to AReaL following the project's testing conventions.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add tests?\"\n- User wants to increase test coverage\n- User needs to write tests for new functionality\n- User wants to understand AReaL testing patterns\n\n## Step-by-Step Guide\n\n### Step 1: Understand Test Types\n\nAReaL has two main test categories:\n\n| Test Type             | Purpose                            | Location Pattern                   | How It Runs                                |\n| --------------------- | ---------------------------------- | ---------------------------------- | ------------------------------------------ |\n| **Unit Tests**        | Test individual functions/modules  | `tests/test_<module>_<feature>.py` | Directly via pytest                        |\n| **Distributed Tests** | Test distributed/parallel behavior | `tests/torchrun/run_*.py`          | Via torchrun (called by pytest subprocess) |\n\n**Note**: All tests are invoked via pytest. Distributed tests use `torchrun` but are\nstill called from pytest test files.\n\n### Step 2: Create Test File Structure\n\nCreate test file with naming convention: `test_<module>_<feature>.py`\n\n```python\nimport pytest\nimport torch\n\n# Import the module to test\nfrom areal.dataset.gsm8k import get_gsm8k_sft_dataset\nfrom tests.utils import get_dataset_path  # Optional test utilities\n# For mocking tokenizer: from unittest.mock import MagicMock\n```\n\n### Step 3: Write Test Functions\n\nFollow Arrange-Act-Assert pattern:\n\n```python\ndef test_function_under_condition_returns_expected():\n    \"\"\"Test that function returns expected value under condition.\"\"\"\n    # Arrange\n    input_data = 5\n    expected_output = 10\n\n    # Act\n    result = function_under_test(input_data)\n\n    # Assert\n    assert result == expected_output\n```\n\n### Step 4: Add Pytest Markers and CI Strategy\n\nUse appropriate pytest markers:\n\n| Marker                                  | When to Use                                                  |\n| --------------------------------------- | ------------------------------------------------------------ |\n| `@pytest.mark.slow`                     | Test takes > 10 seconds (excluded from CI by default)        |\n| `@pytest.mark.ci`                       | Slow test that must run in CI (use with `@pytest.mark.slow`) |\n| `@pytest.mark.asyncio`                  | Async test functions                                         |\n| `@pytest.mark.skipif(cond, reason=...)` | Conditional skip                                             |\n| `@pytest.mark.parametrize(...)`         | Parameterized tests                                          |\n\n**CI Test Strategy**:\n\n- `@pytest.mark.slow`: Excluded from CI by default (CI runs `pytest -m \"not slow\"`)\n- `@pytest.mark.slow` + `@pytest.mark.ci`: Slow but must run in CI\n- No marker: Runs in CI (fast unit tests)\n\n```python\n@pytest.mark.asyncio\nasync def test_async_function():\n    result = await async_function()\n    assert result == expected\n\n@pytest.mark.skipif(not torch.cuda.is_available(), reason=\"CUDA not available\")\ndef test_gpu_feature():\n    tensor = torch.tensor([1, 2, 3], device=\"cuda\")\n    # ... assertions\n\n@pytest.mark.parametrize(\"batch_size\", [1, 4, 16])\ndef test_with_parameters(batch_size):\n    # Parameterized test\n\n@pytest.mark.slow\ndef test_slow_function():\n    # Excluded from CI by default\n\n@pytest.mark.slow\n@pytest.mark.ci\ndef test_slow_but_required_in_ci():\n    # Slow but must run in CI\n```\n\n### Step 5: Mock Distributed Environment\n\nFor unit tests that need distributed mocks:\n\n```python\nimport torch.distributed as dist\n\ndef test_distributed_function(monkeypatch):\n    monkeypatch.setattr(dist, \"get_rank\", lambda: 0)\n    monkeypatch.setattr(dist, \"get_world_size\", lambda: 2)\n    result = distributed_function()\n    assert result == expected\n```\n\n### Step 6: Handle GPU Dependencies\n\nAlways skip gracefully when GPU unavailable:\n\n```python\nCUDA_AVAILABLE = torch.cuda.is_available()\n\n@pytest.mark.skipif(not CUDA_AVAILABLE, reason=\"CUDA not available\")\ndef test_gpu_function():\n    tensor = torch.tensor([1, 2, 3], device=\"cuda\")\n    # ... assertions\n```\n\n## Key Requirements (Based on testing.md)\n\n### Mocking Distributed\n\n- Use `torch.distributed.fake_pg` for unit tests\n- Mock `dist.get_rank()` and `dist.get_world_size()` explicitly\n- Don't mock internals of FSDP/DTensor\n\n### GPU Test Constraints\n\n- **Always skip gracefully** when GPU unavailable\n- Clean up GPU memory: `torch.cuda.empty_cache()` in fixtures\n- Use smallest possible model/batch for unit tests\n\n### Assertions\n\n- Use `torch.testing.assert_close()` for tensor comparison\n- Specify `rtol`/`atol` explicitly for numerical tests\n- Avoid bare `assert tensor.equal()` - no useful error message\n\n## Reference Implementations\n\n| Test File                        | Description                            | Key Patterns                                      |\n| -------------------------------- | -------------------------------------- | ------------------------------------------------- |\n| `tests/test_utils.py`            | Utility function tests                 | Fixtures, parametrized tests                      |\n| `tests/test_examples.py`         | Integration tests with dataset loading | Dataset path resolution, success pattern matching |\n| `tests/test_fsdp_engine_nccl.py` | Distributed tests                      | Torchrun integration                              |\n\n## Common Mistakes\n\n- **Missing test file registration**: Ensure file follows `test_*.py` naming\n- **GPU dependency without skip**: Always use `@pytest.mark.skipif` for GPU tests\n- **Incorrect tensor comparisons**: Use `torch.testing.assert_close()` not\n  `assert tensor.equal()`\n- **Memory leaks in GPU tests**: Clean up with `torch.cuda.empty_cache()`\n- **Mocking too much**: Don't mock FSDP/DTensor internals\n- **Unclear test names**: Follow `test_<what>_<condition>_<expected>` pattern\n- **No docstrings**: Add descriptive docstrings to test functions\n\n## Integration with Other Skills\n\nThis skill complements other AReaL development skills:\n\n- **After `/add-dataset`**: Add tests for new dataset loaders\n- **After `/add-workflow`**: Add tests for new workflows\n- **After `/add-reward`**: Add tests for new reward functions\n- **With expert agents**: Reference this skill when planning test implementation\n\n## Running Tests\n\n```bash\n# First check GPU availability (many tests require GPU)\npython -c \"import torch; print('GPU available:', torch.cuda.is_available())\"\n\n# Run specific test file\nuv run pytest tests/test_<name>.py\n\n# Skip slow tests (CI default)\nuv run pytest -m \"not slow\"\n\n# Run with verbose output\nuv run pytest -v\n\n# Run distributed tests (requires torchrun and multi-GPU)\n# Note: Usually invoked via pytest test files\ntorchrun --nproc_per_node=2 tests/torchrun/run_<test>.py\n```\n\n<!-- File: .agents/skills/add-workflow/SKILL.md (Tokens: ~1002 | Category: anthropic-skill) -->\n---\nname: add-workflow\ndescription: Guide for adding a new RolloutWorkflow to AReaL. Use when user wants to create a new workflow.\n---\n\n# Add Workflow\n\nAdd a new RolloutWorkflow implementation to AReaL.\n\n## When to Use\n\nThis skill is triggered when:\n\n- User asks \"how do I add a workflow?\"\n- User wants to create a new RolloutWorkflow\n- User mentions implementing a custom rollout\n\n## Prerequisites\n\nBefore starting, ensure you understand:\n\n- The workflow's purpose and requirements\n- Input/output data format\n- Reward function to use\n\n## Step-by-Step Guide\n\n### Step 1: Create Workflow File\n\nCreate `areal/workflow/<name>.py`:\n\n```python\nimport uuid\nfrom typing import Any, Callable\n\nimport torch\n\nfrom areal.api.cli_args import GenerationHyperparameters\nfrom areal.api.engine_api import InferenceEngine\nfrom areal.api.io_struct import ModelRequest, ModelResponse\nfrom areal.api.reward_api import AsyncRewardWrapper\nfrom areal.api.workflow_api import RolloutWorkflow\nfrom areal.utils import logging\n\nlogger = logging.getLogger(\"MyWorkflow\")\n\n\nclass MyWorkflow(RolloutWorkflow):\n    \"\"\"Description of your workflow.\"\"\"\n\n    def __init__(\n        self,\n        gconfig: GenerationHyperparameters,\n        tokenizer,\n        reward_fn: Callable,\n    ):\n        self.gconfig = gconfig.new_with_stop_and_pad_token_ids(tokenizer)\n        self.tokenizer = tokenizer\n        self.async_reward_fn = AsyncRewardWrapper(reward_fn)\n\n    async def arun_episode(\n        self,\n        engine: InferenceEngine,\n        data: dict[str, Any],\n    ) -> dict[str, Any] | None | dict[str, InteractionWithTokenLogpReward]:\n        \"\"\"Run a single episode. MUST be async and non-blocking.\"\"\"\n\n        # 1. Prepare input_ids from data\n        input_ids = self.tokenizer.apply_chat_template(\n            data[\"messages\"],\n            tokenize=True,\n            add_generation_prompt=True,\n        )\n\n        # 2. Build ModelRequest\n        req = ModelRequest(\n            rid=uuid.uuid4().hex,\n            input_ids=list(input_ids),\n            gconfig=self.gconfig.new(n_samples=1),\n            tokenizer=self.tokenizer,\n        )\n\n        # 3. Generate completion (async)\n        resp: ModelResponse = await engine.agenerate(req)\n\n        # 4. Compute reward (async)\n        prompt_str = self.tokenizer.decode(input_ids)\n        completion_str = self.tokenizer.decode(resp.output_tokens)\n        reward = await self.async_reward_fn(\n            prompt_str,\n            completion_str,\n            resp.input_tokens,\n            resp.output_tokens,\n            **data,\n        )\n\n        # 5. Return results in expected format\n        return {\n            \"input_ids\": torch.tensor(resp.input_tokens),\n            \"output_ids\": torch.tensor(resp.output_tokens),\n            \"reward\": torch.tensor(reward),\n        }\n```\n\n### Step 2: Register in __init__.py\n\nAdd to `areal/workflow/__init__.py`:\n\n```python\nfrom areal.workflow.<name> import MyWorkflow\n\n__all__ = [\n    # ... existing exports\n    \"MyWorkflow\",\n]\n```\n\n### Step 3: Update Entry Script\n\nUpdate your training script to use the new workflow:\n\n```python\ntrainer.train(\n    workflow=\"areal.workflow.<name>.MyWorkflow\",\n    # ... other args\n)\n```\n\n### Step 4: Add Tests\n\nCreate `tests/test_<name>_workflow.py`:\n\n```python\nimport pytest\nfrom areal.workflow.<name> import MyWorkflow\n\n@pytest.mark.asyncio\nasync def test_workflow_basic():\n    # Test basic functionality\n    pass\n```\n\n## Reference Implementations\n\n| Workflow           | File                            | Description                |\n| ------------------ | ------------------------------- | -------------------------- |\n| MultiTurnWorkflow  | `areal/workflow/multi_turn.py`  | Multi-turn conversation    |\n| RLVRWorkflow       | `areal/workflow/rlvr.py`        | RL with verifiable rewards |\n| VisionRLVRWorkflow | `areal/workflow/vision_rlvr.py` | Vision + RLVR              |\n\n## Key Requirements\n\n1. **Async**: `arun_episode` must be `async def` and non-blocking\n1. **No sync I/O**: Use `aiofiles` for file operations\n1. **Wrap rewards**: Use `AsyncRewardWrapper` for reward functions\n1. **Tensor format**: Output tensors should be `[batch, seq_len, ...]`\n1. **Use helpers**: `concat_padded_tensors` for combining outputs\n\n## Common Mistakes\n\n- Using `open()` instead of `aiofiles.open()`\n- Forgetting to `await` async calls\n- Not wrapping reward function with `AsyncRewardWrapper`\n- Wrong tensor shape conventions\n\n<!-- File: .agents/skills/commit-conventions/SKILL.md (Tokens: ~1250 | Category: anthropic-skill) -->\n---\nname: commit-conventions\ndescription: AReaL commit message conventions. MUST load on every git commit -- provides Conventional Commits format with scope inference from file paths.\n---\n\n# Commit Conventions\n\nCommit message conventions and scope inference rules for the AReaL repository.\n\n## When to Use\n\n**ALWAYS load this skill when making any git commit in AReaL.** This includes:\n\n- Direct commits (`git commit`)\n- Commits during PR creation (`$create-pr` / `/create-pr`)\n- Commits delegated to sub-agents with this skill loaded\n- Any agent workflow that produces a commit\n\n## Commit Message Format\n\n```\n<type>(<scope>): <subject>\n\n<body>\n\n[Optional sections:]\nKey changes:\n- change 1\n- change 2\n\nRefs: #123, #456\n```\n\n## Type Selection\n\n| Type       | When to Use                      |\n| ---------- | -------------------------------- |\n| `feat`     | New feature or capability        |\n| `fix`      | Bug fix                          |\n| `docs`     | Documentation only               |\n| `gov`      | Governance or maintainer changes |\n| `style`    | Formatting/style-only changes    |\n| `refactor` | Code change without feature/fix  |\n| `perf`     | Performance improvement          |\n| `test`     | Adding or fixing tests           |\n| `build`    | Build system or dependencies     |\n| `ci`       | CI pipeline or workflow changes  |\n| `chore`    | Build, deps, config changes      |\n| `revert`   | Revert a previous commit         |\n\n## Scope Inference\n\nInfer scope from the **primary** changed file paths:\n\n| File Path Pattern                                            | Scope                          |\n| ------------------------------------------------------------ | ------------------------------ |\n| `areal/workflow/`                                            | `workflow`                     |\n| `areal/engine/`                                              | `engine`                       |\n| `areal/reward/`                                              | `reward`                       |\n| `areal/dataset/`                                             | `dataset`                      |\n| `areal/api/`                                                 | `api`                          |\n| `areal/utils/`                                               | `utils`                        |\n| `areal/infra/`                                               | `infra`                        |\n| `areal/trainer/`                                             | `trainer`                      |\n| `areal/models/`                                              | `models`                       |\n| `areal/experimental/`                                        | `experimental`                 |\n| `docs/`                                                      | `docs`                         |\n| `examples/`                                                  | `examples`                     |\n| `AGENTS.md`, `.agents/`, `.claude/`, `.codex/`, `.opencode/` | `agents`                       |\n| Multiple areas                                               | Omit scope or use broader term |\n\n## Rules\n\n- **Subject**: imperative mood, ~50-72 chars, no trailing period\n- **Body**: explain \"why\" not \"what\", wrap at 72 chars\n- **Key changes**: bullet list of main modifications (for complex commits with 3+ files)\n- **Refs**: reference issues/PRs if applicable\n\n## Examples\n\n**Single file fix:**\n\n```\nfix(reward): handle empty completion in gsm8k\n\nReturn 0 reward instead of raising exception when\ncompletion string is empty after extraction.\n```\n\n**Multi-file feature:**\n\n```\nfeat(engine): add CPU offload support to ArchonEngine\n\nEnable torch_memory_saver for model offloading during\nrollout phase to reduce GPU memory pressure.\n\nKey changes:\n- Add offload/onload methods to ArchonEngine\n- Integrate with weight update flow\n- Handle ROCm compatibility\n```\n\n**Docs only:**\n\n```\ndocs: update algorithm comparison table\n\nAdd SAPO and GSPO to the algorithm family documentation\nwith configuration examples.\n```\n\n**Agent/tooling changes:**\n\n```\nchore(agents): port review-pr command to OpenCode\n\nAdd OpenCode-native commands with task() category\ndelegation instead of hardcoded model names.\n\nKey changes:\n- Create .opencode/command/ with review-pr, create-pr\n- Replace hardcoded model routing with platform-native review routing\n- Add expert subagent consultation patterns\n```\n\n**Governance/maintainer changes:**\n\n```\ngov(agents): add maintainer ownership for service modules\n\nUpdate CODEOWNERS and maintainer references to reflect\ncurrent governance responsibilities.\n\nKey changes:\n- Add maintainers for agent_service and infra ownership\n- Align governance docs with updated reviewer responsibilities\n```\n\n______________________________________________________________________\n\n<!--\n================================================================================\n                            MAINTAINER GUIDE\n================================================================================\n\nCanonical location: .agents/skills/commit-conventions/SKILL.md\nMirrors: .opencode/skills/commit-conventions/SKILL.md, .claude/skills/commit-conventions/SKILL.md\nInvocation: Automatically loaded on every git commit (all platforms)\n\n## Purpose\n\nProvides Conventional Commits format with AReaL-specific scope inference\nfrom file paths. Unlike other skills, this one is NOT user-triggered --\nit is loaded by the system/agent on every commit operation.\n\n## How to Update\n\n### When New Modules Are Added\n1. Add the file path pattern and scope to the \"Scope Inference\" table\n2. Keep table sorted by areal/ subpackages first, then top-level dirs\n\n### When Commit Types Change\n1. Update the \"Type Selection\" table\n2. Add/update examples to illustrate the new type\n\n### When Adding Examples\n1. Each example should demonstrate a distinct commit pattern\n2. Keep examples realistic -- use actual AReaL module names\n3. Show both subject-only and subject+body+key-changes variants\n\n### Important Design Decisions\n- This skill is ALWAYS loaded (not optional) -- keep it concise to\n  minimize token overhead on every commit\n- Scope inference is path-based, not content-based -- simpler and\n  more deterministic\n- \"Multiple areas\" -> omit scope rather than invent a new one\n\n================================================================================\n-->\n\n<!-- File: .agents/skills/create-pr/SKILL.md (Tokens: ~542 | Category: anthropic-skill) -->\n---\nname: create-pr\ndescription: Rebase the current branch onto the latest base branch, squash local commits, generate a Conventional Commit message, and create or update the GitHub pull request.\n---\n\n# Create Pull Request\n\nUse this skill when the user asks to create or update a PR for the current branch.\n\n## Inputs\n\n- Optional `--draft`\n- Optional `--base <branch>` (default: `main`)\n\n## Preconditions\n\n1. Verify the current branch is not `main` or `master`.\n1. Check for uncommitted changes with `git status --short`.\n1. Ensure `gh` is available.\n1. If there are uncommitted changes, stop and ask the user whether to commit or stash\n   them first.\n\n## Workflow\n\n### Step 1: Check for an existing PR\n\n- Run `gh pr view --json number,title,url,state,isDraft`.\n- If a PR already exists, tell the user before rewriting history or force-pushing.\n\n### Step 2: Fetch and rebase\n\n```bash\ngit fetch origin <base>\ngit rebase origin/<base>\n```\n\n- If rebase conflicts occur, abort the rebase and stop.\n- Tell the user which files conflicted and ask them to resolve manually.\n\n### Step 3: Squash into one commit\n\n```bash\ngit reset --soft origin/<base>\n```\n\n- Load the `commit-conventions` skill before generating the commit message.\n- Infer `type` and `scope` from the staged diff.\n- Keep the commit subject imperative and under about 72 characters.\n\n### Step 4: Generate PR title and body\n\n- Use the squashed commit message style for the PR title.\n- Follow the repository PR template at `.github/PULL_REQUEST_TEMPLATE.md`.\n- Summarize user-facing changes, risk areas, test commands run, and skipped suites with\n  reasons.\n\n### Step 5: Push and create or update the PR\n\n- Push the branch.\n- If history was rewritten, confirm before force-pushing.\n- Create or update the PR with `gh pr create` or `gh pr edit`.\n- Respect `--draft` when requested.\n\n## Guardrails\n\n- Never create a PR from `main` or `master`.\n- Never silently force-push over an existing PR branch.\n- Never bypass `commit-conventions` for the squashed commit.\n- If `gh` authentication or remote permissions fail, stop and report the exact blocker.\n\n## Output\n\nReport:\n\n- Base branch used\n- Final commit message\n- PR title\n- PR URL, if creation or update succeeded\n- Any steps that were skipped or require user follow-up\n\n<!-- File: .agents/skills/debug-distributed/SKILL.md (Tokens: ~1524 | Category: anthropic-skill) -->\n---\nname: debug-distributed\ndescription: Guide for debugging distributed training issues in AReaL. Use when user encounters hangs, wrong results, OOM, or communication errors.\n---\n\n# Debug Distributed Training\n\nDebugging guide for distributed training issues in AReaL (FSDP2, TP, CP, EP).\n\n## When to Use\n\nThis skill is triggered when:\n\n- Training hangs or deadlocks\n- Results differ across ranks or are numerically wrong\n- OOM errors in distributed settings\n- NCCL/communication errors or device mesh issues\n\n## Debugging Principles\n\n### Minimal Reproduction\n\n**Always follow the minimal demo principle**: Reproduce with the least amount of code to\nnarrow down the issue faster.\n\n```python\n# Bad: Debug in full training loop\n# Good: Create minimal script\nimport torch\nimport torch.distributed as dist\n\ndist.init_process_group(\"nccl\")\nrank = dist.get_rank()\n\n# Reproduce the exact operation that fails\ntensor = torch.ones(10).cuda()\ndist.all_reduce(tensor)  # <-- Isolate the failing op\nprint(f\"Rank {rank}: {tensor}\")\n```\n\n**Reduction strategy:**\n\n1. Remove unrelated model components\n1. Use small tensor sizes\n1. Reduce world_size to minimum (e.g., 2 GPUs)\n1. Remove torch.compile if possible\n1. Disable activation checkpointing\n\n## Step-by-Step Debugging Guide\n\n### 1. Hang Debugging (Deadlocks, Synchronization)\n\n**Environment Variables for Debugging**:\n\n```bash\n# Full debug logging\nexport TORCH_DISTRIBUTED_DEBUG=DETAIL\nexport NCCL_DEBUG=INFO\nexport NCCL_DEBUG_SUBSYS=ALL\n\n# torch.compile debugging\nexport TORCH_LOGS=\"+dynamo,recompiles\"\nexport TORCHDYNAMO_VERBOSE=1\n```\n\n**Dump Call Stack with py-spy** (for hung processes):\n\n```bash\n# Find process IDs\nps aux | grep python\n\n# Dump call stack of specific rank\npy-spy dump --pid <PID>\n\n# Record flame graph for performance analysis\npy-spy record -o profile.svg --pid <PID> --duration 30\n```\n\n**Common Causes**:\n\n1. **Mismatched Collectives**: One rank calls `all_reduce`, another doesn't.\n1. **Wrong Process Group**: Using wrong group for collective.\n1. **Tensor Shape Mismatch**: Different shapes across ranks.\n\n**Debug Steps**:\n\n```python\n# Verify group membership\nmesh = parallel_dims.get_mesh(\"dp_shard_cp\")\ngroup = mesh.get_group()\nprint(f\"Rank {dist.get_rank()}: group size = {dist.get_world_size(group)}\")\n\n# Print shapes on all ranks\nprint(f\"Rank {dist.get_rank()}: tensor.shape = {tensor.shape}\")\ndist.barrier()\n```\n\n**Timeout Adjustment** (for debugging only):\n\n```python\nfrom areal.engine.core.distributed import patch_dist_group_timeout\nfrom datetime import timedelta\npatch_dist_group_timeout(timedelta(minutes=30))\n```\n\n### 2. Wrong Results (Gradient, Reduction Issues)\n\n**Check DTensor Placements**:\n\n```python\nfrom torch.distributed.tensor import DTensor\nif isinstance(param, DTensor):\n    print(f\"Param {name}: placements={param.placements}, mesh={param.device_mesh}\")\n```\n\n**Verify Gradient Reduction**:\n\n```python\nfor name, param in model.named_parameters():\n    if param.grad is not None:\n        print(f\"Rank {dist.get_rank()}: {name} grad_sum = {param.grad.sum().item()}\")\n```\n\n### 3. OOM Issues (Memory, Sharding)\n\n**Check Memory Usage**:\n\n```python\nprint(f\"Rank {dist.get_rank()}: \"\n      f\"allocated={torch.cuda.memory_allocated()/1e9:.2f}GB, \"\n      f\"reserved={torch.cuda.memory_reserved()/1e9:.2f}GB\")\n```\n\n**Check FSDP Coverage**:\n\n```python\nfor name, param in model.named_parameters():\n    is_dtensor = isinstance(param, DTensor)\n    print(f\"{name}: is_dtensor={is_dtensor}, shape={param.shape}\")\n```\n\n### 4. Communication Errors\n\n| Error                     | Cause                | Solution                           |\n| ------------------------- | -------------------- | ---------------------------------- |\n| `NCCL WARN Cuda failure`  | GPU communication    | Check NCCL version, GPU topology   |\n| `RuntimeError: Timed out` | Rank synchronization | Increase timeout, check code paths |\n| `Invalid device mesh`     | Mesh configuration   | Verify world_size = dp * tp * cp   |\n\n## Debugging Tools\n\n### Environment Variables Reference\n\n| Variable                          | Purpose                                |\n| --------------------------------- | -------------------------------------- |\n| `TORCH_DISTRIBUTED_DEBUG=DETAIL`  | Detailed distributed logging           |\n| `NCCL_DEBUG=INFO`                 | NCCL communication logging             |\n| `NCCL_DEBUG_SUBSYS=ALL`           | All NCCL subsystems                    |\n| `TORCH_LOGS=\"+dynamo,recompiles\"` | torch.compile logging                  |\n| `TORCHDYNAMO_VERBOSE=1`           | Dynamo verbose output                  |\n| `CUDA_LAUNCH_BLOCKING=1`          | Synchronous CUDA (slow, for debugging) |\n\n### py-spy for Call Stack Analysis\n\n```bash\n# Install\npip install py-spy\n\n# Dump call stack of hung process\npy-spy dump --pid <PID>\n\n# Dump all Python processes\npgrep -f python | xargs -I {} py-spy dump --pid {}\n\n# Record flame graph\npy-spy record -o profile.svg --pid <PID> --duration 30\n```\n\n### Rank-Conditional Printing\n\n```python\ndef print_all_ranks(msg):\n    for r in range(dist.get_world_size()):\n        if dist.get_rank() == r:\n            print(f\"[Rank {r}] {msg}\")\n        dist.barrier()\n```\n\n### Check Device Mesh\n\n```python\ndef debug_mesh(parallel_dims):\n    mesh = parallel_dims.world_mesh\n    for dim_name in mesh.mesh_dim_names:\n        submesh = parallel_dims.get_mesh(dim_name)\n        if submesh:\n            print(f\"Rank {dist.get_rank()}: {dim_name} size={submesh.size()}\")\n```\n\n### Validate Tensor Consistency\n\n```python\ndef check_tensor_consistency(tensor, name, group=None):\n    local_sum = tensor.sum().item()\n    tensor_sums = [None] * dist.get_world_size(group)\n    dist.all_gather_object(tensor_sums, local_sum, group=group)\n    if dist.get_rank() == 0 and len(set(tensor_sums)) > 1:\n        print(f\"WARNING: {name} inconsistent: {tensor_sums}\")\n```\n\n## Key Files Reference\n\n| Component       | File                                                          |\n| --------------- | ------------------------------------------------------------- |\n| Parallel Dims   | `areal/experimental/models/archon/parallel_dims.py`           |\n| Expert Parallel | `areal/experimental/models/archon/expert_parallel.py`         |\n| Ulysses (CP)    | `areal/experimental/models/archon/ulysses.py`                 |\n| FSDP/TP Apply   | `areal/experimental/models/archon/qwen2/infra/parallelize.py` |\n\n<!-- File: .agents/skills/review-pr/MIGRATION.md (Tokens: ~3277 | Category: anthropic-skill) -->\n# `/review-pr` Refactor & Update Plan (Codex / OpenCode / Claude)\n\nLast updated: 2026-03-31 Owner: AI workflow maintainers Scope:\n`.agents/skills/review-pr/*`, `.opencode/{command,data}/review-pr*`,\n`.claude/{commands,data}/review-pr*`\n\n______________________________________________________________________\n\n## 1. Goals and Non-Goals\n\n### Goals\n\n1. Rebuild `/review-pr` taxonomy and templates for better coverage of new AReaL modules.\n1. Keep template count manageable while preserving depth for high-risk PRs.\n1. Eliminate cross-platform drift through a single semantic source of truth.\n1. Reduce false positives by prioritizing path-based triggers.\n\n### Non-Goals\n\n1. Changing `/review-pr` into a file-mutating workflow (must remain read-only).\n1. Adding one-off niche templates for every micro-feature.\n1. Rewriting all platform command orchestration logic in one iteration.\n\n______________________________________________________________________\n\n## 2. Current State Summary (Problem Statement)\n\n### What works\n\n1. Existing workflow phases are clear (context -> analysis -> planning -> delegated\n   review -> summary).\n1. Platform-specific command wrappers already exist and are functional.\n1. Risk-first review behavior (CRITICAL/HIGH/MEDIUM/LOW) is already enforced.\n\n### What is broken or stale\n\n1. Domain/signal matrix lags behind repository evolution (`tree_attn`, `vllm_ext`,\n   service stack, `archon_weight_sync`).\n1. Taxonomy is overly fragmented in some areas and missing in others.\n1. Three-platform data files are near-duplicates and easy to drift.\n1. Trigger rules rely heavily on generic keywords in places where path triggers are\n   safer.\n\n______________________________________________________________________\n\n## 3. Target Information Architecture\n\nUse a **two-layer taxonomy**:\n\n- **L1 Domain (12 domains)**: template-level planning unit.\n- **L2 Signals (2-9 per domain)**: detection and checklist specialization unit.\n\nThis avoids both extremes: too many tiny templates or too coarse single-pass review.\n\n### 3.1 L1 Domains and L2 Signals\n\n1. **Distributed Runtime**\n   - L2: `archon_core`, `archon_parallel`, `process_group`, `fsdp_core`,\n     `megatron_core`, `collectives`, `mesh_dtensor`, `activation_checkpointing`,\n     `weight_sync`\n1. **Model Compute & Attention**\n   - L2: `tree_attn`, `sdpa_varlen`, `sp_cp_attention_mask`, `triton_kernel`,\n     `archon_model_family`, `archon_attention_stack`, `archon_moe_modeling`\n1. **Inference Backend & Serving**\n   - L2: `vllm_ext`, `remote_inference_backend`, `request_lifecycle`\n1. **Service Orchestration**\n   - L2: `service_routing_dataflow`, `session_consistency`\n1. **Workflow & Trainer Contract**\n   - L2: `workflow_engine_boundary`, `dataset_surface`, `async_contract`,\n     `weight_version_contract`\n1. **API & Config Compatibility**\n   - L2: `dataclass_schema`, `cli_compat`, `backward_compat`,\n     `project_dependency_config`\n1. **Numerics & Tensor Semantics**\n   - L2: `shape_dtype`, `numerical_stability`, `reward_surface`, `compile_dynamo`,\n     `mixed_precision_fp8`\n1. **Checkpoint & Recovery**\n   - L2: `dcp_consistency`, `optimizer_state`, `resume_compat`\n1. **Launcher & Infrastructure**\n   - L2: `launcher_resource_match`, `scheduler_contract`, `rpc_transport`,\n     `runtime_image_config`\n1. **Low-Risk Hygiene**\n   - L2: `tests_docs_config`, `logging_import_security`, `project_docs_metadata`\n1. **Harness & Agent Infrastructure**\n   - L2: `skill_definition`, `platform_command_data`, `agent_registry_config`\n1. **CI/CD & Release Automation**\n   - L2: `github_workflow_jobs`, `runner_provisioning`, `release_delivery`\n\n______________________________________________________________________\n\n## 4. Detection Strategy (Path-first)\n\n### 4.1 Trigger priority\n\n1. **Path trigger (primary)**\n1. **Keyword trigger (secondary, only to refine existing path hits)**\n1. **Linkage trigger (auto-add dependent checks)**\n\n### 4.2 Canonical path mapping (v1)\n\n1. `areal/models/tree_attn/**` -> Model Compute & Attention (HIGH/MEDIUM by touched\n   files)\n1. `areal/engine/vllm_ext/**` -> Inference Backend & Serving (HIGH)\n1. `areal/experimental/models/archon/**` + `areal/experimental/engine/archon_engine.py`\n   \\+ `areal/experimental/engine/archon_checkpoint.py` -> Distributed Runtime + Model\n   Compute & Attention (CRITICAL/HIGH)\n1. `areal/v2/agent_service/**` -> Service Orchestration (HIGH)\n1. `areal/v2/inference_service/**` -> Service Orchestration (HIGH)\n1. `areal/experimental/engine/archon_weight_sync.py` -> Distributed Runtime (CRITICAL)\n1. `areal/infra/rpc/**` -> Launcher & Infrastructure + Distributed Runtime (HIGH)\n1. `areal/workflow/**`, `areal/trainer/**` -> Workflow & Trainer Contract (HIGH/MEDIUM)\n1. `areal/api/**` -> API & Config Compatibility (MEDIUM)\n1. `areal/utils/{saver.py,recover.py,async_checkpoint.py}` + `*checkpoint*.py` ->\n   Checkpoint & Recovery (CRITICAL/HIGH)\n1. `areal/experimental/models/archon/activation_checkpoint.py` -> Distributed Runtime\n   (MEDIUM/HIGH)\n1. `areal/experimental/models/archon/compile.py` -> Numerics & Tensor Semantics +\n   Distributed Runtime (MEDIUM)\n1. `pyproject.toml`, `uv.lock` -> API & Config Compatibility (MEDIUM)\n1. `Dockerfile`, `.dockerignore` -> Launcher & Infrastructure (HIGH)\n1. `.agents/**`, `.claude/**`, `.opencode/**`, `.codex/**`, `AGENTS.md`, `CLAUDE.md` ->\n   Harness & Agent Infrastructure (MEDIUM/HIGH)\n1. `.github/workflows/**` -> CI/CD & Release Automation (HIGH/CRITICAL)\n1. `docs/build_all.sh`, `docs/generate_cli_docs.py`, `.github/PULL_REQUEST_TEMPLATE.md`,\n   `README.md`, `CONTRIBUTING.md` -> Low-Risk Hygiene (LOW/MEDIUM)\n\n### 4.3 Must-not-regress coverage (from current review-pr)\n\nThe migration must preserve the existing high-risk framework coverage already present\ntoday:\n\n1. `areal/experimental/models/archon/**` + `areal/experimental/engine/archon_engine.py`\n   - `areal/experimental/engine/archon_checkpoint.py` -> Distributed Runtime + Model\n     Compute & Attention\n1. `areal/engine/fsdp_utils/**` + `areal/engine/fsdp_engine.py` -> Distributed Runtime\n1. `areal/engine/megatron_engine.py` + `areal/engine/megatron_utils/**` -> Distributed\n   Runtime\n1. `areal/trainer/**` -> Workflow & Trainer Contract\n1. `areal/reward/**` -> Numerics & Tensor Semantics + Workflow & Trainer Contract\n1. `areal/dataset/**` -> Workflow & Trainer Contract + API & Config Compatibility\n\n### 4.4 Noise control rules\n\n1. No repo-wide standalone triggering for `current_platform`, `RTensor`, or `fp8`.\n1. These keywords only refine severity/checklists after domain has been path-selected.\n1. Cap task fan-out: max one primary template per domain, plus one general logic pass.\n\n______________________________________________________________________\n\n## 5. Template Strategy\n\nMaintain **12 domain templates** (one per L1), each with signal-specific checklists,\nplus **1 universal logic pass**.\n\n### Template inventory (v1)\n\n1. Distributed Runtime Review\n1. Model Compute & Attention Review\n1. Inference Backend & Serving Review\n1. Service Orchestration Review\n1. Workflow & Trainer Contract Review\n1. API & Config Compatibility Review\n1. Numerics & Tensor Semantics Review\n1. Checkpoint & Recovery Review\n1. Launcher & Infrastructure Review\n1. Low-Risk Hygiene Review\n1. Harness & Agent Infrastructure Review\n1. CI/CD & Release Automation Review\n\n### Mandatory universal pass\n\n- Always run one lightweight **General Logic & Boundary** pass for non-doc PRs.\n\n______________________________________________________________________\n\n## 6. Severity Mapping\n\n1. **CRITICAL**: distributed invariants, checkpoint correctness, core weight sync\n   safety.\n1. **HIGH**: service orchestration, inference backend runtime, trainer/workflow\n   contracts.\n1. **MEDIUM**: API compatibility, numerics/tensor semantics in bounded scope.\n1. **LOW**: docs/tests/config-only hygiene.\n\nRule: domain default severity can be escalated by L2 signal combinations (e.g.,\n`mesh_dtensor` + `weight_sync`).\n\n______________________________________________________________________\n\n## 7. Cross-Domain Linkage Rules (Auto-appended checks)\n\n1. `tree_attn` -> also append Numerics & Tensor Semantics checks.\n1. `archon_core` or `archon_parallel` -> also append Model Compute & Attention checks.\n1. `archon_model_family` or `archon_moe_modeling` -> also append Numerics & Tensor\n   Semantics checks.\n1. `reward_surface` -> also append Workflow & Trainer Contract checks.\n1. `compile_dynamo` -> also append Distributed Runtime checks.\n1. `vllm_ext` -> also append Launcher & Infrastructure checks.\n1. Service Orchestration changes -> also append Workflow & Trainer async-contract\n   checks.\n1. `archon_weight_sync` -> also append DTensor + process-group + checkpoint interaction\n   checks.\n1. RPC transport changes -> also append Distributed Runtime synchronization checks.\n1. `mixed_precision_fp8` + Distributed Runtime -> also append mesh + weight-sync\n   compatibility checks.\n1. `runtime_image_config` -> also append Inference Backend & Serving checks.\n1. `project_dependency_config` -> also append API & Config Compatibility checks.\n1. `github_workflow_jobs` or `release_delivery` -> also append Launcher & Infrastructure\n   checks.\n1. `skill_definition` or `platform_command_data` -> also append Low-Risk Hygiene checks.\n\n______________________________________________________________________\n\n## 8. Three-Platform Synchronization Model\n\n## 8.1 Source-of-truth\n\nSemantic content is authored only in:\n\n1. `.agents/skills/review-pr/references/review-pr-domains-and-signals.md`\n1. `.agents/skills/review-pr/references/review-pr-templates.md`\n\nCanonical semantic scope includes:\n\n1. taxonomy (L1/L2), path/linkage rules, and severity rules\n1. checklist bodies and change-analysis vocabulary\n\nWrapper-specific scope (non-canonical) includes:\n\n1. command syntax and frontmatter fields\n1. shell snippets, import/include syntax, and runtime-specific routing policies\n1. OpenCode-only and Claude-only execution options\n\n## 8.2 Derived outputs\n\nGenerated/derived files:\n\n1. `.opencode/data/review-pr-domains-and-signals.md`\n1. `.opencode/data/review-pr-templates.md`\n1. `.claude/data/review-pr-domains-and-signals.md`\n1. `.claude/data/review-pr-templates.md`\n\n## 8.3 Mechanical sync (definition)\n\n\"Mechanical sync\" means deterministic conversion, not manual copy/paste:\n\n1. Read canonical `.agents` files.\n1. Emit OpenCode and Claude data copies with the same generic review-depth vocabulary\n   (`comprehensive`, `targeted`, `basic`).\n1. Keep all runtime routing and platform execution choices in the wrapper command files\n   only.\n1. Preserve section order and checklist content exactly.\n\n______________________________________________________________________\n\n## 9. Implementation Plan\n\n### Phase A (Foundation)\n\n1. Replace the old review-pr taxonomy with L1/L2 domain/signal references in canonical\n   `.agents` references.\n1. Rebuild template file into 12 domain templates + universal logic pass.\n1. Add linkage rules and fan-out caps.\n\n### Phase B (Platform sync)\n\n1. Add sync script at `.agents/skills/review-pr/sync_review_pr_refs.py`.\n1. Regenerate `.opencode/data/*` and `.claude/data/*`.\n1. Add CI check: fail if derived files differ from regeneration output.\n\n### Phase C (Command layer alignment)\n\n1. Update `.opencode/command/review-pr.md` wording to reflect new domains/signals.\n1. Update `.claude/commands/review-pr.md` wording similarly.\n1. Keep orchestration differences platform-specific (task categories vs model routing).\n\n### Phase D (Validation)\n\n1. Run **classification lane** with `/review-pr --quick` against representative PRs\n   from:\n   - `tree_attn`\n   - `vllm_ext`\n   - `agent_service/inference_service`\n   - `archon_weight_sync`\n1. Measure in classification lane:\n   - expected detected domains/signals\n   - expected severity\n   - false positive rate\n   - missing high-risk findings\n1. Run **delegation lane** with full `/review-pr` (non-quick) on the same fixtures.\n1. Measure in delegation lane:\n   - false positive rate\n   - missing high-risk findings\n   - number of spawned review tasks per PR\n\n______________________________________________________________________\n\n## 10. Plan Review (Critical self-review)\n\n### Strengths\n\n1. Improves coverage for newly introduced high-risk modules.\n1. Shrinks maintenance overhead by moving to domain templates.\n1. Prevents cross-platform drift via deterministic derivation.\n\n### Risks\n\n1. Over-broad domains can dilute checklist quality.\n1. First migration may temporarily shift severity distributions.\n1. Command docs can lag behind taxonomy if sync discipline is not enforced.\n\n### Mitigations\n\n1. Keep L2 signals explicit and path-anchored.\n1. Use golden PR cases for pre/post comparison.\n1. Add CI consistency gate for derived platform data files.\n1. Require wrapper wording updates in the same migration PR as taxonomy changes.\n\n______________________________________________________________________\n\n## 11. Acceptance Criteria\n\n1. Canonical references express all 12 domains + L2 signals and linkage rules.\n1. Derived OpenCode/Claude data files regenerate with zero manual edits.\n1. No mixed old/new label vocabulary remains after regeneration.\n1. Representative PRs trigger expected domains:\n   - `tree_attn` PR -> Model Compute & Attention (+ Numerics linkage)\n   - `vllm_ext` PR -> Inference Backend & Serving (+ Launcher linkage)\n   - service-stack PR -> Service Orchestration (+ Workflow linkage)\n   - `archon_weight_sync` PR -> Distributed Runtime (CRITICAL)\n1. Legacy high-risk coverage remains intact (Archon/FSDP/Megatron/Reward/Dataset).\n1. Task fan-out remains bounded (no uncontrolled template explosion).\n\n______________________________________________________________________\n\n## 12. Out of Scope for v1 (defer)\n\n1. Fully unifying command orchestration syntax across OpenCode and Claude.\n1. Creating standalone domain types for every backend keyword.\n1. Automatically posting review comments to GitHub.\n\n______________________________________________________________________\n\n## 13. Recommended Next Action\n\nLand **Phase A + B + C** in one migration PR (taxonomy + sync tooling + wrapper\nalignment), then run **Phase D** validation immediately using fixed fixtures.\n\n<!-- File: .agents/skills/review-pr/references/review-pr-domains-and-signals.md (Tokens: ~4057 | Category: anthropic-skill) -->\n# PR Review: Domain & Signal Detection Reference\n\nThis file contains the canonical change-domain and signal detection tables for PR\nreview. Referenced by: `.agents/skills/review-pr/SKILL.md`\n\n______________________________________________________________________\n\n## Severity-to-Review-Depth Mapping\n\n- **CRITICAL**: use `comprehensive` review depth\n- **HIGH**: use `comprehensive` review depth\n- **MEDIUM**: use `targeted` review depth\n- **LOW**: use `basic` review depth\n\n______________________________________________________________________\n\n## L1 Domains and L2 Signals\n\n## Domain 1: Distributed Runtime (CRITICAL/HIGH)\n\n| L2 Signal                  | File Path Pattern                                                                              | Code Pattern                                                                                                                                                              |\n| -------------------------- | ---------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `archon_core`              | `areal/experimental/engine/archon_engine.py`, `areal/experimental/engine/archon_checkpoint.py` | `ArchonEngine`, `ArchonCheckpointManager`, `archon`                                                                                                                       |\n| `archon_parallel`          | `areal/experimental/models/archon/`, `parallel_dims.py`, `parallelize.py`                      | `ArchonParallelDims`, `_build_mesh`, `apply_moe_ep_tp`, `apply_tp`, `apply_cp`, `ExpertTensorParallel`, `etp`, `parallelize_module`, `ColwiseParallel`, `RowwiseParallel` |\n| `process_group`            | `areal/engine/fsdp_utils/`, `areal/engine/megatron_utils/`, `areal/experimental/engine/`       | `new_group`, `ProcessGroup`, `dist.get_rank(`                                                                                                                             |\n| `fsdp_core`                | `areal/engine/fsdp_engine.py`, `areal/engine/fsdp_utils/`                                      | `FSDP`, `fully_shard`, `FullyShardedDataParallel`                                                                                                                         |\n| `megatron_core`            | `areal/engine/megatron_engine.py`, `areal/engine/megatron_utils/`                              | `MegatronEngine`, `pipeline`, `micro-batch`                                                                                                                               |\n| `collectives`              | `areal/engine/`, `areal/infra/rpc/`                                                            | `all_reduce`, `all_gather`, `reduce_scatter`, `all_to_all`, `broadcast`, `barrier`                                                                                        |\n| `mesh_dtensor`             | `areal/experimental/models/archon/`, `areal/engine/fsdp_utils/`                                | `DeviceMesh`, `DTensor`, `Shard(`, `Replicate(`, `distribute_tensor`                                                                                                      |\n| `activation_checkpointing` | `areal/experimental/models/archon/activation_checkpoint.py`, `areal/models/`, `areal/engine/`  | `activation_checkpoint`, `checkpoint_wrapper`, `selective_checkpoint`                                                                                                     |\n| `weight_sync`              | `areal/experimental/engine/archon_weight_sync.py`, `areal/api/engine_api.py`, `areal/engine/`  | `WeightUpdateMeta`, `set_version`, `update_weights`                                                                                                                       |\n\n## Domain 2: Model Compute & Attention (HIGH/MEDIUM)\n\n| L2 Signal                | File Path Pattern                                                                                                                                          | Code Pattern                                                                                           |\n| ------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------ |\n| `tree_attn`              | `areal/models/tree_attn/`                                                                                                                                  | `TreeAttention`, `tree_attn`, `TreeNode`, `tree`                                                       |\n| `sdpa_varlen`            | `attention/sdpa.py`, `attention/varlen.py`, `areal/models/tree_attn/`                                                                                      | `sdpa`, `flash_attn`, `varlen`, `causal_mask`                                                          |\n| `sp_cp_attention_mask`   | `areal/models/tree_attn/`, `areal/experimental/models/archon/attention/`                                                                                   | `SequenceParallel`, `context_parallel`, `mask`                                                         |\n| `triton_kernel`          | `areal/models/tree_attn/triton_kernel.py`                                                                                                                  | `triton`, `kernel`, `autotune`                                                                         |\n| `archon_model_family`    | `areal/experimental/models/archon/model_spec.py`, `areal/experimental/models/archon/qwen*/`                                                                | `ModelSpec`, `register_model_spec`, `supported_model_types`, `state_dict_adapter`, `rope`              |\n| `archon_attention_stack` | `areal/experimental/models/archon/attention/`, `areal/experimental/models/archon/ulysses.py`                                                               | `ulysses_slice_inputs`, `ulysses_gather_output`, `gather_seq_scatter_heads`, `sdpa`, `varlen`          |\n| `archon_moe_modeling`    | `areal/experimental/models/archon/moe/`, `areal/experimental/models/archon/expert_parallel.py`, `areal/experimental/models/archon/moe_weight_converter.py` | `TokenChoiceTopKRouter`, `RouterGateLinear`, `GroupedExperts`, `MoEWeightConverter`, `expert_parallel` |\n\n## Domain 3: Inference Backend & Serving (HIGH)\n\n| L2 Signal                  | File Path Pattern                                              | Code Pattern                                                     |\n| -------------------------- | -------------------------------------------------------------- | ---------------------------------------------------------------- |\n| `vllm_ext`                 | `areal/engine/vllm_ext/`                                       | `areal_vllm_server`, `vllm_worker_extension`, `pause_generation` |\n| `remote_inference_backend` | `areal/engine/vllm_remote.py`, `areal/engine/sglang_remote.py` | `vllm`, `sglang`, `OpenAI`, `request`, `response`                |\n| `request_lifecycle`        | `areal/engine/`, `areal/infra/launcher/`                       | `enqueue`, `dequeue`, `cancel`, `timeout`                        |\n\n## Domain 4: Service Orchestration (HIGH)\n\n| L2 Signal                  | File Path Pattern                                                                                                                                       | Code Pattern                                                     |\n| -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------- |\n| `service_routing_dataflow` | `areal/v2/agent_service/gateway/`, `areal/v2/agent_service/router/`, `areal/v2/inference_service/data_proxy/`, `areal/v2/inference_service/controller/` | `route`, `gateway`, `router`, `DataProxy`, `controller`, `batch` |\n| `session_consistency`      | `areal/v2/agent_service/`, `areal/v2/inference_service/`                                                                                                | `session`, `affinity`, `history`, `state`                        |\n\n## Domain 5: Workflow & Trainer Contract (HIGH/MEDIUM)\n\n| L2 Signal                  | File Path Pattern                                                           | Code Pattern                                        |\n| -------------------------- | --------------------------------------------------------------------------- | --------------------------------------------------- |\n| `workflow_engine_boundary` | `areal/workflow/`, `areal/trainer/`, `areal/engine/`                        | `RolloutWorkflow`, `arun_episode`, `agenerate`      |\n| `dataset_surface`          | `areal/dataset/`                                                            | `DataLoader`, `IterableDataset`, `get_*_dataset`    |\n| `async_contract`           | `areal/workflow/`, `areal/v2/agent_service/`, `areal/v2/inference_service/` | `async def`, `await`, `aiofiles`, `asyncio`         |\n| `weight_version_contract`  | `areal/api/engine_api.py`, `areal/workflow/`, `areal/trainer/`              | `WeightUpdateMeta`, `set_version`, `weight version` |\n\n## Domain 6: API & Config Compatibility (MEDIUM)\n\n| L2 Signal                   | File Path Pattern                     | Code Pattern                                                                     |\n| --------------------------- | ------------------------------------- | -------------------------------------------------------------------------------- |\n| `dataclass_schema`          | `areal/api/`                          | `@dataclass`, `field(`, `__post_init__`                                          |\n| `cli_compat`                | `areal/api/cli_args.py`               | `Literal`, `help`, `default`                                                     |\n| `backward_compat`           | `areal/api/`, `areal/infra/launcher/` | `deprecated`, `compat`, `version`                                                |\n| `project_dependency_config` | `pyproject.toml`, `uv.lock`           | `requires-python`, `dependencies`, `optional-dependencies`, `build-system`, `uv` |\n\n## Domain 7: Numerics & Tensor Semantics (MEDIUM)\n\n| L2 Signal             | File Path Pattern                                                               | Code Pattern                                            |\n| --------------------- | ------------------------------------------------------------------------------- | ------------------------------------------------------- |\n| `shape_dtype`         | `areal/engine/`, `areal/models/`, `areal/trainer/`                              | `.view(`, `.reshape(`, `dtype=`, `.contiguous(`         |\n| `numerical_stability` | `areal/engine/`, `areal/reward/`, `areal/utils/functional/`                     | `log(`, `softmax`, `eps=`, `.clamp(`, `nan`, `inf`      |\n| `reward_surface`      | `areal/reward/`                                                                 | `reward_fn`, `AsyncRewardWrapper`, `MathVerifyWorker`   |\n| `compile_dynamo`      | `areal/experimental/models/archon/compile.py`, `areal/models/`, `areal/engine/` | `torch.compile`, `_dynamo`, `mark_dynamic`, `fullgraph` |\n| `mixed_precision_fp8` | `areal/engine/megatron_utils/fp8/`, `areal/experimental/models/archon/`         | `fp8`, `bf16`, `fp16`, `mixed precision`                |\n\n## Domain 8: Checkpoint & Recovery (CRITICAL/HIGH)\n\n| L2 Signal         | File Path Pattern                                                   | Code Pattern                                    |\n| ----------------- | ------------------------------------------------------------------- | ----------------------------------------------- |\n| `dcp_consistency` | `areal/utils/async_checkpoint.py`, `areal/engine/**/checkpoint*.py` | `dcp.save`, `dcp.load`, `DistributedCheckpoint` |\n| `optimizer_state` | `areal/engine/fsdp_utils/checkpoint.py`, `areal/utils/saver.py`     | `optimizer state`, `state_dict`                 |\n| `resume_compat`   | `areal/utils/recover.py`, `areal/utils/saver.py`                    | `resume`, `load_state_dict`, `migration`        |\n\n## Domain 9: Launcher & Infrastructure (HIGH/MEDIUM)\n\n| L2 Signal                 | File Path Pattern                                            | Code Pattern                                              |\n| ------------------------- | ------------------------------------------------------------ | --------------------------------------------------------- |\n| `launcher_resource_match` | `areal/infra/launcher/`                                      | `LaunchConfig`, `RayLauncher`, `SlurmLauncher`            |\n| `scheduler_contract`      | `areal/infra/scheduler/`, `areal/scheduler/`                 | `Scheduler`, `placement`, `resource`                      |\n| `rpc_transport`           | `areal/infra/rpc/`, `areal/v2/inference_service/data_proxy/` | `RTensor`, `serialize`, `rpc`, `fetch`                    |\n| `runtime_image_config`    | `Dockerfile`, `.dockerignore`                                | `FROM`, `ARG`, `RUN`, `ENV`, `COPY`, `uv sync`, `VARIANT` |\n\n## Domain 10: Low-Risk Hygiene (LOW)\n\n| L2 Signal                 | File Path Pattern                                                                                                                                                       | Code Pattern                                                                                                 |\n| ------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |\n| `tests_docs_config`       | `tests/`, `docs/`, `*.md`, `*.yaml`, `*.json`, `*.toml`                                                                                                                 | -                                                                                                            |\n| `logging_import_security` | `areal/`, `examples/`                                                                                                                                                   | `getLogger`, `print(`, `import *`, `api_key`, `token`, `password`                                            |\n| `project_docs_metadata`   | `docs/build_all.sh`, `docs/generate_cli_docs.py`, `docs/en/`, `docs/zh/`, `README.md`, `CONTRIBUTING.md`, `.github/PULL_REQUEST_TEMPLATE.md`, `.github/ISSUE_TEMPLATE/` | `jupyter-book`, `generate_cli_docs`, `build_all`, `_build`, `checklist`, `template`, `contributing`, `usage` |\n\n## Domain 11: Harness & Agent Infrastructure (MEDIUM/HIGH)\n\n| L2 Signal               | File Path Pattern                                                             | Code Pattern                                                 |\n| ----------------------- | ----------------------------------------------------------------------------- | ------------------------------------------------------------ |\n| `skill_definition`      | `.agents/skills/**/SKILL.md`, `.agents/skills/**/references/`                 | `description:`, `## Workflow`, `## Reference Files`, `skill` |\n| `platform_command_data` | `.claude/commands/`, `.claude/data/`, `.opencode/command/`, `.opencode/data/` | `@.`, `/review-pr`, `/create-pr`, `data/`, `task(`           |\n| `agent_registry_config` | `.codex/config.toml`, `.codex/agents/`, `AGENTS.md`, `CLAUDE.md`              | `agents`, `skills`, `registry`, `subagent`, `config.toml`    |\n\n## Domain 12: CI/CD & Release Automation (HIGH/CRITICAL)\n\n| L2 Signal              | File Path Pattern                                                                                                          | Code Pattern                                              |\n| ---------------------- | -------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------- |\n| `github_workflow_jobs` | `.github/workflows/*.yml`                                                                                                  | `jobs:`, `runs-on:`, `needs:`, `if:`, `workflow_dispatch` |\n| `runner_provisioning`  | `.github/workflows/bake-gcp-image.yml`, `.github/workflows/runner-heartbeat.yml`                                           | `gcp`, `runner`, `image`, `heartbeat`                     |\n| `release_delivery`     | `.github/workflows/build-docker-image.yml`, `.github/workflows/tag-release-image.yml`, `.github/workflows/deploy-docs.yml` | `docker`, `tag`, `release`, `pages`, `publish`            |\n\n______________________________________________________________________\n\n## Must-Not-Regress Core Coverage\n\nThe refactor must preserve these existing review surfaces:\n\n- Archon core: `areal/experimental/models/archon/`,\n  `areal/experimental/engine/archon_engine.py`\n- FSDP core: `areal/engine/fsdp_utils/`, `areal/engine/fsdp_engine.py`\n- Megatron core: `areal/engine/megatron_engine.py`, `areal/engine/megatron_utils/`\n- Reward: `areal/reward/`\n- Dataset: `areal/dataset/`\n- Trainer: `areal/trainer/`\n- Harness: `.agents/`, `.claude/`, `.opencode/`, `.codex/`\n- CI/CD and release: `.github/workflows/`, `Dockerfile`, `pyproject.toml`\n\n______________________________________________________________________\n\n## Cross-Domain Linkage Rules\n\n| Detected Signal                                | Auto-Linked Review                                  |\n| ---------------------------------------------- | --------------------------------------------------- |\n| `archon_core` or `archon_parallel`             | Model Compute & Attention checks                    |\n| `archon_model_family` or `archon_moe_modeling` | Numerics & Tensor Semantics checks                  |\n| `tree_attn`                                    | Numerics & Tensor Semantics checks                  |\n| `reward_surface`                               | Workflow & Trainer Contract checks                  |\n| `compile_dynamo`                               | Distributed Runtime checks                          |\n| `vllm_ext`                                     | Launcher & Infrastructure checks                    |\n| `service_routing_dataflow`                     | Workflow & Trainer async-contract checks            |\n| `weight_sync`                                  | DTensor/process-group/checkpoint interaction checks |\n| `rpc_transport`                                | Distributed Runtime synchronization checks          |\n| `mixed_precision_fp8` + Distributed Runtime    | mesh + weight-sync compatibility checks             |\n| `runtime_image_config`                         | Inference Backend & Serving checks                  |\n| `project_dependency_config`                    | API & Config Compatibility checks                   |\n| `github_workflow_jobs` or `release_delivery`   | Launcher & Infrastructure checks                    |\n| `skill_definition` or `platform_command_data`  | Low-Risk Hygiene checks                             |\n\n______________________________________________________________________\n\n## Risk Identification Guidance\n\n### Distributed Runtime Risks\n\n- Archon mesh construction or parallel-dims mismatch\n- EP/TP/CP application order errors in Archon parallelization\n- Activation checkpoint placement violating TP/CP/FSDP ordering assumptions\n- Archon engine lifecycle drift around distributed setup and checkpoint boundaries\n- Collective call order mismatch across ranks\n- Wrong process-group scope in rank-sensitive logic\n- Mesh dimension mismatch and invalid DTensor placement\n- Weight version drift between rollout and training workers\n\n### Model Compute & Attention Risks\n\n- Attention mask inconsistency under TP/SP/CP paths\n- Tree attention index/routing mismatch\n- Archon model-family registration or per-family wiring drift\n- Archon MoE router/expert behavior diverging from weight-conversion expectations\n- Archon Ulysses slicing/gather semantics mismatching attention layout assumptions\n- Kernel assumptions violating dtype/shape invariants\n- Sequence packing alignment errors\n\n### Service Orchestration Risks\n\n- Session affinity or history drift across gateway/router/data proxy\n- Async message handling holes and dropped tasks\n- Controller/worker lifecycle desynchronization\n\n### Inference Backend & Serving Risks\n\n- Request lifecycle inconsistencies (enqueue/cancel/timeout)\n- Worker state transitions leaving requests stranded\n- Backend extension hooks drifting from runtime expectations\n\n### Workflow & Trainer Contract Risks\n\n- Workflow-engine contract drift across async boundaries\n- Weight version handshake mismatch between rollout and train\n- Trainer lifecycle transition inconsistencies\n\n### API & Config Compatibility Risks\n\n- Breaking config/schema changes without migration path\n- Dataclass or CLI default changes altering behavior silently\n- Missing validation for newly introduced fields\n- Dependency or build-system pin changes breaking supported environments\n\n### Numerics & Tensor Semantics Risks\n\n- Silent shape/dtype mismatch under distributed paths\n- Unstable numerical operations in loss/reward logic\n- torch.compile or dynamo guard changes breaking graph assumptions\n- Mixed-precision interaction regressions\n\n### Checkpoint & Recovery Risks\n\n- Partial-rank checkpoint participation\n- Incompatible state key evolution\n- Resume path breaking optimizer/model synchronization\n\n### Launcher & Infrastructure Risks\n\n- Resource assignment mismatching parallel strategy assumptions\n- RPC transport metadata loss (shape/dtype/device)\n- Startup/shutdown ordering races across processes\n- Runtime image or build-arg drift from supported inference/training variants\n\n### Low-Risk Hygiene Risks\n\n- Docs/config drift from actual runtime behavior\n- Logging or import hygiene regressions\n- Sensitive data exposure in logs or config\n- Documentation/build scripts or project templates drifting from actual workflow\n\n### Harness & Agent Infrastructure Risks\n\n- Skill and command docs drifting from actual platform behavior\n- Cross-platform data files falling out of sync with canonical references\n- Agent registry/config changes breaking expert routing or command discovery\n\n### CI/CD & Release Automation Risks\n\n- Workflow trigger or job dependency changes skipping required validation\n- Runner provisioning drift causing flaky or non-reproducible CI\n- Release or docs deployment jobs publishing the wrong artifacts\n\n<!-- File: .agents/skills/review-pr/references/review-pr-templates.md (Tokens: ~2500 | Category: anthropic-skill) -->\n# PR Review: Domain Templates Reference\n\nThis file contains canonical domain templates for PR review. Referenced by:\n`.agents/skills/review-pr/SKILL.md`\n\n______________________________________________________________________\n\n## Template Selection Rules\n\n1. Select templates by detected L1 domains and L2 signals.\n1. Use at most one primary template per domain.\n1. Always include **General Logic & Boundary** for non-doc/config-only PRs.\n1. Apply cross-domain linkage checks from `review-pr-domains-and-signals.md`.\n\n______________________________________________________________________\n\n## Universal Template\n\n### General Logic & Boundary\n\n```\nApplicable: Any non-doc/config-only change\nChecklist:\n- Boundary condition correctness (empty inputs, singleton, max-size)\n- Conditional logic correctness (branch inversion, short-circuit mistakes)\n- Error-path behavior (exceptions propagated with actionable context)\n- Return-value consistency across code paths\n- No newly introduced hidden behavior changes\n```\n\n______________________________________________________________________\n\n## Domain 1 Template: Distributed Runtime Review \\[comprehensive\\]\n\n```\nApplicable signals: archon_core, archon_parallel, process_group, fsdp_core, megatron_core, collectives, mesh_dtensor, activation_checkpointing, weight_sync\nChecklist:\n- Archon engine and checkpoint lifecycle remain aligned with distributed runtime assumptions\n- FSDP and Megatron engine invariants still match process-group, sharding, and pipeline assumptions\n- Archon parallel-dims and mesh construction still match downstream placement logic\n- Process-group creation/usage/cleanup is rank-consistent\n- Collective operations are called by all required ranks in consistent order\n- DeviceMesh dimensions and DTensor placements are correct for each path\n- Activation checkpoint placement remains compatible with parallel and sharding order requirements\n- Local/global tensor conversion boundaries are explicit and correct\n- Weight version propagation and update ordering are deterministic\n- No debug-only barriers left in hot path\n```\n\n## Domain 2 Template: Model Compute & Attention Review \\[comprehensive\\]\n\n```\nApplicable signals: tree_attn, sdpa_varlen, sp_cp_attention_mask, triton_kernel, archon_model_family, archon_attention_stack, archon_moe_modeling\nChecklist:\n- Attention mask semantics preserved under TP/SP/CP\n- Archon model-family registration and per-family wiring remain internally consistent\n- Archon attention/Ulysses slicing and gather paths preserve layout assumptions\n- Archon MoE router, grouped experts, and weight-conversion interfaces remain aligned\n- Tree attention index/order invariants are maintained\n- Kernel assumptions on dtype/shape/contiguity are satisfied\n- No silent behavior change in sequence packing/unpacking\n- Tensor layouts remain compatible with downstream modules\n```\n\n## Domain 3 Template: Inference Backend & Serving Review \\[comprehensive\\]\n\n```\nApplicable signals: vllm_ext, remote_inference_backend, request_lifecycle\nChecklist:\n- Request lifecycle (enqueue, execution, cancellation, timeout) is coherent\n- Worker state transitions are safe under concurrency\n- Backend-specific extension points stay API-compatible\n- Error handling does not strand in-flight requests\n- Versioning/weight-update interactions are explicit and safe\n```\n\n## Domain 4 Template: Service Orchestration Review \\[comprehensive\\]\n\n```\nApplicable signals: service_routing_dataflow, session_consistency\nChecklist:\n- Gateway/router/data-proxy routing rules are deterministic\n- Session affinity and history consistency are preserved\n- Controller/worker coordination has no lost-update window\n- Async boundaries avoid blocking operations in critical paths\n- Failure/retry behavior does not duplicate or drop work\n```\n\n## Domain 5 Template: Workflow & Trainer Contract Review \\[comprehensive\\]\n\n```\nApplicable signals: workflow_engine_boundary, dataset_surface, async_contract, weight_version_contract\nChecklist:\n- RolloutWorkflow and Engine interfaces remain contract-compatible\n- Dataset/output structure still matches workflow and trainer consumption expectations\n- Async flow uses await consistently and avoids sync I/O in async paths\n- Weight update/version handshake is preserved end-to-end\n- Trainer lifecycle transitions are valid for all execution branches\n- Call ordering assumptions across trainer/workflow/engine are unchanged or justified\n```\n\n## Domain 6 Template: API & Config Compatibility Review \\[targeted\\]\n\n```\nApplicable signals: dataclass_schema, cli_compat, backward_compat, project_dependency_config\nChecklist:\n- Public API signature and default value changes are intentional and compatible\n- Dataclass validation remains complete and informative\n- CLI options preserve expected compatibility semantics\n- New fields include safe defaults or explicit migration handling\n- Breaking changes are documented and scoped\n- Dependency and build-system changes remain compatible with supported environments\n```\n\n## Domain 7 Template: Numerics & Tensor Semantics Review \\[targeted\\]\n\n```\nApplicable signals: shape_dtype, numerical_stability, reward_surface, compile_dynamo, mixed_precision_fp8\nChecklist:\n- Tensor shape/dtype transitions are explicit and internally consistent\n- Numerical stability is protected (log/division/softmax/clamp paths)\n- Reward-side numerical behavior remains consistent with workflow consumption expectations\n- torch.compile / dynamo assumptions still hold for dynamic shapes and distributed execution\n- Mixed-precision behavior is correct for forward + backward + reduce paths\n- In-place and view/reshape operations do not corrupt gradient flow\n- Device placement and dtype combinations remain legal across code paths\n```\n\n## Domain 8 Template: Checkpoint & Recovery Review \\[comprehensive\\]\n\n```\nApplicable signals: dcp_consistency, optimizer_state, resume_compat\nChecklist:\n- Save/load requires and enforces all-rank participation where needed\n- State dict naming/structure is stable or migration-safe\n- Optimizer state sharding/gather behavior is consistent\n- Resume path restores model + optimizer + version state coherently\n- Async checkpoint behavior preserves ordering and durability assumptions\n```\n\n## Domain 9 Template: Launcher & Infrastructure Review \\[targeted\\]\n\n```\nApplicable signals: launcher_resource_match, scheduler_contract, rpc_transport, runtime_image_config\nChecklist:\n- Resource assignment matches declared parallel strategy assumptions\n- Scheduler decisions preserve required placement/affinity constraints\n- RPC serialization/deserialization keeps shape/dtype/device semantics\n- Transport retries/timeouts do not violate idempotency expectations\n- Cross-process startup/shutdown ordering is robust\n- Runtime image and build configuration remain aligned with supported variants\n```\n\n## Domain 10 Template: Low-Risk Hygiene Review \\[basic\\]\n\n```\nApplicable signals: tests_docs_config, logging_import_security, project_docs_metadata\nChecklist:\n- Tests/docs/config edits are internally consistent and non-misleading\n- Logging follows project conventions and avoids sensitive leakage\n- No wildcard imports or obvious dependency hygiene regressions\n- No accidental secrets/keys/tokens introduced\n- Docs build scripts and project templates stay aligned with real contributor workflow\n```\n\n## Domain 11 Template: Harness & Agent Infrastructure Review \\[targeted\\]\n\n```\nApplicable signals: skill_definition, platform_command_data, agent_registry_config\nChecklist:\n- Canonical skills and derived platform data remain structurally aligned\n- Command docs still point to the correct data files and execution model\n- Agent registry/config changes preserve command discovery and expert routing\n- Cross-platform mirrors are regenerated after canonical changes\n```\n\n## Domain 12 Template: CI/CD & Release Automation Review \\[comprehensive\\]\n\n```\nApplicable signals: github_workflow_jobs, runner_provisioning, release_delivery\nChecklist:\n- Workflow triggers, job dependencies, and permissions still enforce required validation\n- Runner/image provisioning remains reproducible and compatible with job expectations\n- Release, docker, and docs deployment jobs publish the intended artifacts only\n- CI changes do not silently skip tests, formatting, or release gates\n```\n\n______________________________________________________________________\n\n## Signal-Specific Add-On Checklists\n\nUse these only when corresponding L2 signals are detected.\n\n### `tree_attn` Add-On \\[comprehensive\\]\n\n```\n- Node/edge indexing is deterministic and shape-safe\n- Tree traversal order matches attention mask semantics\n- FSDP/Megatron/Archon variant modules remain behaviorally aligned\n```\n\n### `vllm_ext` Add-On \\[comprehensive\\]\n\n```\n- Server and worker extension hooks still match upstream expectations\n- Request pause/resume/cancel semantics remain coherent\n- Integration-specific monkey-patches are scoped and guarded\n```\n\n### `archon_model_family` Add-On \\[comprehensive\\]\n\n```\n- ModelSpec registration stays unique and complete for supported model types\n- Per-family model/args/spec/state-adapter wiring remains consistent\n- Pipelining hooks and model-part boundaries stay compatible with runtime assumptions\n```\n\n### `archon_moe_modeling` Add-On \\[comprehensive\\]\n\n```\n- Router top-k, gating dtype, and expert grouping semantics remain coherent\n- GroupedExperts layout and token reordering assumptions still match expert execution\n- MoE weight conversion paths stay consistent with runtime sharding expectations\n```\n\n### `service_routing_dataflow` Add-On \\[comprehensive\\]\n\n```\n- Route selection and fallback ordering are deterministic\n- Data proxy transformations preserve payload integrity\n- Session-key partitioning logic is collision-safe\n```\n\n### `remote_inference_backend` Add-On \\[targeted\\]\n\n```\n- Remote backend request/response semantics remain consistent across supported engines\n- Backend-specific transport options do not change lifecycle expectations silently\n- Shared request payload assumptions remain compatible across remote backends\n```\n\n### `weight_sync` Add-On \\[comprehensive\\]\n\n```\n- Versioned updates are monotonic and race-safe\n- Broadcast/all-gather points are aligned with consumer expectations\n- Local caching behavior cannot serve stale weights indefinitely\n```\n\n### `activation_checkpointing` Add-On \\[targeted\\]\n\n```\n- Checkpoint wrappers are applied in a parallelism-safe order\n- Selective checkpoint policies still cover the intended modules only\n- Activation recompute paths do not break sharding or sequence-parallel assumptions\n```\n\n### `reward_surface` Add-On \\[targeted\\]\n\n```\n- AsyncRewardWrapper-facing reward interfaces remain contract-compatible\n- Reward outputs keep expected shape, dtype, and per-sample semantics\n- Workflow assumptions about reward timing and batching remain valid\n```\n\n### `compile_dynamo` Add-On \\[targeted\\]\n\n```\n- torch.compile and dynamo guards still tolerate expected dynamic-shape inputs\n- fullgraph and mark_dynamic choices remain compatible with distributed execution paths\n- Compile-specific changes do not silently alter runtime fallback behavior\n```\n\n### `rpc_transport` Add-On \\[targeted\\]\n\n```\n- RTensor conversion is reversible and metadata-complete\n- Batch fetch/request framing preserves ordering and boundaries\n- Retry logic does not replay non-idempotent actions incorrectly\n```\n\n### `runtime_image_config` Add-On \\[targeted\\]\n\n```\n- Docker base image and build args still match supported backend variants\n- Layer ordering preserves expected cache and dependency behavior\n- Image contents remain aligned with runtime assumptions documented in the repo\n```\n\n### `project_dependency_config` Add-On \\[targeted\\]\n\n```\n- Python/version constraints and extras remain internally consistent\n- Lockfile changes match the intended dependency update scope\n- Build backend/tooling changes do not break install or publish workflows\n```\n\n### `github_workflow_jobs` Add-On \\[comprehensive\\]\n\n```\n- Workflow triggers and job graph still run required validation paths\n- Required secrets/permissions are scoped correctly\n- Matrix or conditional changes do not silently skip critical jobs\n```\n\n### `project_docs_metadata` Add-On \\[basic\\]\n\n```\n- Docs build entrypoints and contributor-facing metadata remain mutually consistent\n- Public templates and contributor instructions still match the actual workflow\n- Build/preview guidance still points to the supported commands\n```\n\n### `skill_definition` / `platform_command_data` Add-On \\[targeted\\]\n\n```\n- Canonical and derived review-pr data files stay in sync after edits\n- Command/import paths remain correct after file moves or renames\n- Wrapper-specific routing stays out of canonical reference files\n```\n\n<!-- File: .agents/skills/review-pr/SKILL.md (Tokens: ~694 | Category: anthropic-skill) -->\n---\nname: review-pr\ndescription: Read-only pull request review workflow with risk analysis, targeted checklists, and Codex subagent consultation.\n---\n\n# Review Pull Request\n\nUse this skill when the user asks for a PR review of the current branch or a specific\nPR.\n\n## Inputs\n\n- Optional PR number\n- Optional `--quick` to stop after the change analysis phase\n\n## Hard Rules\n\n- Stay read-only.\n- Do not edit files, commit, push, rebase, or change GitHub state.\n- Do not run build, install, or test commands that mutate the environment.\n- Use `gh` for PR metadata and git diff retrieval.\n\n## Reference Files\n\n- `references/review-pr-domains-and-signals.md`\n- `references/review-pr-templates.md`\n\n## Workflow\n\n### Phase 1: Resolve PR context\n\n1. Use `gh pr view` to fetch PR title, body, state, draft status, and changed files.\n1. If no PR exists, stop and report that clearly.\n1. If the PR is closed, stop.\n1. Record the branch name and changed file list.\n\n### Phase 2: Change analysis\n\n1. Classify changed files using `references/review-pr-domains-and-signals.md`.\n1. Determine the highest overall risk level: `CRITICAL`, `HIGH`, `MEDIUM`, or `LOW`.\n1. Build a `CHANGE_ANALYSIS_REPORT` that lists:\n   - detected domains/signals\n   - risk level\n   - affected files\n   - related frameworks\n   - likely failure modes\n\nIf `--quick` is set, return the change analysis report and stop here.\n\n### Phase 3: Review planning\n\n1. Select the smallest useful set of review passes from\n   `references/review-pr-templates.md`.\n1. Split by risk area, not by file count.\n1. Always include at least one general logic pass.\n\n### Phase 4: Expert consultation\n\nConsult the matching Codex subagents registered in `.codex/config.toml` when relevant:\n\n- `archon-expert`\n- `fsdp-expert`\n- `megatron-expert`\n- `algorithm-expert`\n- `launcher-expert`\n\nIf the Codex runtime supports parallel subagent execution, run independent review passes\nin parallel. Otherwise, execute them serially.\n\n### Phase 5: Final review\n\nProduce findings first, ordered by severity:\n\n1. `CRITICAL`\n1. `HIGH`\n1. `MEDIUM`\n1. `LOW`\n\nFor every finding, include:\n\n- file path\n- line number when available\n- why it is a bug, regression, or risk\n- concrete fix direction\n\n## What to Ignore\n\n- Pure style nits with no correctness impact\n- Issues outside the changed scope unless the PR makes them worse\n- Failures that standard linters or formatters would already catch\n- Speculative concerns with no concrete trigger in the diff\n\n## Output Shape\n\nUse this structure:\n\n```markdown\nCHANGE_ANALYSIS_REPORT:\n- detected_domains: [...]\n- detected_signals: [...]\n- risk_level: ...\n- affected_files: [...]\n- related_frameworks: [...]\n- identified_risks: [...]\n\nFindings\n1. [severity] Title — path:line\n   - Problem: ...\n   - Fix: ...\n\nOpen Questions\n- ...\n\nResidual Risk\n- ...\n```\n\n<!-- File: .agents/skills/translate-doc-zh/SKILL.md (Tokens: ~373 | Category: anthropic-skill) -->\n---\nname: translate-doc-zh\ndescription: Translate an English document under `docs/en/` into the matching Chinese document under `docs/zh/`.\n---\n\n# Translate Docs EN to ZH\n\nUse this skill when the user asks to translate a document from `docs/en/` to `docs/zh/`.\n\n## Input\n\n- A markdown file path under `docs/en/`\n\n## Workflow\n\n### Step 1: Validate the source path\n\n1. Confirm the file exists.\n1. Confirm it is under `docs/en/`.\n1. Confirm it ends with `.md`.\n1. If any check fails, stop and ask the user for a valid `docs/en/...md` path.\n\n### Step 2: Resolve the target path\n\n- Source: `docs/en/<path>.md`\n- Target: `docs/zh/<path>.md`\n\n### Step 3: Choose translation mode\n\n- If the Chinese file already exists, update only the changed parts while preserving the\n  rest.\n- If the Chinese file does not exist, translate the full document.\n\n## Translation Rules\n\n- Preserve technical terms such as FSDP, FSDP2, GRPO, PPO, DAPO, MoE, LLM, RL, RLVR,\n  Codex, Claude Code, OpenCode, Megatron, Archon, SGLang, vLLM, PyTorch, HuggingFace,\n  and Transformers.\n- Do not translate file paths, code blocks, CLI flags, or literal configuration keys.\n- Preserve Markdown structure, tables, and fenced code blocks.\n- Use concise, professional Chinese terminology.\n\n## Error Handling\n\n- If the target directory does not exist, create it before writing the translated file.\n- If the source document is partially translated already, preserve sections that do not\n  need updates.\n\n<!-- File: .agents/skills/upgrade-deps/CHECKLIST_MAINTENANCE.md (Tokens: ~3327 | Category: anthropic-skill) -->\n# Checklist Maintenance Guide\n\nThis document is the single reference for maintaining the per-package API checklists\nunder `checklists/`. It covers two complementary maintenance activities:\n\n- **Structural validation** — ensuring every AReaL import site is documented in the\n  checklist. Performed in SKILL.md → **Step 0.5** before any dependency changes.\n- **Content update** — ensuring documented API signatures and code snippets match the\n  post-upgrade state. Performed in SKILL.md → **Step 6e** after code changes are\n  applied.\n\nBoth activities share the same checklist format. This guide also covers creating new\nchecklists and writing good API catalog entries.\n\n______________________________________________________________________\n\n## 1. Checklist Structure Reference\n\nEach checklist file lives at `checklists/<package-name>.md` and follows the format\ndefined in `checklists/_TEMPLATE.md`. The key sections are:\n\n| Section                  | Purpose                                                                                                                                                                                                            |\n| ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| **YAML frontmatter**     | `package`, `github` (org/repo), `branch_template`, `upstream_paths` — metadata used by the API audit (Step 6a) to clone the correct upstream tag and locate source files.                                          |\n| **Affected Files**       | Three-tier table (Primary / Secondary / Tertiary) listing every AReaL file that imports or uses the package, with a summary of what it imports.                                                                    |\n| **API Usage Catalog**    | Numbered entries documenting each distinct API surface (function, class, HTTP endpoint, schema contract). Each entry includes the upstream source path, the AReaL call-site code, and specific Check instructions. |\n| **Version-Guarded Code** | List of AReaL code locations that have version-specific conditionals for this package (e.g., `if version < \"0.4.10\": ...`).                                                                                        |\n\n______________________________________________________________________\n\n## 2. Import Patterns by Package\n\nUse these patterns to discover all AReaL usages of each focused package. Grep across\n`areal/`, `tests/`, and `examples/`.\n\n| Package           | Import path(s)    | Grep patterns                                    | Notes                                                                                 |\n| ----------------- | ----------------- | ------------------------------------------------ | ------------------------------------------------------------------------------------- |\n| `megatron-core`   | `megatron.core`   | `from megatron.core`, `import megatron.core`     |                                                                                       |\n| `megatron-bridge` | `megatron.bridge` | `from megatron.bridge`, `import megatron.bridge` |                                                                                       |\n| `mbridge`         | `mbridge`         | `from mbridge`, `import mbridge`                 | Pinned to git commit; also check `mbridge.core.*` submodule imports                   |\n| `transformers`    | `transformers`    | `from transformers`, `import transformers`       | Exclude `flash_attn.bert_padding` false positives                                     |\n| `sglang`          | `sglang`          | `from sglang`, `import sglang`                   | Also scan `sglang_remote.py` for HTTP endpoint paths and JSON field names             |\n| `vllm`            | `vllm`            | `from vllm`, `import vllm`                       | Also scan `vllm_remote.py` and `vllm_ext/` for HTTP endpoints and JSON fields         |\n| `peft`            | `peft`            | `from peft`, `import peft`                       | Also check for indirect PEFT schema dicts (`\"r\"`, `\"lora_alpha\"`, `\"target_modules\"`) |\n| `torchao`         | `torchao`         | `from torchao`, `import torchao`                 | All usage goes through `prototype.*` path                                             |\n\n### Edge cases to watch for\n\n- **HTTP-only integrations** (sglang, vllm): The primary integration is REST-based, not\n  Python imports. Grep for endpoint path strings (e.g., `\"/generate\"`,\n  `\"/update_weights_from_distributed\"`) and JSON field names in request/response parsing\n  code.\n- **Indirect / schema-only usage**: Some files reference a package's conventions without\n  importing it directly (e.g., PEFT config dicts in `areal/api/io_struct.py`). These\n  should be listed in Affected Files and may warrant an API catalog entry if the schema\n  contract is non-trivial.\n- **Conditional / lazy imports**: Imports inside `if TYPE_CHECKING:` blocks or inside\n  function bodies. These are still real dependencies — include them.\n- **String references**: Config enums or registry keys that name a package (e.g.,\n  `\"sglang\"` in `SGLangConfig`). These rarely need catalog entries but should appear in\n  Affected Files if the file also has substantive usage.\n- **Test stubs**: Files like `tests/experimental/archon/conftest.py` that conditionally\n  stub out a package. List in Tertiary Affected Files.\n\n______________________________________________________________________\n\n## 3. Structural Validation Procedure\n\n> **Referenced by:** SKILL.md → Step 0.5\n\nThis procedure checks whether a checklist documents ALL current AReaL import sites and\ncall patterns. Run it before upgrading a package to ensure the API audit (Step 6) has\ncomplete coverage.\n\n### 3.1 Discover all usages\n\nFor each focused package being validated:\n\n1. Grep the AReaL codebase using the patterns from § 2. Search across `areal/`,\n   `tests/`, and `examples/`.\n1. For HTTP-based packages, also scan the relevant remote engine files for endpoint\n   paths and JSON field names.\n1. Exclude false positives: comments, docstrings, unrelated substrings (e.g.,\n   `flash_attn.bert_padding` is not a `transformers` import).\n1. Collect results as `{file_path, imported_names[]}` for each hit.\n\n### 3.2 Compare against Affected Files tables\n\nParse the checklist's Primary / Secondary / Tertiary tables and compare against the\ndiscovered files. Build three lists:\n\n- **MISSING** — files found in the codebase that are not listed in any tier.\n- **STALE** — files listed in the checklist that no longer import the package (file\n  deleted, import removed, or moved to a different file).\n- **CHANGED** — files listed in the checklist whose actual imports differ from what the\n  \"Imports / Usage\" column documents.\n\n### 3.3 Classify new files into tiers\n\nUse these rules (consistent with existing checklists):\n\n| Tier          | Directories                                                                                                                                                               | Rationale                                                                |\n| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------ |\n| **Primary**   | `areal/engine/`, `areal/experimental/engine/`, `areal/experimental/models/`                                                                                               | Engine layer — most likely to break on API changes, highest blast radius |\n| **Secondary** | `areal/models/`, `areal/workflow/`, `areal/infra/`, `areal/api/`, `areal/utils/`, `areal/trainer/`, `areal/dataset/`, `areal/reward/`, `areal/experimental/` (non-engine) | Model / infra / workflow layer — important but lower blast radius        |\n| **Tertiary**  | `tests/`, `examples/`, `docs/`, tool scripts                                                                                                                              | Tests and examples — breakage is contained, lower priority               |\n\nWhen a file spans concerns (e.g., a test that exercises engine internals), classify by\nthe file's location, not its content.\n\n### 3.4 Determine new API catalog entries\n\nFor each MISSING or CHANGED file, examine the specific imports and call sites:\n\n- If the usage pattern is **already covered** by an existing catalog entry (same\n  function/class, same call pattern) → just add the file to the Affected Files table. No\n  new catalog entry needed.\n- If the usage involves a function/class **NOT in the catalog**, or uses it with\n  **significantly different parameters** → create a new API catalog entry (see § 6 for\n  format).\n\n\"Significantly different\" means: different keyword arguments, different return value\nconsumption, or a different code path that could break independently.\n\n### 3.5 Update the checklist\n\n1. Add missing files to the appropriate tier table with their imports summary.\n1. Remove stale file entries. If uncertain whether a removal is correct, add a\n   `<!-- TODO: verify removal -->` comment instead of deleting.\n1. Update the \"Imports / Usage\" column for CHANGED files.\n1. Add new API catalog entries following the format in § 6.\n1. Renumber catalog entries sequentially (1, 2, 3, ...).\n1. Update the entry count in SKILL.md's **Checklist File Status** table at the bottom.\n\n### 3.6 Report format\n\nAfter completing validation, report changes before proceeding:\n\n```text\nChecklist validation for <package>:\n- Added N files to Affected Files (P primary, S secondary, T tertiary)\n- Added M new API catalog entries: [brief list]\n- Removed K stale file entries: [brief list]\n- Updated J existing entries with changed imports\n- No changes needed (if clean)\n```\n\n______________________________________________________________________\n\n## 4. Content Update Procedure\n\n> **Referenced by:** SKILL.md → Step 6e\n\nThis procedure updates a checklist's API signatures and code snippets AFTER an upgrade\nhas been applied and code changes (Step 6d) are complete. The goal is to keep the\nchecklist accurate for the next upgrade cycle.\n\n### 4.1 Update API signatures\n\nFor each catalog entry where the upstream API signature changed at the new version:\n\n1. Update the **Source** path if the upstream file was moved or renamed.\n1. Note any new parameters, removed parameters, or type changes in the **Check**\n   instructions — these become the baseline for the next upgrade.\n1. If a function was removed and replaced, update the entry to reference the\n   replacement.\n\n### 4.2 Update call-site code snippets\n\nIf Step 6d modified an AReaL call site to accommodate the new version:\n\n1. Replace the code snippet in the corresponding catalog entry with the updated code.\n1. Update the line number references if they shifted.\n1. Ensure the snippet includes enough context to understand the parameters used and the\n   return value consumed.\n\n### 4.3 Update version-guarded code\n\n- **Remove** entries for version guards that were cleaned up (the guarded version is now\n  below the minimum supported version).\n- **Add** entries for any new version guards introduced during the upgrade.\n- **Update** threshold versions in existing entries.\n\n### 4.4 Update frontmatter\n\n- Add or remove entries in `upstream_paths` if upstream source files were moved,\n  renamed, or deleted.\n- Update `branch_template` if the upstream project changed its tagging convention (e.g.,\n  from `v${VERSION}` to `release/${VERSION}`).\n\n### 4.5 Update SKILL.md status table\n\nUpdate the entry count and description in the **Checklist File Status** table at the\nbottom of SKILL.md to reflect the current state of the checklist.\n\n______________________________________________________________________\n\n## 5. Creating a New Checklist\n\nWhen a new package becomes \"focused\" (added to SKILL.md's Focused Packages table):\n\n1. **Copy the template.** Copy `checklists/_TEMPLATE.md` to\n   `checklists/<package-name>.md`.\n\n1. **Fill in YAML frontmatter:**\n\n   - `package`: the pip package name (e.g., `sglang`)\n   - `github`: the GitHub org/repo (e.g., `sgl-project/sglang`)\n   - `branch_template`: how to construct the git tag from a version string (e.g.,\n     `v${VERSION}`)\n   - `upstream_paths`: list of source paths in the upstream repo that are most relevant\n     to AReaL's usage\n\n1. **Run structural validation.** Follow the full procedure in § 3 to populate the\n   Affected Files tables and API Usage Catalog.\n\n1. **Update SKILL.md.** Add the package to:\n\n   - The **Focused Packages** table (Architecture section)\n   - The **Package Impact Matrix** (with correct scope and pyproject locations)\n   - The **Checklist File Status** table (bottom of file)\n\n1. **Commit** the new checklist alongside the SKILL.md update.\n\n______________________________________________________________________\n\n## 6. API Catalog Entry Format\n\nEach entry documents one distinct API surface — a function, class, HTTP endpoint, or\nschema contract. Use this format:\n\n````markdown\n### N. `fully.qualified.FunctionOrClass`\n\n**Source:** `upstream-repo/path/to/file.py`\n\nCalled in `areal/path/to/file.py` (description, lines N–M):\n\n```python\n# Paste the ACTUAL call site code — enough context to understand\n# the parameters used and the return value consumed.\nactual_call(param1=..., param2=...)\n```\n\n**Check:** [What to verify — be specific.]\n````\n\n### Guidelines\n\n- **One entry per distinct API surface.** A function, class, HTTP endpoint, or schema\n  contract gets one entry. If the same function is called in multiple files with the\n  same pattern, list all call sites in a single entry.\n- **Multiple call sites.** When the same API is used in several files, list the primary\n  call site with a full code snippet, then note additional call sites with a brief\n  reference: \"Also called in `areal/path/to/other.py`.\"\n- **HTTP endpoints.** Document the endpoint path, HTTP method, request payload fields,\n  and response fields that AReaL parses. These are API surfaces even though they are not\n  Python imports.\n- **Indirect / schema usage.** If AReaL constructs a dict that must conform to a\n  package's schema (e.g., PEFT config dicts), document the schema contract and note that\n  it is indirect.\n- **Check instructions.** Be specific and actionable:\n  - Good: \"Confirm `bias` still accepts the string `\"none\"` (not renamed to an enum).\"\n  - Bad: \"Check if anything changed.\"\n- **Code snippets.** Include enough surrounding code to show parameter names, types, and\n  how the return value is consumed. Strip unrelated code. Include line numbers for\n  reference.\n\n______________________________________________________________________\n\n## 7. When NOT to Add a Catalog Entry\n\nNot every import needs its own API catalog entry. Skip a dedicated entry when:\n\n- **Type-only imports.** A class imported solely for type annotations or `isinstance`\n  checks (e.g., `from transformers import PretrainedConfig` used only as\n  `config: PretrainedConfig`). List the file in Affected Files but skip the catalog\n  unless the type annotation relies on specific attributes that could change.\n- **Re-exports / pass-throughs.** A file that re-exports a symbol without calling it.\n- **Standard patterns unlikely to break.** Accessing `__version__`, basic enum values,\n  or other stable public API that has never changed across versions.\n- **Test files duplicating Primary/Secondary coverage.** If a test file uses the exact\n  same API pattern already documented in a Primary or Secondary entry, list it in\n  Tertiary Affected Files but skip a separate catalog entry. Exception: if the test\n  exercises a unique code path or parameter combination, add an entry.\n\nWhen in doubt, **add the entry**. A slightly verbose checklist is better than a gap that\ncauses a missed breaking change.\n\n<!-- File: .agents/skills/upgrade-deps/checklists/_TEMPLATE.md (Tokens: ~397 | Category: anthropic-skill) -->\n<!-- Template for per-package API checklists. Copy this file and fill in for each package. -->\n\n______________________________________________________________________\n\npackage: <pip-package-name> github: \\<org/repo> branch_template: \"v${VERSION}\"\nupstream_paths:\n\n- path/to/relevant/file.py\n- path/to/relevant/module/\n\n______________________________________________________________________\n\n## Affected Files\n\n### Primary (engine layer — most likely to break)\n\n| File                    | Imports / Usage                   |\n| ----------------------- | --------------------------------- |\n| `areal/path/to/file.py` | `module.function`, `module.Class` |\n\n### Secondary (model / infra layer)\n\n| File                    | Imports / Usage   |\n| ----------------------- | ----------------- |\n| `areal/path/to/file.py` | `module.function` |\n\n### Tertiary (tests, config)\n\n| File                 | Imports / Usage   |\n| -------------------- | ----------------- |\n| `tests/test_file.py` | `module.function` |\n\n______________________________________________________________________\n\n## API Usage Catalog\n\nFor each function/class below, verify the call signature against the upstream source at\nthe target version. Focus on: **missing new required parameters**, **removed old\nparameters**, **renamed parameters**, **changed return types**, **changed method\nsignatures on returned objects**, and **moved/renamed modules**.\n\n### 1. `module.submodule.FunctionOrClass`\n\n**Source:** `upstream-repo/path/to/file.py`\n\nCalled in `areal/path/to/file.py`:\n\n```python\n# Paste the actual call site code here\nFunctionOrClass(param1=..., param2=...)\n```\n\n**Check:** \\[Describe what to verify — new params? renamed? return type change?\\]\n\n### 2. ...\n\n______________________________________________________________________\n\n## Version-Guarded Code\n\n<!-- List any AReaL code that has version-specific behavior for this package -->\n\n- `areal/path/to/file.py:LINE` — description of version guard and threshold\n\n<!-- File: .agents/skills/upgrade-deps/checklists/mbridge.md (Tokens: ~3242 | Category: anthropic-skill) -->\n---\npackage: mbridge\ngithub: ISEEKYAN/mbridge\nbranch_template: ${VERSION}\nupstream_paths:\n  - mbridge/__init__.py\n  - mbridge/core/__init__.py\n  - mbridge/core/auto_bridge.py\n  - mbridge/core/bridge.py\n  - mbridge/core/llm_bridge.py\n  - mbridge/core/util.py\n---\n\n## Affected Files\n\n### Primary (engine layer — most likely to break)\n\n| File                                        | Imports / Usage                                                                                                                                                                                 |\n| ------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `areal/engine/megatron_engine.py`           | `import mbridge` → `mbridge.AutoBridge.from_pretrained`, `.dtype`, `.set_extra_args`                                                                                                            |\n| `areal/models/mcore/hf_load.py`             | `from mbridge.core.bridge import Bridge` → `bridge._get_actual_hf_path`, `._weight_name_mapping_*`, `.hf_config`, `.dtype`, `.config`                                                           |\n| `areal/models/mcore/hf_save.py`             | `from mbridge.core import Bridge`, `from mbridge.core.util import unwrap_model` → `bridge._weight_name_mapping_*`, `._weight_merge_across_tp`, `._weight_to_hf_format`, `.hf_config`, `.config` |\n| `areal/models/mcore/bailing_moe_bridge.py`  | `from mbridge.core import LLMBridge, register_model` → subclasses `LLMBridge`, uses `@register_model` decorator                                                                                 |\n| `areal/models/tree_attn/module_megatron.py` | `from mbridge.core import LLMBridge` → monkey-patches `LLMBridge._get_transformer_layer_spec`                                                                                                   |\n\n### Secondary (model / infra layer)\n\n| File                             | Imports / Usage                                                                          |\n| -------------------------------- | ---------------------------------------------------------------------------------------- |\n| `areal/models/mcore/registry.py` | `from mbridge.core.bridge import Bridge` → `bridge.hf_config`, `.config`, `.get_model()` |\n\n### Tertiary (tests, config)\n\n| File                                          | Imports / Usage                                         |\n| --------------------------------------------- | ------------------------------------------------------- |\n| `tests/test_estimate_num_params.py`           | `import mbridge` → `mbridge.AutoBridge.from_pretrained` |\n| `areal/tools/validation_base.py`              | `\"mbridge\"` in `CRITICAL_PACKAGES` (metadata only)      |\n| `areal/tools/validate_docker_installation.py` | `\"mbridge\"` in package validation list (metadata only)  |\n\n______________________________________________________________________\n\n## API Usage Catalog\n\nFor each function/class below, verify the call signature against the upstream source at\nthe target version. Focus on: **missing new required parameters**, **removed old\nparameters**, **renamed parameters**, **changed return types**, **changed method\nsignatures on returned objects**, and **moved/renamed modules**.\n\n### 1. `mbridge.AutoBridge.from_pretrained`\n\n**Source:** `mbridge/core/auto_bridge.py`\n\nCalled in `areal/engine/megatron_engine.py` (line 507):\n\n```python\nself.bridge = mbridge.AutoBridge.from_pretrained(\n    self.config.path, trust_remote_code=True\n)\n```\n\nAlso called in `tests/test_estimate_num_params.py` (line 43):\n\n```python\nbridge = mbridge.AutoBridge.from_pretrained(model_name_or_path)\n```\n\n**Check:** Confirm `trust_remote_code` is still accepted as a keyword argument. Verify\nthe first positional arg is still the model path (str). Verify the return type is still\na `Bridge` (or subclass like `LLMBridge`) instance with `.hf_config`, `.config`, and\n`.dtype` attributes.\n\n______________________________________________________________________\n\n### 2. `Bridge.dtype` (property/attribute)\n\n**Source:** `mbridge/core/bridge.py`\n\nSet in `areal/engine/megatron_engine.py` (line 510):\n\n```python\nself.bridge.dtype = self.dtype\n```\n\n**Check:** Confirm `dtype` is still a settable attribute (not read-only property).\nVerify it accepts `torch.dtype` values. This controls the precision used for weight\nloading and conversion throughout the bridge.\n\n______________________________________________________________________\n\n### 3. `Bridge.set_extra_args`\n\n**Source:** `mbridge/core/bridge.py` or `mbridge/core/llm_bridge.py`\n\nCalled in `areal/engine/megatron_engine.py` (lines 512–518):\n\n```python\nself.bridge.set_extra_args(\n    recompute_granularity=self.mcore_config.recompute_granularity,\n    recompute_method=self.mcore_config.recompute_method,\n    recompute_num_layers=self.mcore_config.recompute_num_layers,\n    distribute_saved_activations=self.mcore_config.distribute_saved_activations,\n    recompute_modules=self.mcore_config.recompute_modules,\n)\n```\n\n**Check:** Confirm all five keyword arguments are still accepted:\n`recompute_granularity`, `recompute_method`, `recompute_num_layers`,\n`distribute_saved_activations`, `recompute_modules`. Check if new required arguments\nwere added.\n\n______________________________________________________________________\n\n### 4. `Bridge.get_model`\n\n**Source:** `mbridge/core/bridge.py` or `mbridge/core/llm_bridge.py`\n\nCalled in `areal/models/mcore/registry.py` (lines 178–188):\n\n```python\nmodels = bridge.get_model(\n    wrap_with_ddp=mcore_config.wrap_with_ddp,\n    ddp_config=dataclasses.asdict(mcore_config.ddp),\n    use_torch_fsdp2=mcore_config.use_torch_fsdp2,\n    use_custom_fsdp=mcore_config.use_custom_fsdp,\n    fp16=tf_config.fp16,\n    bf16=tf_config.bf16,\n    use_precision_aware_optimizer=mcore_config.use_precision_aware_optimizer,\n    overlap_param_gather_with_optimizer_step=mcore_config.overlap_param_gather_with_optimizer_step,\n)\n```\n\n**Check:** Confirm all keyword arguments are still accepted: `wrap_with_ddp`,\n`ddp_config`, `use_torch_fsdp2`, `use_custom_fsdp`, `fp16`, `bf16`,\n`use_precision_aware_optimizer`, `overlap_param_gather_with_optimizer_step`. Verify the\nreturn type is still an iterable of model instances (GPTModel or DDP-wrapped). Check for\nnewly added required parameters.\n\n______________________________________________________________________\n\n### 5. `Bridge._get_actual_hf_path`\n\n**Source:** `mbridge/core/bridge.py`\n\nCalled in `areal/models/mcore/hf_load.py` (line 539):\n\n```python\nweights_path = bridge._get_actual_hf_path(weights_path)\n```\n\n**Check:** Confirm this private method still exists and accepts a single string argument\n(path). Verify it returns a string (resolved path to the actual HF weights directory).\nSince this is a private API, check if it was renamed or replaced.\n\n______________________________________________________________________\n\n### 6. `Bridge._weight_name_mapping_mcore_local_to_global`\n\n**Source:** `mbridge/core/bridge.py` or `mbridge/core/llm_bridge.py`\n\nCalled in `areal/models/mcore/hf_load.py` (line 570):\n\n```python\nlocal_to_global_map = bridge._weight_name_mapping_mcore_local_to_global(model)\n```\n\nAlso called in `areal/models/mcore/hf_save.py` (line 243):\n\n```python\nbridge._weight_name_mapping_mcore_local_to_global(model, consider_ep=False)\n```\n\n**Check:** Confirm the method still accepts a model as the first positional argument.\nVerify `consider_ep` keyword argument is still accepted (used in hf_save.py). Verify the\nreturn type is still `dict[str, str]` mapping local parameter names to global names.\nCheck if the method was renamed or its signature changed.\n\n______________________________________________________________________\n\n### 7. `Bridge._weight_name_mapping_mcore_to_hf`\n\n**Source:** `mbridge/core/bridge.py` or `mbridge/core/llm_bridge.py`\n\nCalled in `areal/models/mcore/hf_load.py` (line 573):\n\n```python\nlocal_to_hf_map = {\n    k: bridge._weight_name_mapping_mcore_to_hf(v)\n    for k, v in local_to_global_map.items()\n    if \"_extra_state\" not in k\n}\n```\n\n**Check:** Confirm the method accepts a single string argument (mcore global weight\nname). Verify the return type is still `list[str]` (list of HF weight names). This is\nalso overridden in `BailingMoeBridge` — check that the base class signature is stable.\n\n______________________________________________________________________\n\n### 8. `Bridge._weight_merge_across_tp`\n\n**Source:** `mbridge/core/bridge.py` or `mbridge/core/llm_bridge.py`\n\nCalled in `areal/models/mcore/hf_save.py` (lines 414–415):\n\n```python\ninfer_params = bridge._weight_merge_across_tp(\n    s.global_name, infer_params, param\n)\n```\n\n**Check:** Confirm the method accepts three positional arguments: `mcore_weights_name`\n(str), `tp_shards` (list of tensors), `param` (tensor). Verify the return type is still\na `torch.Tensor`. This is overridden in `BailingMoeBridge` — check base class stability.\n\n______________________________________________________________________\n\n### 9. `Bridge._weight_to_hf_format`\n\n**Source:** `mbridge/core/bridge.py` or `mbridge/core/llm_bridge.py`\n\nCalled in `areal/models/mcore/hf_save.py` (lines 422–423):\n\n```python\nconverted_names, converted_params = bridge._weight_to_hf_format(\n    s.global_name, infer_params\n)\n```\n\n**Check:** Confirm the method accepts two positional arguments: `mcore_weights_name`\n(str) and `mcore_weights` (tensor). Verify the return type is still\n`tuple[list[str], list[torch.Tensor]]`. This is overridden in `BailingMoeBridge` — check\nbase class stability.\n\n______________________________________________________________________\n\n### 10. `mbridge.core.util.unwrap_model`\n\n**Source:** `mbridge/core/util.py`\n\nCalled in `areal/models/mcore/hf_save.py` (line 237):\n\n```python\nmodels = [unwrap_model(model) for model in models]\n```\n\n**Check:** Confirm the function still accepts a single model argument. Verify it returns\nthe unwrapped model (strips DDP/FSDP wrappers). Check if the function was moved to a\ndifferent module.\n\n______________________________________________________________________\n\n### 11. `LLMBridge` (subclassing contract)\n\n**Source:** `mbridge/core/llm_bridge.py`\n\nSubclassed in `areal/models/mcore/bailing_moe_bridge.py` (line 87):\n\n```python\nclass BailingMoeBridge(LLMBridge):\n    TransformerConfigClass = MLATransformerConfig\n\n    def _build_config(self): ...\n    def _get_gptmodel_args(self) -> dict: ...\n    def _get_transformer_layer_spec(self, vp_stage: int | None = None): ...\n    def _weight_name_mapping_mcore_to_hf(self, mcore_weights_name: str) -> list[str]: ...\n    def _weight_merge_across_tp(self, mcore_weights_name, tp_shards, param): ...\n    def _weight_to_mcore_format(self, mcore_weights_name, hf_weights): ...\n    def _weight_to_hf_format(self, mcore_weights_name, mcore_weights): ...\n```\n\n**Check:** Confirm `LLMBridge` still exposes these overridable methods with compatible\nsignatures. Verify `TransformerConfigClass` class attribute is still supported. Check if\nany abstract methods were added that subclasses must now implement. Verify\n`_build_base_config(...)` still accepts the keyword arguments used in\n`BailingMoeBridge._build_config()` (especially MoE and MLA parameters). Check if\n`has_vp_stage` attribute is still read by the base class.\n\n______________________________________________________________________\n\n### 12. `register_model` decorator\n\n**Source:** `mbridge/core/__init__.py` (re-exported from `mbridge/core/llm_bridge.py` or\nsimilar)\n\nUsed in `areal/models/mcore/bailing_moe_bridge.py` (lines 84–86):\n\n```python\n@register_model(\"bailing_moe_v2\")\n@register_model(\"bailing_moe_linear\")\n@register_model(\"bailing_hybrid\")\nclass BailingMoeBridge(LLMBridge):\n    ...\n```\n\n**Check:** Confirm `register_model` is still importable from `mbridge.core`. Verify it\nstill accepts a single string argument (model type name). Verify it still works as a\nclass decorator that registers the bridge class for `AutoBridge.from_pretrained`\ndispatch. Check if the registration mechanism changed (e.g., from decorator to explicit\nregistry call).\n\n______________________________________________________________________\n\n### 13. `LLMBridge._get_transformer_layer_spec` (monkey-patch target)\n\n**Source:** `mbridge/core/llm_bridge.py`\n\nMonkey-patched in `areal/models/tree_attn/module_megatron.py` (lines 195–213):\n\n```python\noriginal_layer_spec_getter = LLMBridge._get_transformer_layer_spec\n\ndef _patched_getter(self, vp_stage: int | None = None):\n    spec: TransformerBlockSubmodules = original_layer_spec_getter(self, vp_stage)\n    for layer_spec in spec.layer_specs:\n        ...\n    return spec\n\nLLMBridge._get_transformer_layer_spec = _patched_getter\n```\n\n**Check:** Confirm `_get_transformer_layer_spec` still exists on `LLMBridge` with\nsignature `(self, vp_stage: int | None = None)`. Verify the return type is still\n`TransformerBlockSubmodules` with a `.layer_specs` attribute (list of layer specs). Each\nlayer spec must still have `.module`, `.submodules`, and\n`.submodules.self_attention.submodules.core_attention` that can be reassigned. If the\nmethod was renamed or its return structure changed, the monkey-patch will silently fail.\n\n______________________________________________________________________\n\n### 14. `Bridge.hf_config` and `Bridge.config` (properties)\n\n**Source:** `mbridge/core/bridge.py`\n\nAccessed throughout:\n\n- `bridge.hf_config` → HuggingFace `PretrainedConfig` instance (used in hf_load.py,\n  hf_save.py, registry.py, bailing_moe_bridge.py)\n- `bridge.config` → Megatron `TransformerConfig` instance (used in hf_save.py,\n  registry.py)\n\n```python\n# hf_load.py\nbridge.hf_config  # .num_key_value_heads, .hidden_size, .num_attention_heads, .quantization_config\nbridge.config     # .fp8, .fp8_param\nbridge.dtype      # torch.dtype\n\n# registry.py\nbridge.hf_config  # ._name_or_path\nbridge.config     # returned as TransformerConfig\n```\n\n**Check:** Confirm `hf_config` still returns a HuggingFace `PretrainedConfig` (or\nsubclass). Confirm `config` still returns a Megatron `TransformerConfig` with at least\n`.fp8`, `.fp8_param`, `.num_moe_experts` attributes. These are the most widely accessed\nproperties — any type change will cascade across all Primary files.\n\n______________________________________________________________________\n\n## Version-Guarded Code\n\n_None currently._ The `mbridge` package is pinned to a git commit (`310e8fb`) rather\nthan a versioned release, so no version-conditional code exists.\n\n<!-- File: .agents/skills/upgrade-deps/checklists/megatron-bridge.md (Tokens: ~2166 | Category: anthropic-skill) -->\n---\npackage: megatron-bridge\ngithub: NVIDIA-NeMo/Megatron-Bridge\nbranch_template: v${VERSION}\nupstream_paths:\n  - megatron/bridge/__init__.py\n  - megatron/bridge/models/conversion/auto_bridge.py\n  - megatron/bridge/models/conversion/param_mapping.py\n  - megatron/bridge/models/qwen_vl/modelling_qwen3_vl/text_model.py\n  - megatron/bridge/peft/lora.py\n---\n\n## Affected Files\n\n### Primary (engine layer — most likely to break)\n\n| File                                           | Imports / Usage                                                |\n| ---------------------------------------------- | -------------------------------------------------------------- |\n| `areal/engine/megatron_engine.py`              | `megatron.bridge.AutoBridge`, `megatron.bridge.peft.lora.LoRA` |\n| `areal/engine/megatron_utils/megatron_lora.py` | `megatron.bridge.AutoBridge` (inside function, monkey-patched) |\n\n### Secondary (model / infra layer)\n\n| File                                        | Imports / Usage                                                   |\n| ------------------------------------------- | ----------------------------------------------------------------- |\n| `areal/models/mcore/vocab_parallel_head.py` | `AutoMapping.register_module_type` for dynamic LM-head subclasses |\n\n### Tertiary (tests, config)\n\n| File                                          | Imports / Usage                                                                   |\n| --------------------------------------------- | --------------------------------------------------------------------------------- |\n| `areal/tools/validation_base.py`              | `\"megatron-bridge\"` → `\"megatron.bridge\"` in `PACKAGE_IMPORT_MAP` (metadata only) |\n| `areal/tools/validate_docker_installation.py` | Megatron Bridge package validation metadata                                       |\n| `tests/test_bailing_v3_nccl.py`               | Conditional Bridge availability and real conversion integration                   |\n| `tests/test_megatron_bridge_deterministic.py` | Bridge provider determinism integration                                           |\n| `tests/test_megatron_engine.py`               | Megatron engine integration through Bridge-backed paths                           |\n| `tests/test_megatron_transport.py`            | Conditional import of Bridge-backed Megatron engine                               |\n| `tests/test_megatron_lm_head.py`              | `AutoMapping` registry assertion for promoted LM-head subclasses                  |\n\n______________________________________________________________________\n\n## API Usage Catalog\n\nFor each function/class below, verify the call signature against the upstream source at\nthe target version. Focus on: **missing new required parameters**, **removed old\nparameters**, **renamed parameters**, **changed return types**, **changed method\nsignatures on returned objects**, and **moved/renamed modules**.\n\n### 1. `megatron.bridge.AutoBridge.from_hf_pretrained`\n\n**Source:** `megatron/bridge/models/conversion/auto_bridge.py`\n\nCalled in `areal/engine/megatron_engine.py` (line 430):\n\n```python\nself.bridge = MegatronBridgeAutoBridge.from_hf_pretrained(\n    self.config.path,\n    trust_remote_code=True,\n    dtype=self.config.dtype,\n)\n```\n\n**Check:** Confirm `trust_remote_code` and `dtype` are still accepted keyword arguments.\nVerify the first positional arg is still the model path. Verify the method still returns\na bridge object that exposes `save_hf_pretrained`, `load_hf_weights`, and (depending on\nversion) `save_hf_adapter`. Check for any new required parameters.\n\n______________________________________________________________________\n\n### 2. `megatron.bridge.AutoBridge.save_hf_pretrained`\n\n**Source:** `megatron/bridge/models/conversion/auto_bridge.py`\n\nCalled in `areal/engine/megatron_engine.py` (line 1561):\n\n```python\nbridge.save_hf_pretrained(model, path, source_path=base_model_path)\n```\n\n**Check:** Confirm `source_path` is still a valid keyword argument. Verify positional\norder of `model` and `path` hasn't changed. Check return type (currently void/`None`).\n\n______________________________________________________________________\n\n### 3. `megatron.bridge.AutoBridge.load_hf_weights`\n\n**Source:** `megatron/bridge/models/conversion/auto_bridge.py`\n\nCalled in `areal/engine/megatron_engine.py` (line 1595):\n\n```python\nbridge.load_hf_weights(model, hf_path=path)\n```\n\n**Check:** Confirm `hf_path` is still the correct keyword name. Verify `model` is still\nthe first positional argument. Check for newly added required arguments.\n\n______________________________________________________________________\n\n### 4. `megatron.bridge.AutoBridge.save_hf_adapter`\n\n**Source:** `megatron/bridge/models/conversion/auto_bridge.py`\n\nCalled in `areal/engine/megatron_engine.py` (lines 1554-1559) via the monkey-patched\nmethod on the bridge instance:\n\n```python\nself.bridge.save_hf_adapter(\n    self.model,\n    path=path,\n    peft_config=self.bridge_lora,\n    base_model_name_or_path=base_model_path or self.config.path,\n)\n```\n\n**Check:** If the new version adds this method natively, confirm its signature matches\nthe monkey-patch in `areal/engine/megatron_utils/megatron_lora.py` (line 189). The\nmonkey-patched signature is:\n\n```python\ndef save_hf_adapter(\n    self, model, path, peft_config, base_model_name_or_path=None, show_progress=True\n)\n```\n\nAny mismatch in parameter names or order will silently break adapter saving. Note that\n`peft_config` receives a `megatron.bridge.peft.lora.LoRA` instance (not a dict). See\nalso the [Version-Guarded Code](#version-guarded-code) section.\n\n______________________________________________________________________\n\n### 5. `megatron.bridge.AutoBridge.export_adapter_weights`\n\n**Source:** `megatron/bridge/models/conversion/auto_bridge.py`\n\nCalled inside the monkey-patched `save_hf_adapter` in\n`areal/engine/megatron_utils/megatron_lora.py` (lines 237-240):\n\n```python\nfor name, tensor in self.export_adapter_weights(\n    model, cpu=True, show_progress=show_progress\n):\n    adapter_state[f\"base_model.model.{name}\"] = tensor.clone().float()\n```\n\n**Check:** Confirm `cpu` and `show_progress` are still accepted. Verify the method\nyields `(name, tensor)` tuples (iterable of pairs). The names are module FQNs without\nthe `base_model.model.` prefix — that prefix is added by the caller. This is a native\nbridge method used inside the monkey-patch — if its return type or signature changes,\nthe patch breaks.\n\n______________________________________________________________________\n\n### 6. `megatron.bridge.peft.lora.LoRA`\n\n**Source:** `megatron/bridge/peft/lora.py`\n\nCalled in `areal/engine/megatron_engine.py` (line 233):\n\n```python\nbridge_lora = MegatronBridgeLoRA(\n    target_modules=target_modules,\n    dim=lora_rank,\n    alpha=lora_alpha,\n    dropout=0.0,\n)\n```\n\n**Check:** Confirm `dim` is still the rank parameter (not renamed to `r` or `rank`).\nVerify `alpha` and `dropout` are still accepted. Check the `target_modules` accepted\ntype (list of strings vs. regex).\n\n______________________________________________________________________\n\n### 7. `LoRA.__call__` (apply to model)\n\n**Source:** `megatron/bridge/peft/lora.py`\n\nCalled in `areal/engine/megatron_engine.py` (lines 239-240):\n\n```python\nself.model = _MegatronModelList(self.bridge_lora(self.model, training=True))\nself.bridge_lora.set_params_to_save(self.model)\n```\n\n**Check:** Confirm `LoRA` instances are still callable with `(model, training=...)`.\nVerify the return type — it returns a modified model (or list of model chunks), which is\nthen wrapped in `_MegatronModelList`. Confirm `set_params_to_save(model)` still exists\nand marks LoRA parameters for checkpoint saving. Check if `training=True` is still the\ncorrect keyword to enable grad on LoRA parameters.\n\n______________________________________________________________________\n\n### 8. Qwen3-VL model forward contract\n\n**Source:** `megatron/bridge/models/qwen_vl/modelling_qwen3_vl/text_model.py`\n\nCalled through `packed_context_parallel_forward` in\n`areal/engine/megatron_utils/packed_context_parallel.py`:\n\n```python\noutput = model(\n    input_ids=input_ids,\n    attention_mask=attention_mask,\n    position_ids=position_ids,\n    packed_seq_params=packed_seq_params,\n    loss_mask=mtp_loss_mask,\n)\n```\n\n**Check:** Verify `Qwen3VLModel.forward` and its `Qwen3VLGPTModel` decoder accept\n`loss_mask`, preserve `labels=None` logits output, and pass layout-aligned `input_ids`\nto MCore's native MTP path. Confirm the upstream shadow embedding exposes\n`word_embeddings`; AReaL no longer patches either class.\n\n______________________________________________________________________\n\n### 9. `AutoMapping.register_module_type`\n\n**Source:** `megatron/bridge/models/conversion/param_mapping.py`\n\nCalled after dynamically promoting an MCore output layer in\n`areal/models/mcore/vocab_parallel_head.py`:\n\n```python\nAutoMapping.register_module_type(output_layer.__class__.__name__, \"column\")\n```\n\n**Check:** Confirm the import path, classmethod signature, accepted mapping type\n(`\"column\"`), and registry lifetime. This mapping must allow Bridge to export dynamic\nAReAL LM-head subclasses without replacing their parameters or hooks.\n\n______________________________________________________________________\n\n## Version-Guarded Code\n\n- `areal/engine/megatron_utils/megatron_lora.py:185` —\n  `hasattr(AutoBridge, \"save_hf_adapter\")` guard. The monkey-patch at line 291 is only\n  applied when `save_hf_adapter` does not exist on `AutoBridge`. The module-level call\n  at line 298 (`_monkey_patch_save_hf_adapter()`) runs at import time, so the guard is\n  evaluated once on first import. If upgrading to a version that ships `save_hf_adapter`\n  natively, the guard will skip patching, but the native signature must then match what\n  AReaL's call site expects (see entry 4 above). Once confirmed compatible, the entire\n  function `_monkey_patch_save_hf_adapter()` and its line-298 invocation can be removed.\n\n<!-- File: .agents/skills/upgrade-deps/checklists/megatron-core.md (Tokens: ~6626 | Category: anthropic-skill) -->\n---\npackage: megatron-core\ngithub: NVIDIA/Megatron-LM\nbranch_template: core_v${VERSION}\nupstream_paths:\n  - megatron/core/parallel_state.py\n  - megatron/core/distributed/\n  - megatron/core/optimizer/\n  - megatron/core/optimizer_param_scheduler.py\n  - megatron/core/pipeline_parallel/\n  - megatron/core/tensor_parallel/\n  - megatron/core/transformer/transformer_config.py\n  - megatron/core/transformer/pipeline_parallel_layer_layout.py\n  - megatron/core/transformer/multi_token_prediction.py\n  - megatron/core/transformer/multi_latent_attention.py\n  - megatron/core/transformer/spec_utils.py\n  - megatron/core/transformer/transformer_block.py\n  - megatron/core/transformer/transformer_layer.py\n  - megatron/core/dist_checkpointing/\n  - megatron/core/dist_checkpointing/mapping.py\n  - megatron/core/dist_checkpointing/serialization.py\n  - megatron/core/dist_checkpointing/strategies/async_utils.py\n  - megatron/core/dist_checkpointing/strategies/fully_parallel.py\n  - megatron/core/fp8_utils.py\n  - megatron/core/models/gpt/\n  - megatron/core/packed_seq_params.py\n  - megatron/core/models/common/embeddings/rotary_pos_embedding.py\n  - megatron/core/utils.py\n---\n\n## Affected Files\n\n### Primary (engine layer — most likely to break)\n\n| File                                                     | Imports / Usage                                                                                                                                                                                                  |\n| -------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `areal/engine/megatron_engine.py`                        | `parallel_state`, `tensor_parallel`, `DDP`, `finalize_model_grads`, `OptimizerConfig`, `get_megatron_optimizer`, `OptimizerParamScheduler`, `get_forward_backward_func`, `TransformerConfig`, `get_model_config` |\n| `areal/engine/megatron_utils/checkpointer.py`            | `dist_checkpointing.save`, `load`, sharded strategies, `ShardedObject`                                                                                                                                           |\n| `areal/engine/megatron_utils/megatron.py`                | `parallel_state`, FP8 detection, `TransformerConfig`                                                                                                                                                             |\n| `areal/engine/megatron_utils/pipeline_parallel.py`       | `TransformerConfig`, `PipelineParallelLayerLayout`                                                                                                                                                               |\n| `areal/engine/megatron_utils/packed_context_parallel.py` | `PackedSeqParams`, `parallel_state`                                                                                                                                                                              |\n| `areal/engine/megatron_utils/fp8/tensor_helper.py`       | `fp8_utils.is_float8tensor`                                                                                                                                                                                      |\n| `areal/engine/megatron_utils/deterministic.py`           | `AttnBackend` enum for deterministic attention selection                                                                                                                                                         |\n| `areal/engine/megatron_utils/weight_residency.py`        | `DistributedDataParallel` flat-buffer residency (`buffers`, `expert_parallel_buffers`)                                                                                                                           |\n\n### Secondary (model / infra layer)\n\n| File                                              | Imports / Usage                                                                                              |\n| ------------------------------------------------- | ------------------------------------------------------------------------------------------------------------ |\n| `areal/models/mcore/registry.py`                  | `GPTModel`, `DDP`, `TransformerConfig`, `parallel_state`, tensor-parallel utilities                          |\n| `areal/models/mcore/bailing_moe.py`               | `MLATransformerConfig`, layer specs/builders, transformer blocks, TE/torch/fused norms                       |\n| `areal/models/mcore/bailing_v3.py`                | `MLATransformerConfig`, layer specs/builders, transformer blocks, TE/TP linears, TE/torch/fused norms        |\n| `areal/models/mcore/bailing_v3_bridge.py`         | `MLATransformerConfig`, `AttnBackend`, `parallel_state`                                                      |\n| `areal/models/mcore/bailing_v3_mla.py`            | `MLASelfAttention`, `build_module`, TE/TP column-parallel linear                                             |\n| `areal/models/mcore/lightning_attention.py`       | `parallel_state`, `apply_rotary_pos_emb`, `MegatronModule`, `ModuleSpec`, `build_module`, checkpoint helpers |\n| `areal/models/mcore/kda_attention.py`             | Sharded checkpoint types, `PackedSeqParams`, TP mappings/RNG, transformer module/spec/checkpoint helpers     |\n| `areal/models/mcore/hf_load.py`                   | `parallel_state`, `fp8_utils.is_float8tensor`                                                                |\n| `areal/models/mcore/hf_save.py`                   | `parallel_state`, `fp8_utils.is_float8tensor`                                                                |\n| `areal/models/mcore/vocab_parallel_head.py`       | Tensor-parallel layers and collectives                                                                       |\n| `areal/models/tree_attn/module_megatron.py`       | `PackedSeqParams`, `TransformerConfig`, `SelfAttention`, `AttnMaskType`, transformer layer/block specs       |\n| `areal/models/mcore/bailing_moe_bridge.py`        | `MLATransformerConfig`, `AttnBackend`                                                                        |\n| `areal/models/mcore/common.py`                    | `TransformerConfig`                                                                                          |\n| `areal/models/mcore/qwen3.py`                     | `gpt_layer_specs`, `TransformerConfig`                                                                       |\n| `areal/v2/weight_update/awex/delta_detect.py`     | Lazy `parallel_state` access for Megatron delta ownership                                                    |\n| `areal/v2/weight_update/awex/megatron_adapter.py` | Lazy `parallel_state` access for AWEX Megatron rank/group handling                                           |\n\n### Tertiary (tests, infra)\n\n| File                                                | Imports / Usage                                                   |\n| --------------------------------------------------- | ----------------------------------------------------------------- |\n| `areal/infra/workflow_executor.py`                  | conditional `parallel_state` for DP world size                    |\n| `tests/test_estimate_num_params.py`                 | `parallel_state`, `tensor_parallel`                               |\n| `tests/fp8/engine_utils.py`                         | `parallel_state`                                                  |\n| `tests/fp8/model_hooks.py`                          | `parallel_state`                                                  |\n| `tests/fp8/test_fp8_rmsnorm.py`                     | `fp8_utils`, `get_model_config`                                   |\n| `tests/torchrun/run_megatron_engine_distributed.py` | `parallel_state`                                                  |\n| `tests/test_megatron_engine_vlm.py`                 | `GPTModel` unwrapping and VLM engine contracts                    |\n| `tests/test_megatron_lm_head.py`                    | TP layers, `TransformerConfig`, `Float16Module`, PP helpers       |\n| `tests/test_reassemble_cp_logprobs.py`              | Conditional `megatron.core` availability                          |\n| `tests/torchrun/run_vocab_parallel.py`              | `parallel_state`, `ColumnParallelLinear`, `TransformerConfig`     |\n| `areal/tools/validation_base.py`                    | Megatron package/import metadata validation                       |\n| `areal/tools/validate_docker_installation.py`       | Megatron package and submodule smoke imports                      |\n| `tests/test_bailing_v3_hf_load.py`                  | Stubbed `parallel_state` and FP8 import contracts                 |\n| `tests/test_bailing_v3_kda_cp_helpers.py`           | Stubbed KDA checkpoint, packed-sequence, TP, and transformer APIs |\n| `tests/test_bailing_v3_nccl.py`                     | Conditional real MCore integration                                |\n| `tests/test_deterministic_prebuild.py`              | Conditional `AttnBackend` integration                             |\n| `tests/test_megatron_async_save.py`                 | Stubbed async distributed-checkpoint contracts                    |\n| `tests/test_megatron_bridge_deterministic.py`       | Conditional MCore bridge-provider integration                     |\n| `tests/test_megatron_engine.py`                     | Megatron engine integration and package version reporting         |\n| `tests/test_megatron_transport.py`                  | Conditional Megatron engine transport integration                 |\n| `tests/torchrun/run_mopd_teacher_residency.py`      | Patched MCore DDP flat-buffer residency                           |\n\n______________________________________________________________________\n\n## API Usage Catalog\n\nFor each function/class below, verify the call signature against the upstream source at\nthe target version. Focus on: **missing new required parameters**, **removed old\nparameters**, **renamed parameters**, **changed return types**, **changed method\nsignatures on returned objects**, and **moved/renamed modules**.\n\n### 1. `megatron.core.parallel_state.initialize_model_parallel`\n\n**Source:** `megatron/core/parallel_state.py`\n\nCalled in `areal/engine/megatron_engine.py` (line 195):\n\n```python\nmpu.initialize_model_parallel(\n    tensor_model_parallel_size=self.parallel_strategy.tensor_parallel_size,\n    pipeline_model_parallel_size=self.parallel_strategy.pipeline_parallel_size,\n    virtual_pipeline_model_parallel_size=vpp_size if vpp_size > 1 else None,\n    use_sharp=False,\n    order=\"tp-cp-ep-dp-pp\",\n    context_parallel_size=self.parallel_strategy.context_parallel_size,\n    expert_model_parallel_size=self.parallel_strategy.expert_parallel_size,\n    expert_tensor_parallel_size=self.parallel_strategy.expert_tensor_parallel_size,\n    distributed_timeout_minutes=int(DIST_GROUP_DEFAULT_TIMEOUT.seconds / 60),\n)\n```\n\n**Check:** Verify all keyword arguments still accepted — especially\n`expert_tensor_parallel_size` (recently added) and `distributed_timeout_minutes`. This\nis the most critical call — any signature change blocks ALL Megatron training. Confirm\n`order` string format `\"tp-cp-ep-dp-pp\"`.\n\n______________________________________________________________________\n\n### 2. `megatron.core.parallel_state` getter functions\n\n**Source:** `megatron/core/parallel_state.py`\n\nCalled extensively in `areal/engine/megatron_engine.py`, `areal/models/mcore/*.py`:\n\n```python\nmpu.get_data_parallel_rank()\nmpu.get_data_parallel_world_size()\nmpu.get_data_parallel_group()\nmpu.get_tensor_model_parallel_rank()\nmpu.get_tensor_model_parallel_world_size()\nmpu.get_tensor_model_parallel_group()\nmpu.get_pipeline_model_parallel_rank()\nmpu.get_pipeline_model_parallel_world_size()\nmpu.get_pipeline_model_parallel_group()\nmpu.get_context_parallel_rank()\nmpu.get_context_parallel_world_size()\nmpu.get_context_parallel_group()\nmpu.get_expert_model_parallel_rank()\nmpu.get_expert_model_parallel_world_size()\nmpu.is_pipeline_last_stage()\nmpu.is_pipeline_first_stage()\nmpu.model_parallel_is_initialized()\nmpu.destroy_model_parallel()\n```\n\n**Check:** Confirm all getter functions still exist with same no-arg signatures. These\nare called dozens of times across the codebase — any removal or rename is immediately\nfatal.\n\n______________________________________________________________________\n\n### 3. `megatron.core.parallel_state` utility functions\n\n**Source:** `megatron/core/parallel_state.py`\n\nCalled in `areal/engine/megatron_engine.py` (line 1048):\n\n```python\nrank_generator = mpu.RankGenerator(\n    tp=self.parallel_strategy.tensor_parallel_size,\n    ep=1,\n    dp=self.parallel_strategy.data_parallel_size,\n    pp=self.parallel_strategy.pipeline_parallel_size,\n    cp=self.parallel_strategy.context_parallel_size,\n    order=\"tp-cp-ep-dp-pp\",\n    rank_offset=0,\n)\ncontext_and_model_parallel_ranks = rank_generator.get_ranks(\"tp-cp-pp\")\n\ngroup = mpu.create_group(\n    ranks,\n    timeout=DIST_GROUP_DEFAULT_TIMEOUT,\n    pg_options=mpu.get_nccl_options(\"tp-cp-pp\", {}),\n    group_desc=\"CONTEXT_AND_MODEL_PARALLEL_GROUP\",\n)\n```\n\n**Check:** Verify `RankGenerator.__init__` keyword args — especially `rank_offset` and\n`order`. Confirm `get_ranks(group_str)` method. Verify `create_group` accepts `timeout`,\n`pg_options`, `group_desc` kwargs. Confirm `get_nccl_options(group_name, config_dict)`\nsignature.\n\n______________________________________________________________________\n\n### 4. `megatron.core.distributed.DistributedDataParallel`\n\n**Source:** `megatron/core/distributed/`\n\nCalled in `areal/models/mcore/registry.py` (line 310):\n\n```python\nfrom megatron.core.distributed import DistributedDataParallel as DDP\nfrom megatron.core.distributed import DistributedDataParallelConfig as MCoreDDPConfig\n\nddp_config = MCoreDDPConfig(**dataclasses.asdict(mcore_config.ddp))\nwrapped = DDP(\n    config=tf_config,\n    ddp_config=ddp_config,\n    module=model,\n    disable_bucketing=False,\n)\n```\n\n**Check:** Verify keyword arguments `config`, `ddp_config`, `module`,\n`disable_bucketing` are still accepted. Note AReaL does NOT pass `data_parallel_group`\nor `expert_data_parallel_group` — confirm these remain optional with sane defaults.\nCheck for new required parameters. Also verify `MCoreDDPConfig` dataclass fields haven't\nchanged (constructed from `mcore_config.ddp`).\n\n______________________________________________________________________\n\n### 5. `megatron.core.distributed.finalize_model_grads`\n\n**Source:** `megatron/core/distributed/`\n\nAssigned in `areal/engine/megatron_engine.py` (line 403):\n\n```python\nmodel_config.finalize_model_grads_func = finalize_model_grads\n```\n\nNot called directly by AReaL — instead assigned to\n`TransformerConfig.finalize_model_grads_func` and invoked internally by Megatron's\npipeline parallel forward-backward functions.\n\n**Check:** Confirm `finalize_model_grads` still has a compatible signature for\n`TransformerConfig.finalize_model_grads_func`. Verify it accepts `(model_chunks)` or\n`(model_chunks, num_tokens=...)` as expected by the pipeline executor.\n\n______________________________________________________________________\n\n### 6. `megatron.core.optimizer.OptimizerConfig`\n\n**Source:** `megatron/core/optimizer/`\n\nCalled in `areal/engine/megatron_engine.py` (line 1090):\n\n```python\nmcore_opt_config = MCoreOptimizerConfig(\n    optimizer=self.optimizer_config.type,\n    lr=self.optimizer_config.lr,\n    min_lr=self.optimizer_config.min_lr_ratio * self.optimizer_config.lr,\n    weight_decay=self.optimizer_config.weight_decay,\n    bf16=self.dtype is torch.bfloat16,\n    fp16=self.dtype is torch.float16,\n    adam_beta1=self.optimizer_config.beta1,\n    adam_beta2=self.optimizer_config.beta2,\n    adam_eps=self.optimizer_config.eps,\n    use_distributed_optimizer=use_distributed_optimizer,\n    params_dtype=self.dtype,\n    clip_grad=self.optimizer_config.gradient_clipping,\n    fp8_recipe=(self.fp8_config.recipe if self.enable_fp8 else None),\n)\n# Post-init field assignments:\nmcore_opt_config.overlap_param_gather_with_optimizer_step = ...\nmcore_opt_config.use_precision_aware_optimizer = ...\nmcore_opt_config.main_grads_dtype = getattr(torch, ...)\nmcore_opt_config.main_params_dtype = getattr(torch, ...)\nmcore_opt_config.exp_avg_dtype = getattr(torch, ...)\nmcore_opt_config.exp_avg_sq_dtype = getattr(torch, ...)\n```\n\n**Check:** Verify all ~13 constructor kwargs still accepted — especially `fp8_recipe`\nand `fp16`. Confirm the 6 post-init fields (`overlap_param_gather_with_optimizer_step`,\n`use_precision_aware_optimizer`, `main_grads_dtype`, `main_params_dtype`,\n`exp_avg_dtype`, `exp_avg_sq_dtype`) still exist on the dataclass. Check for renamed or\nremoved fields.\n\n______________________________________________________________________\n\n### 7. `megatron.core.optimizer.get_megatron_optimizer`\n\n**Source:** `megatron/core/optimizer/`\n\nCalled in `areal/engine/megatron_engine.py` (line 1122):\n\n```python\nself.optimizer = get_megatron_optimizer(mcore_opt_config, self.model)\n```\n\n**Check:** Verify the two-arg call `(config, model_chunks)` is still valid. Confirm\n`model_chunks` accepts a list of DDP-wrapped modules. Check if new required arguments\nwere added (e.g., `no_weight_decay_cond`, `scale_lr_cond` — AReaL does not pass these,\nso confirm they remain optional).\n\n______________________________________________________________________\n\n### 8. `megatron.core.optimizer_param_scheduler.OptimizerParamScheduler`\n\n**Source:** `megatron/core/optimizer_param_scheduler.py`\n\nCalled in `areal/engine/megatron_engine.py` (line 1126):\n\n```python\nlr_scheduler = OptimizerParamScheduler(\n    self.optimizer,\n    init_lr=0.0 if warmup_steps_proportion > 0 else self.optimizer_config.lr,\n    max_lr=self.optimizer_config.lr,\n    min_lr=self.optimizer_config.min_lr_ratio * self.optimizer_config.lr,\n    lr_warmup_steps=warmup_steps,\n    lr_decay_steps=ft_spec.total_train_steps - warmup_steps,\n    lr_decay_style=self.optimizer_config.lr_scheduler_type,\n    start_wd=self.optimizer_config.weight_decay,\n    end_wd=self.optimizer_config.weight_decay,\n    wd_incr_steps=ft_spec.total_train_steps,\n    wd_incr_style=\"constant\",\n)\n```\n\n**Check:** Confirm all keyword arguments still accepted — especially `start_wd`,\n`end_wd`, `wd_incr_steps`, `wd_incr_style` (weight decay scheduling). Verify\n`lr_decay_style` valid choices (e.g., `\"cosine\"`, `\"linear\"`, `\"WSD\"`). Check for\nrenamed params.\n\n______________________________________________________________________\n\n### 9. `megatron.core.pipeline_parallel.get_forward_backward_func`\n\n**Source:** `megatron/core/pipeline_parallel/`\n\nCalled in `areal/engine/megatron_engine.py` (line 732, hot training loop):\n\n```python\nforward_backward_func = get_forward_backward_func()\nforward_backward_func(\n    forward_step_func=forward_step,\n    data_iterator=data_iterator,\n    model=self.model if len(self.model) > 1 else self.model[0],\n    num_microbatches=len(mb_list),\n    seq_length=mb_list.max_seqlen,\n    micro_batch_size=1,\n    forward_only=forward_only,\n)\n```\n\n**Check:** Verify `get_forward_backward_func()` still returns a callable with this\nsignature. Confirm `seq_length` and `micro_batch_size` kwargs are still accepted (marked\n\"no use when input_shapes was set\" in code). This is in the HOT training loop — any\nbreakage stops all PP training.\n\n______________________________________________________________________\n\n### 10. `megatron.core.transformer.TransformerConfig`\n\n**Source:** `megatron/core/transformer/transformer_config.py`\n\nUsed in `areal/engine/megatron_engine.py`,\n`areal/engine/megatron_utils/pipeline_parallel.py`, `areal/models/mcore/registry.py`,\n`areal/models/mcore/common.py`, `areal/models/mcore/qwen3.py`:\n\n```python\ntransformer_config = TransformerConfig(\n    num_layers=num_layers,\n    hidden_size=hidden_size,\n    num_attention_heads=num_heads,\n    num_key_value_heads=num_kv_heads,\n    # ... many more fields\n)\n```\n\n**Check:** This is the central config dataclass — verify no fields were removed or\nrenamed. New optional fields with defaults are fine. Check `MLATransformerConfig`\nsubclass compatibility (used in `bailing_moe.py` and `bailing_moe_bridge.py`).\n\n______________________________________________________________________\n\n### 11. `megatron.core.dist_checkpointing` save/load\n\n**Source:** `megatron/core/dist_checkpointing/`\n\nCalled in `areal/engine/megatron_utils/checkpointer.py` (lines 23-32, 53-85):\n\n```python\nfrom megatron.core import dist_checkpointing\nfrom megatron.core.dist_checkpointing.mapping import ShardedObject\nfrom megatron.core.dist_checkpointing.serialization import (\n    get_default_save_sharded_strategy,\n    get_default_load_sharded_strategy,\n)\nfrom megatron.core.dist_checkpointing.strategies.fully_parallel import (\n    FullyParallelLoadStrategyWrapper,\n    FullyParallelSaveStrategyWrapper,\n)\n\n# Save (line 62):\nsave_strategy = get_default_save_sharded_strategy(\"torch_dist\")\nsave_strategy = FullyParallelSaveStrategyWrapper(save_strategy, dp_group)\ndist_checkpointing.save(\n    sharded_state_dict, ckpt_path,\n    sharded_strategy=save_strategy,\n    async_sharded_save=async_save,\n    validate_access_integrity=validate_sharding_integrity,\n)\n\n# Load (line 75):\nload_strategy = get_default_load_sharded_strategy(ckpt_dir)\nload_strategy = FullyParallelLoadStrategyWrapper(load_strategy, dp_group)\nstate_dict = dist_checkpointing.load(\n    sharded_state_dict, ckpt_dir, sharded_strategy=load_strategy\n)\n```\n\n**Check:** Verify `save` kwargs — especially `async_sharded_save` and\n`validate_access_integrity`. Confirm `get_default_save_sharded_strategy` accepts\n`\"torch_dist\"` string. Confirm `get_default_load_sharded_strategy` accepts `ckpt_dir`\npath. Verify import paths: `serialization` for strategy factories,\n`strategies.fully_parallel` for wrappers, `mapping` for `ShardedObject`. Checkpoint\nformat compatibility is critical — verify no format version bumps.\n\n______________________________________________________________________\n\n### 12. `megatron.core.transformer.PipelineParallelLayerLayout`\n\n**Source:** `megatron/core/transformer/pipeline_parallel_layer_layout.py`\n\nCalled in `areal/engine/megatron_utils/pipeline_parallel.py` (line 6):\n\n```python\nfrom megatron.core.transformer.pipeline_parallel_layer_layout import (\n    PipelineParallelLayerLayout,\n)\n```\n\n**Check:** Confirm the import path is still\n`megatron.core.transformer.pipeline_parallel_layer_layout` (not\n`transformer_config.py`). Verify constructor kwargs. Check if\n`get_num_layers_to_build()` and `get_transformer_layer_offset()` are still accessible.\n\n______________________________________________________________________\n\n### 13. `megatron.core.fp8_utils.is_float8tensor`\n\n**Source:** `megatron/core/fp8_utils.py`\n\nCalled in `areal/engine/megatron_utils/fp8/tensor_helper.py`:\n\n```python\nfrom megatron.core.fp8_utils import is_float8tensor\nif is_float8tensor(param):\n    ...\n```\n\n**Check:** Confirm function still exists at this path. Verify it accepts a single tensor\nargument. Check if FP8 support was restructured (e.g., moved to\n`megatron.core.extensions.transformer_engine`).\n\n______________________________________________________________________\n\n### 14. `megatron.core.models.gpt.GPTModel`\n\n**Source:** `megatron/core/models/gpt/`\n\nCalled in `areal/models/mcore/registry.py`:\n\n```python\nmodel = GPTModel(\n    config=transformer_config,\n    transformer_layer_spec=layer_spec,\n    vocab_size=vocab_size,\n    max_sequence_length=max_seq_len,\n    pre_process=pre_process,\n    post_process=post_process,\n)\n```\n\n**Check:** Verify constructor kwargs. Confirm `get_gpt_decoder_block_spec()` still\nreturns a valid layer spec. Check for new required parameters.\n\n______________________________________________________________________\n\n### 15. `megatron.core.tensor_parallel` utilities\n\n**Source:** `megatron/core/tensor_parallel/`\n\nCalled in `areal/engine/megatron_engine.py` and\n`areal/models/mcore/lightning_attention.py`; extended by\n`areal/models/mcore/vocab_parallel_head.py`:\n\n```python\ntensor_parallel.model_parallel_cuda_manual_seed(seed)\ntensor_parallel.gather_from_sequence_parallel_region(output)\ntensor_parallel.get_cuda_rng_tracker()\nlinear_with_areal_output(..., gtp_remat_size=gtp_remat_size)\n```\n\n**Check:** Verify these functions still exist with same signatures.\n`model_parallel_cuda_manual_seed` is called once at init;\n`gather_from_sequence_parallel_region` is in hot path. Keep the custom LM-head autograd\nfunction's forward/backward arity aligned with\n`LinearWithGradAccumulationAndAsyncCommunication`, including `gtp_remat_size`.\n\n______________________________________________________________________\n\n### 16. Transformer layer specs and module builders\n\n**Source:** `megatron/core/transformer/`\n\nCalled in `areal/models/mcore/bailing_moe.py`,\n`areal/models/mcore/lightning_attention.py`,\n`areal/models/mcore/tree_attn/module_megatron.py`:\n\n```python\nfrom megatron.core.transformer import ModuleSpec, build_module\nfrom megatron.core.transformer.enums import AttnMaskType, LayerType\nfrom megatron.core.transformer.transformer_layer import (\n    TransformerLayer, TransformerLayerSubmodules,\n)\nfrom megatron.core.transformer.transformer_block import TransformerBlockSubmodules\nfrom megatron.core.transformer.attention import SelfAttention\nfrom megatron.core.transformer.custom_layers.transformer_engine import (\n    # TE norm modules\n)\n```\n\n**Check:** Verify enum values in `AttnMaskType` and `LayerType`. Confirm `ModuleSpec`\nand `build_module` signatures. Check `SelfAttention.__init__` for new required params.\nVerify TE integration module paths haven't been restructured.\n\n______________________________________________________________________\n\n### 17. `PackedSeqParams`\n\n**Source:** `megatron/core/packed_seq_params.py` (or `megatron/core/transformer/`)\n\nCalled in `areal/engine/megatron_utils/packed_context_parallel.py`:\n\n```python\nfrom megatron.core.packed_seq_params import PackedSeqParams\npacked_seq_params = PackedSeqParams(\n    cu_seqlens_q=cu_seqlens_q,\n    cu_seqlens_kv=cu_seqlens_kv,\n    max_seqlen_q=max_seqlen_q,\n    max_seqlen_kv=max_seqlen_kv,\n    qkv_format=\"thd\",\n)\n```\n\n**Check:** Confirm constructor kwargs and import path. Verify `qkv_format` valid\nchoices.\n\n______________________________________________________________________\n\n### 18. RoPE utilities\n\n**Source:** `megatron/core/models/common/embeddings/`\n\nCalled in `areal/models/mcore/bailing_moe.py`,\n`areal/models/mcore/lightning_attention.py`:\n\n```python\nfrom megatron.core.models.common.embeddings.rotary_pos_embedding import apply_rotary_pos_emb\n# Also various rope_utils for extended RoPE (YaRN, etc.)\n```\n\n**Check:** Verify `apply_rotary_pos_emb` signature (`t`, `freqs`) and the optional MLA\nrotary arguments. MCore 0.19 owns THD+CP frequency selection; AReaL must not reinstall\nthe removed global MLA RoPE workaround.\n\n______________________________________________________________________\n\n### 19. Async distributed-checkpoint request and queue internals\n\n**Source:** `megatron/core/dist_checkpointing/strategies/async_utils.py`\n\nUsed in `areal/engine/megatron_utils/checkpointer.py`:\n\n```python\nfrom megatron.core.dist_checkpointing.strategies.async_utils import (\n    AsyncCallsQueue,\n    AsyncRequest,\n)\n\nsave_kwargs[\"async_strategy\"] = \"mcore\"\nasync_request = dist_checkpointing.save(**save_kwargs)\nqueue = AsyncCallsQueue()\nqueue.schedule_async_request(async_request)\n```\n\nThe checkpoint manager also validates and clears the retained tensor payload through the\nprivate `AsyncRequest.async_fn_args` write-bucket layout after D2H staging.\n\n**Check:** Confirm `AsyncRequest` and `AsyncCallsQueue` import paths and methods. Verify\n`dist_checkpointing.save(..., async_strategy=\"mcore\")` still returns an `AsyncRequest`,\nand audit the exact `async_fn_args` / write-bucket payload structure before retaining\nthe private-reference cleanup optimization.\n\n______________________________________________________________________\n\n### 20. MTP model, loss, and metrics APIs\n\n**Source:** `megatron/core/models/gpt/gpt_model.py` and\n`megatron/core/transformer/multi_token_prediction.py`\n\nConfigured in `areal/models/mcore/registry.py`, forwarded by\n`areal/engine/megatron_utils/packed_context_parallel.py`, and collected in\n`areal/engine/megatron_engine.py`:\n\n```python\nprovider.mtp_detach_heads = True\nmodel(..., labels=None, loss_mask=mtp_loss_mask)\nMTPLossLoggingHelper.reduce_metrics_in_tracker()\nloss = MTPLossLoggingHelper.tracker[\"loss_values\"]\nMTPLossLoggingHelper.clean_metrics_in_tracker()\n```\n\n**Check:** Verify `GPTModel.forward` forwards `input_ids` and `loss_mask` to native\n`process_mtp_loss` when `labels=None`; `mtp_detach_heads` must isolate both hidden state\nand tied/untied output weights. Verify the `MTPLossLoggingHelper` tracker keys and\nreduce/clean method names. These are semi-private contracts and changed in 0.19.\n\n______________________________________________________________________\n\n### 21. MLA configuration, attention backend, and layer-layout helpers\n\n**Source:** `megatron/core/transformer/multi_latent_attention.py`,\n`megatron/core/transformer/enums.py`, `transformer_block.py`, and `transformer_layer.py`\n\nUsed by the Bailing model family and pipeline layout helpers:\n\n```python\nfrom megatron.core.transformer.multi_latent_attention import (\n    MLASelfAttention,\n    MLATransformerConfig,\n)\nfrom megatron.core.transformer.enums import AttnBackend, LayerType\nfrom megatron.core.transformer.transformer_block import get_num_layers_to_build\nfrom megatron.core.transformer.transformer_layer import get_transformer_layer_offset\n```\n\n**Check:** Confirm imports and constructor signatures, enum members used by AReaL, and\nthe helper return types. Verify `MLASelfAttention` still exposes the overridable methods\nconsumed by `bailing_v3_mla.py`.\n\n______________________________________________________________________\n\n### 22. Custom attention module and sharded-state helpers\n\n**Source:** `megatron/core/dist_checkpointing/mapping.py`,\n`megatron/core/tensor_parallel/mappings.py`, and `megatron/core/transformer/`\n\nUsed by `areal/models/mcore/kda_attention.py` and the custom attention modules:\n\n```python\nfrom megatron.core.dist_checkpointing import ShardedTensor\nfrom megatron.core.dist_checkpointing.mapping import ReplicaId, ShardedTensorFactory\nfrom megatron.core.tensor_parallel.mappings import (\n    gather_from_tensor_model_parallel_region,\n    scatter_to_sequence_parallel_region,\n)\nfrom megatron.core.transformer.spec_utils import ModuleSpec, build_module\nfrom megatron.core.transformer.utils import (\n    make_sharded_tensors_for_checkpoint,\n    sharded_state_dict_default,\n)\n```\n\n**Check:** Verify import paths, factory/dataclass fields, TP collective signatures, and\nthe sharded-state helper inputs/returns. Confirm the optional\n`PackedSeqParamsWithSeqidx` import remains safely absent or compatible upstream.\n\n______________________________________________________________________\n\n### 23. `Float16Module` and pipeline-stage helpers\n\n**Source:** `megatron/core/transformer/module.py` and\n`megatron/core/pipeline_parallel/utils.py`\n\nExercised in `tests/test_megatron_lm_head.py` to preserve output casting:\n\n```python\nmodel = Float16Module(config, module)\noutput = model(fp32_output=False)\n```\n\nThe test also patches `is_pp_first_stage`, `is_pp_last_stage`, `is_vp_first_stage`, and\n`is_vp_last_stage` with their process-group/stage arguments.\n\n**Check:** Verify the `Float16Module` constructor/forward pass-through behavior and the\npipeline helper signatures so the AReAL LM-head output override is still propagated.\n\n______________________________________________________________________\n\n## Version-Guarded Code\n\nNo known version-guarded code exists in AReaL for `megatron-core`. However, the\n`MLATransformerConfig` class used in `bailing_moe.py` and `bailing_moe_bridge.py` is a\nrelatively new addition — confirm it exists in the target version.\n\n<!-- File: .agents/skills/upgrade-deps/checklists/peft.md (Tokens: ~2067 | Category: anthropic-skill) -->\n---\npackage: peft\ngithub: huggingface/peft\nbranch_template: v${VERSION}\nupstream_paths:\n  - src/peft/peft_model.py\n  - src/peft/tuners/lora/config.py\n  - src/peft/utils/peft_types.py\n---\n\n## Affected Files\n\n### Primary (engine layer — most likely to break)\n\n| File                          | Imports / Usage                            |\n| ----------------------------- | ------------------------------------------ |\n| `areal/engine/fsdp_engine.py` | `LoraConfig`, `TaskType`, `get_peft_model` |\n\n### Secondary (model / infra layer)\n\n| File                                             | Imports / Usage                                                                                                                                                     |\n| ------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `areal/engine/megatron_engine.py`                | indirect — consumes PEFT config dict schema (`r`, `lora_alpha`, `target_modules`, `bias`) via `megatron.bridge.peft.lora`; stores in `WeightUpdateMeta.peft_config` |\n| `areal/engine/megatron_utils/megatron_lora.py`   | no direct import — generates `adapter_config.json` in PEFT format; parses PEFT weight key format (`base_model.model.layers.N.self_attn.q_proj.lora_A.weight`)       |\n| `areal/engine/vllm_ext/vllm_worker_extension.py` | indirect — uses `vllm.lora.peft_helper.PEFTHelper`, not direct peft import                                                                                          |\n| `areal/engine/vllm_remote.py`                    | builds HTTP payloads containing `peft_config` dict for distributed LoRA updates                                                                                     |\n| `areal/api/cli_args.py`                          | config fields only: `use_lora`, `lora_rank`, `lora_alpha`, `target_modules`, `peft_type`                                                                            |\n| `areal/api/io_struct.py`                         | `WeightUpdateMeta.peft_config: dict` — schema `{\"r\": rank, \"lora_alpha\": alpha, \"target_modules\": [...], \"bias\": \"none\"}`                                           |\n\n### Tertiary (tests, config)\n\n| File                                               | Imports / Usage                                                                     |\n| -------------------------------------------------- | ----------------------------------------------------------------------------------- |\n| `tests/torchrun/run_fsdp_memory_efficient_lora.py` | integration test with `use_lora=True, lora_rank=8, lora_alpha=16, peft_type=\"lora\"` |\n\n______________________________________________________________________\n\n## API Usage Catalog\n\nFor each function/class below, verify the call signature against the upstream source at\nthe target version. Focus on: **missing new required parameters**, **removed old\nparameters**, **renamed parameters**, **changed return types**, **changed method\nsignatures on returned objects**, and **moved/renamed modules**.\n\n### 1. `peft.LoraConfig` and `peft.TaskType.CAUSAL_LM`\n\n**Source:** `src/peft/tuners/lora/config.py`, `src/peft/utils/peft_types.py`\n\nCalled in `areal/engine/fsdp_engine.py` (`_apply_peft_wrapper`, lines 930–946):\n\n```python\nfrom peft import LoraConfig, TaskType, get_peft_model\n\npeft_config = {\n    \"task_type\": TaskType.CAUSAL_LM,\n    \"r\": config.lora_rank,\n    \"lora_alpha\": config.lora_alpha,\n    \"target_modules\": target_modules,  # str \"all-linear\" or list[str]\n    \"bias\": \"none\",\n}\nif self.config.peft_type == \"lora\":\n    peft_config = LoraConfig(**peft_config)\nelse:\n    raise NotImplementedError()\n```\n\n**Check:** Verify `LoraConfig.__init__` still accepts `task_type`, `r`, `lora_alpha`,\n`target_modules`, and `bias` as keyword arguments with the same types. Check whether any\nnew required parameters were added. Confirm `bias=\"none\"` is still a valid string choice\n(not renamed or replaced with an enum). Confirm `target_modules` still accepts both a\nstring (`\"all-linear\"`) and a `list[str]`. Verify `TaskType` is still an enum importable\nfrom `peft` and `TaskType.CAUSAL_LM` still exists. Verify the class is still importable\ndirectly from `peft` (not moved to `peft.tuners.lora` only).\n\n______________________________________________________________________\n\n### 2. `peft.get_peft_model`\n\n**Source:** `src/peft/peft_model.py`\n\nCalled in `areal/engine/fsdp_engine.py` (`_apply_peft_wrapper`, lines 948–953), after\n`model.enable_input_require_grads()` and before FSDP sharding:\n\n```python\nself.model.enable_input_require_grads()\nself.model = get_peft_model(\n    self.model,\n    peft_config,\n    autocast_adapter_dtype=False,\n)\n```\n\n**Check:** Confirm `get_peft_model` is still exported at the top-level `peft` namespace.\nVerify its signature is `get_peft_model(model, peft_config, ...)` with\n`autocast_adapter_dtype` still an accepted keyword argument (it was added in a mid-cycle\nrelease and could be renamed or removed). Confirm the return type is still a `PeftModel`\nwrapping the original model. Check whether the returned model's `.parameters()`,\n`.named_modules()`, and FSDP-compatibility hooks remain intact — the call site\nimmediately passes the result into FSDP2 sharding.\n\n______________________________________________________________________\n\n### 3. PEFT weight key format and `adapter_config.json` schema (indirectly consumed)\n\n**Source:** `src/peft/tuners/lora/` (weight saving conventions)\n\nParsed in `areal/engine/megatron_utils/megatron_lora.py`\n(`_infer_target_modules_from_adapter_weights`, lines 124–150):\n\n```python\n# Expected key pattern for LoRA weights:\n# \"base_model.model.layers.0.self_attn.q_proj.lora_A.weight\" -> q_proj\n# \"base_model.model.layers.1.mlp.gate_proj.lora_B.weight\"   -> gate_proj\nfor key in weight_keys:\n    key = key.replace(\"base_model.model.\", \"\")\n    if \".lora_A.weight\" in key:\n        module_name = key.replace(\".lora_A.weight\", \"\").split(\".\")[-1]\n    elif \".lora_B.weight\" in key:\n        module_name = key.replace(\".lora_B.weight\", \"\").split(\".\")[-1]\n```\n\nAlso used in `_build_adapter_config_dict` (lines 153–178) to emit an\n`adapter_config.json`:\n\n```python\n{\n    \"base_model_name_or_path\": base_model_name_or_path,\n    \"peft_type\": \"LORA\",\n    \"task_type\": \"CAUSAL_LM\",\n    \"inference_mode\": False,\n    \"r\": peft_config.dim,\n    \"lora_alpha\": peft_config.alpha,\n    \"lora_dropout\": peft_config.dropout,\n    \"target_modules\": target_modules,\n    \"bias\": \"none\",\n    \"fan_in_fan_out\": False,\n    \"modules_to_save\": None,\n    \"init_lora_weights\": True,\n    \"layers_to_transform\": None,\n    \"layers_pattern\": None,\n}\n```\n\n**Check:** Confirm the saved weight key format (`base_model.model.<...>.lora_A.weight` /\n`lora_B.weight`) hasn't changed. Check whether new LoRA variants (e.g.,\n`lora_magnitude_vector` for DoRA) added extra keys that the parser doesn't handle.\nVerify all `adapter_config.json` fields (`peft_type`, `task_type`, `r`, `lora_alpha`,\n`lora_dropout`, `target_modules`, `bias`, `fan_in_fan_out`, etc.) still match what\nPEFT's `LoraConfig.from_pretrained()` expects when loading. This schema is also used for\nvLLM LoRA hot-swap via `WeightUpdateMeta.peft_config`.\n\n______________________________________________________________________\n\n### 4. `WeightUpdateMeta.peft_config` dict schema (cross-engine contract)\n\n**Source:** PEFT conventions (not a direct import, but a schema contract)\n\nPopulated in `areal/engine/megatron_engine.py` (lines 1196–1203) and\n`areal/engine/fsdp_engine.py` (lines 1148–1153):\n\n```python\nmeta.peft_config = {\n    \"r\": self.config.lora_rank,\n    \"lora_alpha\": self.config.lora_alpha,\n    \"target_modules\": target_modules,  # list[str] or str\n    \"bias\": \"none\",\n}\n```\n\nConsumed in `areal/engine/vllm_remote.py` (lines 169–172):\n\n```python\n\"lora_target_modules\": meta.peft_config[\"target_modules\"],\n\"lora_rank\": meta.peft_config[\"r\"],\n\"lora_alpha\": meta.peft_config[\"lora_alpha\"],\n\"lora_bias\": meta.peft_config[\"bias\"],\n```\n\nAnd in `areal/engine/vllm_ext/vllm_worker_extension.py` (lines 243–246) to reconstruct a\n`PEFTHelper` for vLLM's LoRA manager.\n\n**Check:** This dict is the **contract** between AReaL's training engines and inference\nbackends. If PEFT changes its config field names (`r` → `rank`, `lora_alpha` → `alpha`,\netc.), all three sites must be updated in sync. Verify that vLLM's\n`PEFTHelper.from_dict` still accepts the same key names as what PEFT's `LoraConfig`\nuses.\n\n______________________________________________________________________\n\n## Version-Guarded Code\n\nNo known version-guarded code exists in AReaL for `peft`.\n\n<!-- File: .agents/skills/upgrade-deps/checklists/sglang.md (Tokens: ~5865 | Category: anthropic-skill) -->\n---\npackage: sglang\ngithub: sgl-project/sglang\nbranch_template: v${VERSION}\nupstream_paths:\n  - python/sglang/srt/entrypoints/engine.py\n  - python/sglang/srt/entrypoints/http_server.py\n  - python/sglang/srt/entrypoints/openai/protocol.py\n  - python/sglang/srt/function_call/function_call_parser.py\n  - python/sglang/srt/function_call/core_types.py\n  - python/sglang/srt/managers/scheduler.py\n  - python/sglang/srt/managers/io_struct.py\n  - python/sglang/srt/parser/reasoning_parser.py\n  - python/sglang/srt/server_args.py\n  - python/sglang/srt/utils/__init__.py\n  - python/sglang/srt/utils/common.py\n  - python/sglang/srt/utils/network.py\n  - python/sglang/srt/utils/numa_utils.py\n  - python/sglang/launch_server.py\n---\n\n## Affected Files\n\n### Primary (engine layer — most likely to break)\n\n| File                                                 | Imports / Usage                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                             |\n| ---------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `areal/engine/sglang_remote.py`                      | HTTP-only (no Python imports). Sends requests to `/generate`, `/load_lora_adapter`, `/update_weights_from_disk`, `/update_weights_from_distributed`, `/init_weights_update_group`, `/pause_generation`, `/continue_generation`, `/health`, `/release_memory_occupation`, `/resume_memory_occupation`. Parses `meta_info`, `finish_reason`, `output_token_logprobs`, `routed_experts` from JSON responses.                                                                                                                   |\n| `areal/experimental/openai/tool_call_parser.py`      | `sglang.srt.entrypoints.openai.protocol.Function`, `sglang.srt.entrypoints.openai.protocol.Tool`, `sglang.srt.function_call.function_call_parser.FunctionCallParser`, `sglang.srt.parser.reasoning_parser.ReasoningParser`                                                                                                                                                                                                                                                                                                  |\n| `areal/v2/inference_service/sglang/launch_server.py` | `sglang.srt.entrypoints.engine.Engine`, `sglang.srt.entrypoints.engine.init_tokenizer_manager`, `sglang.srt.entrypoints.http_server._execute_server_warmup`, `sglang.srt.entrypoints.http_server._setup_and_run_http_server`, `sglang.srt.entrypoints.http_server.app`, `sglang.srt.managers.detokenizer_manager.run_detokenizer_process`                                                                                                                                                                                   |\n| `areal/v2/inference_service/sglang/scheduler.py`     | `sglang.srt.managers.scheduler.Scheduler`, `sglang.srt.managers.scheduler.configure_scheduler`, `sglang.srt.observability.trace.process_tracing_init`, `sglang.srt.observability.trace.trace_set_thread_info`, `sglang.srt.utils.get_bool_env_var`, `sglang.srt.utils.kill_itself_when_parent_died`, `sglang.srt.utils.set_gpu_proc_affinity`, `sglang.srt.utils.numa_utils.get_numa_node_if_available`, `sglang.srt.utils.numa_utils.numa_bind_to_node`, `sglang.srt.environ.envs`, `sglang.utils.get_exception_traceback` |\n| `areal/v2/inference_service/sglang/rpc_proxy.py`     | `sglang.srt.managers.io_struct.RpcReqInput`, `sglang.srt.managers.io_struct.RpcReqOutput`, `sglang.srt.server_args.PortArgs`, `sglang.srt.utils.network.get_zmq_socket`                                                                                                                                                                                                                                                                                                                                                     |\n| `areal/v2/inference_service/sglang/bridge.py`        | HTTP-only (same protocol as `sglang_remote.py`). `/generate` request/response, `/pause_generation`, `/continue_generation`, `/release_memory_occupation`, `/resume_memory_occupation`.                                                                                                                                                                                                                                                                                                                                      |\n\n### Secondary (model / infra layer)\n\n| File                                                  | Imports / Usage                                                                                                                      |\n| ----------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |\n| `areal/infra/launcher/sglang_server.py`               | HTTP-only. Polls `/v1/models` to confirm server readiness. Calls `SGLangConfig.build_cmd()` to construct the server launch command.  |\n| `areal/api/cli_args.py`                               | `SGLangConfig.build_cmd()`, `SGLangConfig.build_cmd_from_args()`, `SGLangConfig.build_args()`. Version check against `0.5.10.post1`. |\n| `areal/trainer/rl_trainer.py`                         | `SGLangConfig.build_args()` to construct `RemoteSGLangEngine`.                                                                       |\n| `areal/infra/launcher/ray.py`                         | `SGLangConfig` import + `to_structured_cfg`.                                                                                         |\n| `areal/infra/launcher/local.py`                       | `SGLangConfig` import + `to_structured_cfg`.                                                                                         |\n| `areal/infra/launcher/slurm.py`                       | `SGLangConfig` import + `to_structured_cfg`.                                                                                         |\n| `areal/v2/inference_service/controller/controller.py` | `SGLangConfig` in gateway controller.                                                                                                |\n| `areal/v2/inference_service/data_proxy/backend.py`    | HTTP protocol implementation for inference service proxy (SGLangBridgeBackend).                                                      |\n\n### Tertiary (tests, config)\n\n| File                                                 | Imports / Usage                                             |\n| ---------------------------------------------------- | ----------------------------------------------------------- |\n| `tests/grpo/test_grpo.py`                            | Test parameterization with `sglang` backend.                |\n| `tests/experimental/openai/test_tool_call_parser.py` | `FunctionCallParser` / `ReasoningParser` integration tests. |\n\n______________________________________________________________________\n\n## API Usage Catalog\n\nFor each function/class below, verify the call signature against the upstream source at\nthe target version. Focus on: **missing new required parameters**, **removed old\nparameters**, **renamed parameters**, **changed return types**, **changed method\nsignatures on returned objects**, and **moved/renamed modules**.\n\n> **Note:** Most sglang integration in AReaL is HTTP-based — the server is launched as a\n> subprocess and communicated with over REST. Only `areal/experimental/openai/` imports\n> the Python SDK directly. For the HTTP-based call sites, the risk is in **JSON field\n> renames** in request payloads and response bodies, not in Python API signature\n> changes.\n\n______________________________________________________________________\n\n### 1. `POST /generate` — request payload\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py` (request handler)\n\nCalled in `areal/engine/sglang_remote.py` (`SGLangBackend.build_generation_request`,\nlines 42–88):\n\n```python\nsample_params = {\n    \"top_p\": gconfig.top_p,\n    \"top_k\": gconfig.top_k,\n    \"max_new_tokens\": gconfig.max_new_tokens,\n    \"temperature\": 0.0 if gconfig.greedy else gconfig.temperature,\n    \"stop_token_ids\": stop_token_ids,\n    \"ignore_eos\": gconfig.ignore_eos,\n    \"skip_special_tokens\": gconfig.skip_special_tokens,\n    \"frequency_penalty\": gconfig.frequency_penalty,\n}\nif stop:\n    sample_params[\"stop\"] = stop\n\npayload = {\n    \"input_ids\": req.input_ids.copy(),\n    \"image_data\": req.image_data,\n    \"sampling_params\": sample_params,\n    \"return_logprob\": True,\n    \"stream\": False,\n}\n# conditionally added: \"return_routed_experts\": True, \"lora_path\": <str>\n```\n\n**Check:** Confirm `input_ids`, `image_data`, `sampling_params`, `return_logprob`,\n`lora_path`, and `return_routed_experts` are still accepted top-level fields. Verify all\n`sampling_params` sub-keys are unchanged (`top_p`, `top_k`, `max_new_tokens`,\n`temperature`, `stop_token_ids`, `ignore_eos`, `skip_special_tokens`,\n`frequency_penalty`, `stop`). Check whether `stream=False` is still the correct way to\nrequest a non-streaming response.\n\n______________________________________________________________________\n\n### 2. `POST /generate` — response parsing\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py` (response schema)\n\nParsed in `areal/engine/sglang_remote.py` (`SGLangBackend.parse_generation_response`,\nlines 90–126):\n\n```python\nmeta_info = response[\"meta_info\"]\nfinish_reason = meta_info[\"finish_reason\"]\nstop_reason = finish_reason[\"type\"]\nstop_message = finish_reason.get(\"message\", \"\")\nrouted_experts = meta_info.get(\"routed_experts\", None)\nif routed_experts is not None:\n    num_sgl_token = meta_info[\"prompt_tokens\"] + meta_info[\"completion_tokens\"] - 1\n    routed_experts = np.frombuffer(\n        pybase64.b64decode(routed_experts.encode(\"utf-8\")), dtype=np.int32\n    ).reshape(num_sgl_token, -1)\noutput_tokens = [x[1] for x in meta_info[\"output_token_logprobs\"]]\noutput_logprobs = [x[0] for x in meta_info[\"output_token_logprobs\"]]\n```\n\n**Check:** Confirm `meta_info` is still a top-level key. Verify `finish_reason` is still\na dict with a `\"type\"` key (not a plain string). Verify `output_token_logprobs` is still\na list of `[logprob, token_id]` pairs. Check that `prompt_tokens` and\n`completion_tokens` are still present when `return_logprob=True`. Confirm\n`routed_experts` is still base64-encoded int32 that can be decoded via\n`pybase64.b64decode` and reshaped to `(num_tokens, num_layers*expert_top_k)`.\n\n______________________________________________________________________\n\n### 3. `POST /load_lora_adapter` — request payload\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py`\n\nCalled in `areal/engine/sglang_remote.py`\n(`SGLangBackend.build_disk_weight_update_requests`, lines 139–144):\n\n```python\nHttpRequest(\n    endpoint=\"/load_lora_adapter\",\n    payload={\"lora_name\": lora_name, \"lora_path\": str(meta.path)},\n)\n```\n\n**Check:** Confirm the endpoint still exists and accepts `lora_name` and `lora_path`.\nCheck whether the endpoint was renamed or merged into another route.\n\n______________________________________________________________________\n\n### 4. `POST /update_weights_from_disk` — request payload\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py`\n\nCalled in `areal/engine/sglang_remote.py`\n(`SGLangBackend.build_disk_weight_update_requests`, lines 148–158):\n\n```python\nHttpRequest(\n    endpoint=\"/update_weights_from_disk\",\n    payload={\n        \"model_path\": str(meta.path),\n        \"abort_all_requests\": True,\n    },\n)\n```\n\n**Check:** Confirm `model_path` and `abort_all_requests` are still valid payload keys.\nCheck whether the endpoint was renamed (e.g., to `/update_weights`).\n\n______________________________________________________________________\n\n### 5. `POST /update_weights_from_distributed` — request payload\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py`\n\nCalled in `areal/engine/sglang_remote.py`\n(`SGLangBackend.build_distributed_weight_update_requests`, lines 173–186):\n\n```python\nHttpRequest(\n    endpoint=\"/update_weights_from_distributed\",\n    payload={\n        \"names\": [pspec.name for pspec in param_specs],\n        \"dtypes\": [pspec.dtype for pspec in param_specs],\n        \"shapes\": [pspec.shape for pspec in param_specs],\n        \"group_name\": meta.nccl_group_name,\n        \"abort_all_requests\": True,\n    },\n)\n```\n\n**Check:** Confirm all five payload keys are unchanged. Verify `dtypes` still accepts\nstring representations of torch dtypes. Check whether `abort_all_requests` is still\nhonored.\n\nAReaL's NEXTN compatibility bridge in\n`areal/v2/inference_service/sglang/mtp_weight_update_bridge.py` additionally depends on\nthe following SGLang 0.5.10.post1 internals:\n\n- `Scheduler.tp_worker.model_runner` is the target `ModelRunner`.\n- Spec v1 exposes the NEXTN draft `ModelRunner` as `Scheduler.draft_worker.model_runner`\n  (`EAGLEWorker`).\n- Spec v2 exposes it as `Scheduler.draft_worker.draft_worker.draft_runner`\n  (`EAGLEWorkerV2` wrapping `EagleDraftWorker`).\n- `ModelRunner._model_update_group`, `ModelRunner.device`, and\n  `ModelRunner.update_weights_from_tensor(named_tensors, load_format)` retain their\n  current semantics.\n- `sglang.srt.weight_sync.tensor_bucket.FlattenedTensorBucket` accepts `named_tensors`,\n  exposes `get_flattened_tensor()`, and reconstructs regular `(name, tensor)` pairs with\n  `reconstruct_tensors()`.\n\n**Check:** Revalidate these private APIs before changing the SGLang pin. The bridge must\nreceive every distributed bucket exactly once, then apply the same named tensor objects\nto draft and target runners. Keep the explicit version guard until this path has been\nrevalidated end to end.\n\n______________________________________________________________________\n\n### 6. `POST /init_weights_update_group` — request payload\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py`\n\nCalled in `areal/engine/sglang_remote.py`\n(`SGLangBackend.build_init_weights_group_request`, lines 188–207):\n\n```python\nHttpRequest(\n    endpoint=\"/init_weights_update_group\",\n    payload={\n        \"master_address\": format_host_for_url(meta.nccl_master_address),\n        \"master_port\": str(meta.nccl_master_port),\n        \"rank_offset\": rank_offset,\n        \"world_size\": gen_parallel.world_size + 1,\n        \"backend\": current_platform.communication_backend,\n        \"group_name\": meta.nccl_group_name,\n    },\n)\n```\n\n**Check:** Confirm all six payload keys are still accepted. Verify `rank_offset`\nsemantics (whether the training-rank offset convention changed). Check `backend`\naccepted values (e.g., `\"nccl\"`, `\"gloo\"`).\n\n______________________________________________________________________\n\n### 7. `POST /pause_generation` and `POST /continue_generation`\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py`\n\nCalled in `areal/engine/sglang_remote.py` (`get_pause_request` line 211,\n`get_resume_request` line 215):\n\n```python\nHttpRequest(endpoint=\"/pause_generation\", payload={})\nHttpRequest(endpoint=\"/continue_generation\", payload={})\n```\n\n**Check:** Confirm both endpoints still exist with these exact names. Check whether they\nnow require any payload fields (e.g., `wait_for_inflight_requests`).\n\n______________________________________________________________________\n\n### 8. `GET /health`\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py`\n\nCalled in `areal/engine/sglang_remote.py` (`get_health_check_request`, line 219):\n\n```python\nHttpRequest(endpoint=\"/health\", payload={}, method=\"GET\")\n```\n\n**Check:** Confirm the endpoint is still a `GET` request and returns a 2xx on success.\nVerify it doesn't require authentication headers.\n\n______________________________________________________________________\n\n### 9. `GET /v1/models` — server readiness poll\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py`\n\nCalled in `areal/infra/launcher/sglang_server.py` (`wait_for_server`, lines 65–84):\n\n```python\nresponse = requests.get(\n    f\"{base_url}/v1/models\",\n    headers={\"Authorization\": \"Bearer None\"},\n)\nif response.status_code == 200:\n    break\n```\n\n**Check:** Confirm `/v1/models` still returns 200 once the server is ready. Check\nwhether the `Authorization: Bearer None` header is still accepted (or if auth\nrequirements changed). If SGLang changes its readiness probe endpoint, update this\npolling logic.\n\n______________________________________________________________________\n\n### 10. `POST /release_memory_occupation` and `POST /resume_memory_occupation`\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py`\n\nCalled in `areal/engine/sglang_remote.py` (`get_offload_request` line 223,\n`get_onload_request` lines 225–234):\n\n```python\n# offload (release GPU memory)\nHttpRequest(endpoint=\"/release_memory_occupation\", payload={})\n\n# onload (resume GPU memory, optional tags for multi-stage resume)\npayload = {\"tags\": tags} if tags is not None else {}\nHttpRequest(endpoint=\"/resume_memory_occupation\", payload=payload)\n```\n\n**Check:** Confirm both endpoints still exist. For `/resume_memory_occupation`, verify\n`tags` is still an optional list of strings (available tags: `\"weights\"`, `\"kv_cache\"`).\nCheck if additional fields are now required for either endpoint.\n\n______________________________________________________________________\n\n### 11. `sglang.srt.entrypoints.openai.protocol.Function` and `.Tool`\n\n**Source:** `python/sglang/srt/entrypoints/openai/protocol.py`\n\nCalled in `areal/experimental/openai/tool_call_parser.py` (`_process_tool_calls_sglang`,\nlines 73–94):\n\n```python\nfrom sglang.srt.entrypoints.openai.protocol import Function as SglFunction\nfrom sglang.srt.entrypoints.openai.protocol import Tool as SglTool\n\nSglTool(\n    type=tool[\"type\"],\n    function=SglFunction(\n        name=tool.get(\"name\"),\n        description=tool.get(\"description\"),\n        parameters=tool.get(\"parameters\"),\n    ),\n)\n# or:\nSglTool(type=tool[\"type\"], function=SglFunction(**tool[\"function\"]))\n```\n\n**Check:** Confirm `Function` and `Tool` are still exported from this module path (not\nmoved to `sglang.srt.protocol` or similar). Verify `Function` still accepts `name`,\n`description`, `parameters`. Verify `Tool` still accepts `type` and `function`. Check\nfor newly required fields.\n\n______________________________________________________________________\n\n### 12. `sglang.srt.function_call.function_call_parser.FunctionCallParser`\n\n**Source:** `python/sglang/srt/function_call/function_call_parser.py`\n\nCalled in `areal/experimental/openai/tool_call_parser.py` (`_process_tool_calls_sglang`,\nlines 96, 105, 109):\n\n```python\nfrom sglang.srt.function_call.function_call_parser import FunctionCallParser\n\nparser_p = FunctionCallParser(tools, tool_call_parser)\n# Public methods called:\nparser_p.has_tool_call(content_text)       # -> bool\ncontent_text, call_info_list = parser_p.parse_non_stream(content_text)\n# call_info_list items expose: call_info.name, call_info.parameters\n```\n\n**Check:** Confirm the module path is unchanged. Verify the constructor still takes\n`(tools, tool_call_parser)` positionally where `tools` is a list of `SglTool` objects.\nConfirm `has_tool_call(text) -> bool` and `parse_non_stream(text) -> (str, list)`\nmethods still exist with the same signatures. Verify returned `call_info` objects still\nexpose `.name` and `.parameters` attributes.\n\n______________________________________________________________________\n\n### 13. `sglang.srt.parser.reasoning_parser.ReasoningParser`\n\n**Source:** `python/sglang/srt/parser/reasoning_parser.py`\n\nCalled in `areal/experimental/openai/tool_call_parser.py` (`_process_tool_calls_sglang`,\nlines 76, 97, 101–102):\n\n```python\nfrom sglang.srt.parser.reasoning_parser import ReasoningParser\n\nreasoning_parser_p = ReasoningParser(reasoning_parser)\n# Accessed attributes:\nreasoning_parser_p.detector.think_start_token\nreasoning_parser_p.detector.think_end_token\n```\n\n**Check:** Confirm the module path is unchanged (not moved to\n`sglang.srt.reasoning_parser`). Verify the constructor still accepts a single parser\nname string. Critically, verify the returned instance still exposes\n`.detector.think_start_token` and `.detector.think_end_token` — these are used to\nextract `<think>` blocks from model output.\n\n______________________________________________________________________\n\n### 14. `SGLangConfig.build_cmd` / CLI flag compatibility\n\n**Source:** `python/sglang/launch_server.py`, `python/sglang/srt/server_args.py`\n\nCalled in `areal/api/cli_args.py` via `SGLangConfig.build_args()` and\n`SGLangConfig.build_cmd()`:\n\n```python\n# build_args() assembles a dict of CLI flags from SGLangConfig fields\n# build_cmd() converts those flags into a subprocess command list\nargs = SGLangConfig.build_args()\n# flags like --model-path, --tp-size, --max-loaded-loras, etc.\n```\n\n**Check:** For each of the ~70 config fields in `SGLangConfig`, confirm the\ncorresponding `sglang` CLI flag still exists with the same name and accepted values. Pay\nspecial attention to any fields that were added in recent releases (e.g.,\n`max_loaded_loras` added in 0.4.10.post2). Check `python/sglang/srt/server_args.py` for\nadded/removed/renamed `argparse` arguments.\n\n______________________________________________________________________\n\n### 15. `Engine._launch_subprocesses` — server lifecycle\n\n**Source:** `python/sglang/srt/entrypoints/engine.py`\n\nCalled in `areal/v2/inference_service/sglang/launch_server.py` (`areal_launch_server`):\n\n```python\nfrom sglang.srt.entrypoints.engine import Engine, init_tokenizer_manager\n\n(\n    tokenizer_manager,\n    template_manager,\n    port_args,\n    scheduler_init_result,\n    subprocess_watchdog,\n) = Engine._launch_subprocesses(\n    server_args=server_args,\n    init_tokenizer_manager_func=init_tokenizer_manager,\n    run_scheduler_process_func=areal_run_scheduler_process,\n    run_detokenizer_process_func=run_detokenizer_process,\n)\n```\n\n**Check:** Confirm `_launch_subprocesses` is still a classmethod. Verify it still\naccepts `run_scheduler_process_func` as a callable parameter for scheduler process\ninjection. Verify the return tuple is\n`(TokenizerManager, TemplateManager, PortArgs, SchedulerInitResult, SubprocessWatchdog)`.\nConfirm `SchedulerInitResult.scheduler_infos` attribute exists.\n\n______________________________________________________________________\n\n### 16. `_setup_and_run_http_server` — HTTP server bootstrap\n\n**Source:** `python/sglang/srt/entrypoints/http_server.py`\n\nCalled in `areal/v2/inference_service/sglang/launch_server.py` (`areal_launch_server`):\n\n```python\nfrom sglang.srt.entrypoints.http_server import (\n    _execute_server_warmup,\n    _setup_and_run_http_server,\n    app,\n)\n\n_setup_and_run_http_server(\n    server_args,\n    tokenizer_manager,\n    template_manager,\n    port_args,\n    scheduler_init_result.scheduler_infos,\n    subprocess_watchdog,\n    execute_warmup_func=_execute_server_warmup,\n)\n```\n\n**Check:** Confirm the function still exists at this module path. Verify signature:\n`(server_args, tokenizer_manager, template_manager, port_args, scheduler_infos, subprocess_watchdog, execute_warmup_func, launch_callback)`.\nConfirm `app` is still the FastAPI instance exported from this module.\n\n______________________________________________________________________\n\n### 17. `Scheduler` constructor and `configure_scheduler`\n\n**Source:** `python/sglang/srt/managers/scheduler.py`\n\nCalled in `areal/v2/inference_service/sglang/scheduler.py`\n(`areal_run_scheduler_process`):\n\n```python\nfrom sglang.srt.managers.scheduler import Scheduler, configure_scheduler\n\ndp_rank = configure_scheduler(\n    server_args, tp_rank, attn_cp_rank, moe_dp_rank, moe_ep_rank, pp_rank, dp_rank\n)\n\nscheduler = Scheduler(\n    server_args, port_args, gpu_id, tp_rank, moe_ep_rank, pp_rank,\n    attn_cp_rank, moe_dp_rank, dp_rank,\n)\npipe_writer.send(scheduler.get_init_info())\nscheduler.run_event_loop()\n```\n\n**Check:** Confirm `configure_scheduler` signature is\n`(server_args, tp_rank, attn_cp_rank, moe_dp_rank, moe_ep_rank, pp_rank, dp_rank)`.\nConfirm `Scheduler.__init__` signature is\n`(server_args, port_args, gpu_id, tp_rank, moe_ep_rank, pp_rank, attn_cp_rank, moe_dp_rank, dp_rank)`.\nVerify `get_init_info()` and `run_event_loop()` still exist. Confirm `tp_rank`,\n`dp_rank`, `tp_cpu_group`, `tp_size` attributes are present on the scheduler instance\n(used by `AwexSchedulerBridge`).\n\n______________________________________________________________________\n\n### 18. `RpcReqInput` / `RpcReqOutput` — scheduler RPC protocol\n\n**Source:** `python/sglang/srt/managers/io_struct.py`\n\nUsed in `areal/v2/inference_service/sglang/rpc_proxy.py`:\n\n```python\nfrom sglang.srt.managers.io_struct import RpcReqInput, RpcReqOutput\n\nreq = RpcReqInput(method=method, parameters=kwargs if kwargs else None)\nself._rpc_socket.send_pyobj(req)\nresp: RpcReqOutput = self._rpc_socket.recv_pyobj()\n# resp.success: bool, resp.message: str\n```\n\n**Check:** Confirm `RpcReqInput` still has `method: str` and\n`parameters: Optional[Dict]` fields. Confirm `RpcReqOutput` still has `success: bool`\nand `message: str` fields. Verify `send_pyobj`/`recv_pyobj` serialization still works\n(both inherit from `BaseReq` which is a dataclass).\n\n______________________________________________________________________\n\n### 19. `sglang.srt.utils` module reorganization (v0.5.10+)\n\n**Source:** `python/sglang/srt/utils/__init__.py`, `python/sglang/srt/utils/common.py`,\n`python/sglang/srt/utils/network.py`, `python/sglang/srt/utils/numa_utils.py`\n\nIn v0.5.10+, `sglang.srt.utils/__init__.py` only re-exports from `common.py` via\n`from sglang.srt.utils.common import *`. Functions in submodules must be imported\ndirectly:\n\n- `get_zmq_socket` → `sglang.srt.utils.network`\n- `get_numa_node_if_available`, `numa_bind_to_node` → `sglang.srt.utils.numa_utils`\n- `kill_itself_when_parent_died`, `get_bool_env_var`, `set_gpu_proc_affinity`,\n  `kill_process_tree` → `sglang.srt.utils` (via `common.py`)\n\n**Check:** On each upgrade, verify `sglang.srt.utils/__init__.py` re-export scope. If\nfunctions move between submodules, update AReaL import paths accordingly.\n\n______________________________________________________________________\n\n## Version-Guarded Code\n\n- `areal/api/cli_args.py:1877-1878` —\n  `if not pkg_version.is_version_greater_or_equal(\"sglang\", \"0.5.10.post1\"): raise RuntimeError(...)`.\n  Minimum floor raised from `0.4.9.post2` to `0.5.10.post1` during this upgrade. The\n  legacy `max_loaded_loras` version guard (`<0.4.10.post2`) was removed because the new\n  floor is well past that version.\n\n<!-- File: .agents/skills/upgrade-deps/checklists/torchao.md (Tokens: ~2314 | Category: anthropic-skill) -->\n---\npackage: torchao\ngithub: pytorch/ao\nbranch_template: v${VERSION}\nupstream_paths:\n  - torchao/prototype/blockwise_fp8_training/linear.py\n  - torchao/prototype/blockwise_fp8_training/kernels.py\n---\n\n## Affected Files\n\n### Primary (engine layer — most likely to break)\n\n| File                                                      | Imports / Usage                                                                                                                        |\n| --------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------- |\n| `areal/experimental/models/archon/fp8.py`                 | `torchao.prototype.blockwise_fp8_training.linear.fp8_blockwise_mm`; `fp8_blockwise_mm.apply(x, weight, block_size, dtype, use_triton)` |\n| `areal/experimental/models/archon/moe/grouped_experts.py` | `torchao.prototype.blockwise_fp8_training.linear.fp8_blockwise_mm`; `fp8_blockwise_mm.apply(x, w, block_size, dtype, use_triton)`      |\n\n### Secondary (model / infra layer)\n\n| File                                         | Imports / Usage                                                                             |\n| -------------------------------------------- | ------------------------------------------------------------------------------------------- |\n| `areal/api/cli_args.py`                      | `ArchonFP8Config` dataclass — string references to `torchao_config` field; no direct import |\n| `areal/tools/check_pyproject_consistency.py` | `torchao` listed in `ESCAPABLE_PACKAGES` — expected to differ between sglang/vllm variants  |\n\n### Tertiary (tests, config)\n\n| File                                                     | Imports / Usage                                                                                                                                                                                                           |\n| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `tests/experimental/archon/conftest.py`                  | Conditional stub of 5 modules: `torchao`, `torchao.quantization`, `torchao.prototype`, `torchao.prototype.blockwise_fp8_training`, `torchao.prototype.blockwise_fp8_training.linear` when unavailable; SM90+ GPU required |\n| `tests/experimental/archon/fp8/test_fp8_linear.py`       | `import torchao.prototype.blockwise_fp8_training.linear`; forward/backward correctness and convergence tests                                                                                                              |\n| `tests/experimental/archon/fp8/test_fp8_scale_layout.py` | `torchao.prototype.blockwise_fp8_training.kernels.triton_fp8_blockwise_act_quant_lhs`, `triton_fp8_blockwise_weight_quant_transposed_rhs`                                                                                 |\n| `tests/experimental/archon/fp8/test_conversion.py`       | Tests `enable_fp8_linear()` and `enable_fp8_experts()` patching logic                                                                                                                                                     |\n| `tests/experimental/archon/fp8/test_moe_dispatch.py`     | Tests `_run_experts_fp8_for_loop()` with 11 edge cases                                                                                                                                                                    |\n\n______________________________________________________________________\n\n## API Usage Catalog\n\nFor each function/class below, verify the call signature against the upstream source at\nthe target version. Focus on: **missing new required parameters**, **removed old\nparameters**, **renamed parameters**, **changed return types**, **changed method\nsignatures on returned objects**, and **moved/renamed modules**.\n\n> **Warning:** All torchao usage goes through the `prototype` module path. These APIs\n> are explicitly experimental and carry a HIGH risk of breakage between releases.\n\n### 1. `torchao.prototype.blockwise_fp8_training.linear.fp8_blockwise_mm` (HIGH RISK)\n\n**Source:** `torchao/prototype/blockwise_fp8_training/linear.py`\n\nImported inside `areal/experimental/models/archon/fp8.py` `enable_fp8_linear()` (line\n36\\) and stored on each patched module as `mod._fp8_mm`. Called in the patched forward\n(line 156-157):\n\n```python\nfrom torchao.prototype.blockwise_fp8_training.linear import fp8_blockwise_mm\n\n# Inside patched nn.Linear.forward — on-the-fly FP8 quantization\nout = self._fp8_mm.apply(x, weight, self._fp8_block, x.dtype, self._fp8_use_triton)\n# Positional args: (input, weight, block_size=128, dtype, use_triton)\n```\n\nImported inside `areal/experimental/models/archon/moe/grouped_experts.py`\n`_run_experts_fp8_for_loop()` (line 132). Called three times per expert (lines 161-166):\n\n```python\nfrom torchao.prototype.blockwise_fp8_training.linear import fp8_blockwise_mm\n\n# SwiGLU: silu(x @ w1.T) * (x @ w3.T) @ w2.T — all via FP8\nh1 = fp8_blockwise_mm.apply(x_e, w1_e, block_size, x_e.dtype, use_triton)\nh3 = fp8_blockwise_mm.apply(x_e, w3_e, block_size, x_e.dtype, use_triton)\nh = F.silu(h1) * h3\nh2 = fp8_blockwise_mm.apply(h, w2_e, block_size, h.dtype, use_triton)\n```\n\n**Check:** Confirm `fp8_blockwise_mm` is still a `torch.autograd.Function` subclass with\nan `.apply()` entry point. Verify positional argument order\n`(x, weight, block_size, dtype, use_triton)` is unchanged. Check whether `block_size`\nstill accepts an `int` (default 128) and `use_triton` remains a plain `bool`. Confirm\nthe module path `torchao.prototype.blockwise_fp8_training.linear` has not been renamed\nor graduated out of `prototype`.\n\n______________________________________________________________________\n\n### 2. `enable_fp8_linear()` (HIGH RISK — monkey-patches `nn.Linear`)\n\n**Source:** `areal/experimental/models/archon/fp8.py` (AReaL function, not torchao)\n\nDefined at line 18, calls `fp8_blockwise_mm` from torchao internally:\n\n```python\ndef enable_fp8_linear(\n    model: nn.Module,\n    *,\n    exclude_fqns: set[str] | None = None,  # defaults to {\"output\", \"router\", \"score\"}\n    use_triton: bool = True,\n) -> None:\n    from torchao.prototype.blockwise_fp8_training.linear import fp8_blockwise_mm\n    # ... patches eligible nn.Linear modules via _patch_fp8_forward(mod, fp8_blockwise_mm, use_triton)\n```\n\nCalled from test at `tests/experimental/archon/fp8/test_fp8_scale_layout.py` (line 46):\n\n```python\nenable_fp8_linear(model, exclude_fqns=set())\n```\n\n**Check:** This is an AReaL function, but it depends on `fp8_blockwise_mm` from torchao\n(entry 1). If torchao renames or moves `fp8_blockwise_mm`, this function's internal\nimport breaks. Verify `exclude_fqns` and `use_triton` are the only kwargs (no\n`block_size` or `dtype` — block size is hardcoded as `_FP8_BLOCK = 128`).\n\n______________________________________________________________________\n\n### 3. `enable_fp8_experts()` (HIGH RISK — monkey-patches MoE expert modules)\n\n**Source:** `areal/experimental/models/archon/fp8.py` (AReaL function, not torchao)\n\nDefined at line 57:\n\n```python\ndef enable_fp8_experts(model: nn.Module, *, use_triton: bool = True) -> None:\n    # Patches each GroupedExperts module to use _run_experts_fp8_for_loop()\n    # which internally calls fp8_blockwise_mm from torchao\n```\n\n**Check:** This is an AReaL function. Its only torchao dependency is transitive via\n`_run_experts_fp8_for_loop()` → `fp8_blockwise_mm` (entry 1). Verify `use_triton` is the\nonly kwarg (no `block_size` or `dtype` — block size is hardcoded as `_FP8_BLOCK = 128`).\nConfirm it targets `GroupedExperts` submodules specifically.\n\n______________________________________________________________________\n\n### 4. `validate_fp8_shard_alignment()`\n\n**Source:** `areal/experimental/models/archon/fp8.py` (AReaL function, not torchao)\n\nDefined at line 166:\n\n```python\ndef validate_fp8_shard_alignment(\n    model_parts: list[nn.Module],\n    block_size: int = 128,  # _FP8_BLOCK\n) -> None:\n    # Checks both nn.Linear (2D) and GroupedExperts (3D) modules\n    # for block-alignment AFTER parallelism (TP/PP) is applied.\n    # Raises ValueError if local weight dims are not multiples of block_size.\n```\n\n**Check:** This is an AReaL function. It does NOT depend on torchao directly — it only\nchecks that modules already patched with `_fp8_block` attribute have compatible local\nweight shapes. However, if torchao changes `_FP8_BLOCK` convention or the kernel's\nalignment requirements, this validation must be updated to match.\n\n______________________________________________________________________\n\n### 5. `triton_fp8_blockwise_act_quant_lhs` / `triton_fp8_blockwise_weight_quant_transposed_rhs` (HIGH RISK)\n\n**Source:** `torchao/prototype/blockwise_fp8_training/kernels.py`\n\nImported in `tests/experimental/archon/fp8/test_fp8_scale_layout.py` (lines 89-92,\n101-104):\n\n```python\nfrom torchao.prototype.blockwise_fp8_training.kernels import (\n    triton_fp8_blockwise_act_quant_lhs,\n    triton_fp8_blockwise_weight_quant_transposed_rhs,\n)\n\n# Call signatures (lines 101-104):\nx_fp8, x_scale = triton_fp8_blockwise_act_quant_lhs(x, block_size)\nw_t_fp8, w_t_scale = triton_fp8_blockwise_weight_quant_transposed_rhs(w, block_size=block_size)\n\n# Scale shape contract tested:\n# x_scale.shape == (m, k_blocks)\n# w_t_scale.shape == (k_blocks, n_blocks)\n# Both MUST be column-major: stride(0) < stride(1)\n```\n\n**Check:** Confirm both kernel symbols still exist at this path and have not been\nrenamed (e.g., to drop the `triton_` prefix or unify under a single dispatch function).\nVerify return types are `(fp8_tensor, scale_tensor)` tuples. Verify the scale layout\nconvention: column-major strides (`stride(0) < stride(1)`), shape\n`(rows, ceil(cols/block_size))`. Any change in output tensor memory layout silently\nbreaks `fp8_blockwise_mm` which consumes these scales.\n\n______________________________________________________________________\n\n## Version-Guarded Code\n\nNo version-guarded code exists in AReaL for `torchao`. Availability is checked at test\ntime via `try/except` stubs in `tests/experimental/archon/conftest.py` — the stubs cover\nall five affected submodules so tests degrade gracefully on machines without SM90+ GPUs\nor without `torchao` installed.\n\n<!-- File: .agents/skills/upgrade-deps/checklists/transformers.md (Tokens: ~4435 | Category: anthropic-skill) -->\n---\npackage: transformers\ngithub: huggingface/transformers\nbranch_template: v${VERSION}\nupstream_paths:\n  - src/transformers/models/auto/auto_factory.py\n  - src/transformers/models/auto/configuration_auto.py\n  - src/transformers/configuration_utils.py\n  - src/transformers/tokenization_utils_fast.py\n  - src/transformers/tokenization_utils_base.py\n  - src/transformers/processing_utils.py\n  - src/transformers/optimization.py\n  - src/transformers/modeling_flash_attention_utils.py\n  - src/transformers/integrations/flash_attention.py\n  - src/transformers/models/qwen2_vl/modeling_qwen2_vl.py\n  - src/transformers/models/qwen2_5_vl/\n  - src/transformers/models/qwen3_vl/modeling_qwen3_vl.py\n  - src/transformers/utils/import_utils.py\n---\n\n## Affected Files\n\n### Primary (engine layer — most likely to break)\n\n| File                                         | Imports / Usage                                                                                                                                                                                                                                 |\n| -------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `areal/engine/fsdp_engine.py`                | `AutoConfig`, `AutoModelForCausalLM`, `AutoModelForImageTextToText`, `AutoModelForTokenClassification`, `PretrainedConfig`, `PreTrainedTokenizerFast`, `ProcessorMixin`, `get_linear_schedule_with_warmup`, `get_constant_schedule_with_warmup` |\n| `areal/engine/megatron_engine.py`            | `PretrainedConfig`                                                                                                                                                                                                                              |\n| `areal/engine/fsdp_utils/__init__.py`        | `PreTrainedModel`                                                                                                                                                                                                                               |\n| `areal/engine/fsdp_utils/parallel.py`        | `PretrainedConfig`                                                                                                                                                                                                                              |\n| `areal/experimental/engine/archon_engine.py` | `AutoConfig`, `PretrainedConfig`, `PreTrainedTokenizerFast`                                                                                                                                                                                     |\n\n### Secondary (model / infra layer — HIGH RISK: monkey-patching)\n\n| File                                                                       | Imports / Usage                                                                                                                                                                                                                                                    |\n| -------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `areal/models/transformers/ulyssess_patch.py`                              | `transformers.modeling_flash_attention_utils._flash_attention_forward` (direct import); `transformers.integrations.flash_attention._flash_attention_forward` (monkey-patch); dynamic import of `transformers.models.{qwen2_vl,qwen2_5_vl,qwen3_vl}` module strings |\n| `areal/models/transformers/qwen3_vl.py`                                    | `transformers.integrations.flash_attention.flash_attention_forward`; `transformers.models.qwen3_vl.modeling_qwen3_vl.{apply_rotary_pos_emb, repeat_kv}`                                                                                                            |\n| `areal/models/transformers/qwen2_vl.py`                                    | `transformers.integrations.flash_attention.flash_attention_forward`; `transformers.models.qwen2_vl.modeling_qwen2_vl.{apply_multimodal_rotary_pos_emb, repeat_kv}`                                                                                                 |\n| `areal/models/transformers/vision_sp_shard.py`                             | monkey-patches Qwen VL vision modules (internal submodule access)                                                                                                                                                                                                  |\n| `areal/models/tree_attn/module_fsdp.py`                                    | `transformers.integrations.flash_attention._flash_attention_forward` (monkey-patch save/restore)                                                                                                                                                                   |\n| `areal/workflow/rlvr.py`                                                   | `PreTrainedTokenizerFast` (`decode`, `apply_chat_template`, `eos_token_id`)                                                                                                                                                                                        |\n| `areal/workflow/vision_rlvr.py`                                            | `AutoProcessor`, `PreTrainedTokenizerFast`                                                                                                                                                                                                                         |\n| `areal/workflow/multi_turn.py`                                             | `PreTrainedTokenizerFast`                                                                                                                                                                                                                                          |\n| `areal/utils/hf_utils.py`                                                  | `AutoTokenizer.from_pretrained()`, `AutoProcessor.from_pretrained()`                                                                                                                                                                                               |\n| `areal/utils/seeding.py`                                                   | `transformers.set_seed()`                                                                                                                                                                                                                                          |\n| `areal/infra/platforms/__init__.py`                                        | `transformers.utils.import_utils.is_torch_npu_available`                                                                                                                                                                                                           |\n| `areal/infra/rpc/serialization.py`                                         | `AutoTokenizer`, `PreTrainedTokenizer`, `PreTrainedTokenizerFast`, `AutoProcessor`, `ProcessorMixin`                                                                                                                                                               |\n| `areal/models/mcore/registry.py`                                           | `AutoConfig`, `PretrainedConfig`                                                                                                                                                                                                                                   |\n| `areal/models/mcore/bailing_moe.py`                                        | `PretrainedConfig`                                                                                                                                                                                                                                                 |\n| `areal/experimental/models/archon/*/args.py` and `*/state_dict_adapter.py` | `PretrainedConfig`                                                                                                                                                                                                                                                 |\n\n### Tertiary (tests and examples)\n\n| File                                     | Imports / Usage                                                                                                                    |\n| ---------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------- |\n| 30+ files under `tests/` and `examples/` | `AutoTokenizer`, `AutoConfig`, `AutoModelForCausalLM`, `PreTrainedTokenizerFast` — standard load patterns only, no monkey-patching |\n\n______________________________________________________________________\n\n## API Usage Catalog\n\nFor each function/class below, verify the call signature against the upstream source at\nthe target version. Focus on: **missing new required parameters**, **removed old\nparameters**, **renamed parameters**, **changed return types**, **changed method\nsignatures on returned objects**, and **moved/renamed modules**.\n\n### 1. Auto\\* model and config classes\n\n**Source:** `src/transformers/models/auto/auto_factory.py`,\n`src/transformers/models/auto/configuration_auto.py`\n\nCalled in `areal/engine/fsdp_engine.py`:\n\n```python\n# Config loading (line 196)\nself.model_config = AutoConfig.from_pretrained(\n    pretrained_model_name_or_path=self.config.path,\n    trust_remote_code=True,\n)\n\n# VLM path (line 899)\nmodel = AutoModelForImageTextToText.from_pretrained(\n    pretrained_model_name_or_path=self.config.path,\n    trust_remote_code=True,\n    dtype=dtype,\n    attn_implementation=self.config.attn_impl,\n)\n\n# LLM path — _create_llm_actor_or_critic (lines 829–856)\nmodel_class = AutoModelForTokenClassification if is_critic else AutoModelForCausalLM\nmodel_kwargs = {\"num_labels\": 1} if is_critic else {}\nmodel_kwargs.update({\"dtype\": dtype, \"attn_implementation\": self.config.attn_impl})\n\n# Branch: from_config (meta-device, memory-efficient) or from_pretrained\nmodel = model_class.from_config(self.model_config, **model_kwargs)\n# or:\nmodel = model_class.from_pretrained(\n    pretrained_model_name_or_path=self.config.path,\n    trust_remote_code=True,\n    **model_kwargs,\n)\n```\n\nAlso called in `areal/utils/hf_utils.py`, `areal/models/mcore/registry.py`,\n`areal/experimental/engine/archon_engine.py`, and\n`areal/experimental/models/archon/*/args.py`.\n\n**Check:** Verify `from_pretrained` / `from_config` keyword arguments (`dtype`,\n`attn_implementation`, `trust_remote_code`, `num_labels`). Check whether\n`AutoModelForImageTextToText` still exists or was renamed/merged. Confirm `from_config`\nstill accepts `num_labels` for `AutoModelForTokenClassification`. Look for new required\nkwargs introduced in the target version.\n\n______________________________________________________________________\n\n### 2. `PretrainedConfig`\n\n**Source:** `src/transformers/configuration_utils.py`\n\nCalled in `areal/engine/fsdp_engine.py`, `areal/engine/megatron_engine.py`,\n`areal/engine/fsdp_utils/parallel.py`, `areal/experimental/engine/archon_engine.py`,\n`areal/models/mcore/registry.py`, and various\n`areal/experimental/models/archon/*/args.py`:\n\n```python\n# Type annotation / isinstance check only\nconfig: PretrainedConfig\n\n# Attribute access (common patterns)\nconfig.model_type\nconfig.num_attention_heads\nconfig.num_key_value_heads\nconfig.text_config.num_attention_heads   # VLM sub-config\n```\n\n**Check:** Confirm `PretrainedConfig` is still the base class at this path (not moved).\nCheck whether `text_config` sub-config attribute access pattern for VLMs is still valid.\nVerify no new required fields were added to `__init__`.\n\n______________________________________________________________________\n\n### 3. `PreTrainedTokenizerFast` and tokenizer methods\n\n**Source:** `src/transformers/tokenization_utils_fast.py`,\n`src/transformers/tokenization_utils_base.py`\n\nCalled in `areal/utils/hf_utils.py` (`load_hf_tokenizer`, lines 12–30):\n\n```python\n@lru_cache(maxsize=8)\ndef load_hf_tokenizer(model_name_or_path, fast_tokenizer=True, padding_side=None):\n    tokenizer = transformers.AutoTokenizer.from_pretrained(\n        model_name_or_path,\n        fast_tokenizer=fast_tokenizer,\n        trust_remote_code=True,\n        force_download=True,\n        **kwargs,  # padding_side if not None\n    )\n    if tokenizer.pad_token_id is None:\n        tokenizer.pad_token_id = tokenizer.eos_token_id\n```\n\nCalled in `areal/workflow/rlvr.py`, `areal/workflow/multi_turn.py`, and\n`areal/workflow/vision_rlvr.py`:\n\n```python\ntokenizer.decode(token_ids, ...)\ntokenizer.apply_chat_template(messages, ...)\ntokenizer.eos_token_id\n```\n\n**Check:** Verify `fast_tokenizer` parameter still exists in\n`AutoTokenizer.from_pretrained` (it was renamed to `use_fast` in some versions — confirm\nwhich name the current version uses). Confirm `apply_chat_template` signature is\nunchanged (especially `tokenize`, `add_generation_prompt`, `return_tensors` kwargs).\nCheck `force_download` is still accepted. Confirm `eos_token_id` and `pad_token_id` are\nstill plain attributes. Note: the function is `@lru_cache`'d — if the return type\nchanges, the cache must be invalidated.\n\n______________________________________________________________________\n\n### 4. `AutoProcessor` / `ProcessorMixin`\n\n**Source:** `src/transformers/processing_utils.py`\n\nCalled in `areal/utils/hf_utils.py` (`load_hf_processor_and_tokenizer`, lines 33–55):\n\n```python\nprocessor = transformers.AutoProcessor.from_pretrained(\n    model_name_or_path,\n    trust_remote_code=True,\n    force_download=True,\n    use_fast=True,\n)\n```\n\nAlso used as type annotation in `areal/infra/rpc/serialization.py` and\n`areal/engine/fsdp_engine.py` (`ProcessorMixin`).\n\n**Check:** Confirm `AutoProcessor.from_pretrained` still accepts `use_fast`. Verify\n`ProcessorMixin` is still the importable base class at `transformers.ProcessorMixin`.\nCheck for signature changes on processor call methods (image/text preprocessing) if\n`vision_rlvr.py` calls them directly.\n\n______________________________________________________________________\n\n### 5. LR scheduler helpers\n\n**Source:** `src/transformers/optimization.py`\n\nCalled in `areal/engine/fsdp_engine.py` (lines 1017–1026):\n\n```python\n# \"linear\" scheduler (line 1017)\nself.lr_scheduler = get_linear_schedule_with_warmup(\n    self.optimizer,\n    num_warmup_steps,\n    total_train_steps,\n)\n\n# \"constant\" scheduler (line 1023)\nself.lr_scheduler = get_constant_schedule_with_warmup(\n    self.optimizer,\n    num_warmup_steps,\n)\n```\n\n**Check:** Confirm both functions still exist in `transformers.optimization` (they have\noccasionally been soft-deprecated in favor of `get_scheduler`). Verify positional\nargument order (`optimizer`, `num_warmup_steps`, `num_training_steps`) is unchanged.\nCheck return type is still a `LambdaLR`-compatible scheduler.\n\n______________________________________________________________________\n\n### 6. `transformers.set_seed`\n\n**Source:** `src/transformers/trainer_utils.py` (re-exported from top-level)\n\nCalled in `areal/utils/seeding.py` (line 28):\n\n```python\nimport transformers\ntransformers.set_seed(seed)\n```\n\n**Check:** Confirm `set_seed` is still exported at `transformers.set_seed` (top-level\nre-export). Verify signature is `set_seed(seed: int)` with no new required parameters.\n\n______________________________________________________________________\n\n### 7. `transformers.modeling_flash_attention_utils._flash_attention_forward` (HIGH RISK)\n\n**Source:** `src/transformers/modeling_flash_attention_utils.py`\n\nCalled in `areal/models/transformers/ulyssess_patch.py` (import line 6, usage line 57):\n\n```python\nfrom transformers.modeling_flash_attention_utils import _flash_attention_forward\n\n# Called inside _ulysses_flash_attention_forward as a pass-through (line 57):\nattn_output = _flash_attention_forward(\n    query_states, key_states, value_states, *args, **kwargs,\n)\n```\n\n**Check:** This is a **private** function (leading underscore). Verify it still exists\nat this exact import path. Confirm its positional signature\n`(query_states, key_states, value_states, ...)` is unchanged. Any argument reorder or\nrename breaks all Ulysses sequence parallel attention paths. Note: this is used as the\n_real_ flash attention call inside the Ulysses wrapper — it's the only place the actual\nflash attention kernel is invoked.\n\n______________________________________________________________________\n\n### 8. `transformers.integrations.flash_attention._flash_attention_forward` (HIGH RISK — monkey-patched)\n\n**Source:** `src/transformers/integrations/flash_attention.py`\n\nMonkey-patched in `areal/models/transformers/ulyssess_patch.py` (lines 244–246) and\n`areal/models/tree_attn/module_fsdp.py` (lines 163–166, 182–185):\n\n```python\n# ulyssess_patch.py (line 244) — for non-VLM models\nfrom transformers.integrations import flash_attention\nflash_attention._flash_attention_forward = _ulysses_flash_attention_forward\n\n# module_fsdp.py (lines 163-166) — save and replace for tree attention\nORIGINAL_FLASH_ATTENTION_FORWARD = flash_attention._flash_attention_forward\nflash_attention._flash_attention_forward = _tree_attn_fwd_func\n\n# module_fsdp.py (lines 182-185) — restore original after use\nflash_attention._flash_attention_forward = ORIGINAL_FLASH_ATTENTION_FORWARD\nORIGINAL_FLASH_ATTENTION_FORWARD = None\n```\n\n**Check:** Confirm `transformers.integrations.flash_attention` module still exists and\nstill has a `_flash_attention_forward` attribute. If the module was renamed, moved, or\nthe function removed, ALL of the Ulysses and tree-attention patches silently break. Also\nverify `flash_attention_forward` (public, **no** underscore) still exists — it is\nimported directly in `areal/models/transformers/qwen2_vl.py` (line 7) and\n`areal/models/transformers/qwen3_vl.py` (line 12) via\n`from transformers.integrations.flash_attention import flash_attention_forward`.\n\n______________________________________________________________________\n\n### 9. Qwen2-VL internal symbols (HIGH RISK — imported from private modules)\n\n**Source:** `src/transformers/models/qwen2_vl/modeling_qwen2_vl.py`\n\nImported in `areal/models/transformers/qwen2_vl.py` (lines 8–11):\n\n```python\nfrom transformers.models.qwen2_vl.modeling_qwen2_vl import (\n    apply_multimodal_rotary_pos_emb,\n    repeat_kv,\n)\n```\n\nAlso accessed by module string in `areal/models/transformers/ulyssess_patch.py` (lines\n187–193):\n\n```python\n\"module\": \"transformers.models.qwen2_vl.modeling_qwen2_vl\",\n\"attn_class\": \"Qwen2VLAttention\",\n\"model_class\": \"Qwen2VLTextModel\",\n\"patch_module\": \"areal.models.transformers.qwen2_vl\",\n\"patch_attn_func\": \"ulysses_flash_attn_forward\",\n```\n\nThe patch replaces `Qwen2VLAttention.forward` at line 229:\n`attn_class.forward = patch_attn_func`.\n\n**Check:** Confirm `apply_multimodal_rotary_pos_emb` and `repeat_kv` are still exported\nfrom this submodule (they are internal helpers, not public API). Confirm\n`Qwen2VLAttention` and `Qwen2VLTextModel` class names are unchanged. Verify the patch\ntarget `Qwen2VLAttention.forward` still accepts the same signature as\n`areal/models/transformers/qwen2_vl.py:ulysses_flash_attn_forward`.\n\n______________________________________________________________________\n\n### 10. Qwen2.5-VL internal symbols (HIGH RISK — imported by module string)\n\n**Source:** `src/transformers/models/qwen2_5_vl/`\n\nAccessed by module string in `areal/models/transformers/ulyssess_patch.py` (lines\n180–186):\n\n```python\n\"module\": \"transformers.models.qwen2_5_vl.modeling_qwen2_5_vl\",\n\"attn_class\": \"Qwen2_5_VLAttention\",\n\"model_class\": \"Qwen2_5_VLTextModel\",\n\"patch_module\": \"areal.models.transformers.qwen2_vl\",\n\"patch_attn_func\": \"ulysses_flash_attn_forward\",\n```\n\nNote: Qwen2.5-VL reuses the **same** patch function from `qwen2_vl.py` (not a separate\nfile).\n\n**Check:** Confirm the submodule path\n`transformers.models.qwen2_5_vl.modeling_qwen2_5_vl` is unchanged. Confirm class names\n`Qwen2_5_VLAttention` and `Qwen2_5_VLTextModel` still exist. Verify\n`Qwen2_5_VLAttention.forward` has the same signature as `Qwen2VLAttention.forward`\n(since they share the same patch function).\n\n______________________________________________________________________\n\n### 11. Qwen3-VL internal symbols (HIGH RISK — imported from private modules)\n\n**Source:** `src/transformers/models/qwen3_vl/modeling_qwen3_vl.py`\n\nImported in `areal/models/transformers/qwen3_vl.py` (lines 13–16):\n\n```python\nfrom transformers.models.qwen3_vl.modeling_qwen3_vl import (\n    apply_rotary_pos_emb,\n    repeat_kv,\n)\n```\n\nAlso imports `flash_attention_forward` (public, no underscore) at line 12:\n\n```python\nfrom transformers.integrations.flash_attention import flash_attention_forward\n```\n\nAccessed by module string in `areal/models/transformers/ulyssess_patch.py` (lines\n194–200):\n\n```python\n\"module\": \"transformers.models.qwen3_vl.modeling_qwen3_vl\",\n\"attn_class\": \"Qwen3VLTextAttention\",\n\"model_class\": \"Qwen3VLTextModel\",\n\"patch_module\": \"areal.models.transformers.qwen3_vl\",\n\"patch_attn_func\": \"ulysses_flash_attn_forward\",\n```\n\nNote: Qwen3-VL has its **own** patch function (different from Qwen2-VL) with this\nsignature (line 25):\n\n```python\ndef ulysses_flash_attn_forward(\n    self, hidden_states, position_embeddings, attention_mask=None,\n    past_key_values=None, cache_position=None, **kwargs,\n) -> tuple[torch.Tensor, torch.Tensor | None]:\n```\n\n**Check:** Confirm `apply_rotary_pos_emb` and `repeat_kv` still exist at this path.\nConfirm `Qwen3VLTextAttention` and `Qwen3VLTextModel` class names are unchanged. Verify\n`Qwen3VLTextAttention.forward` signature matches the patch:\n`(self, hidden_states, position_embeddings, attention_mask, past_key_values, cache_position, **kwargs)`.\nAny signature change breaks the Ulysses SP for Qwen3-VL.\n\n______________________________________________________________________\n\n### 12. `transformers.utils.import_utils.is_torch_npu_available`\n\n**Source:** `src/transformers/utils/import_utils.py`\n\nCalled in `areal/infra/platforms/__init__.py` (lines 6, 19):\n\n```python\nfrom transformers.utils.import_utils import is_torch_npu_available\nis_npu_available = is_torch_npu_available()\n```\n\n**Check:** Confirm the function is still at this exact submodule path (private utils\nmodule). Check whether it was moved to a different location or replaced with a different\nNPU detection mechanism.\n\n______________________________________________________________________\n\n## Version-Guarded Code\n\nNo known version-guarded code exists in AReaL for `transformers`.\n\n<!-- File: .agents/skills/upgrade-deps/checklists/vllm.md (Tokens: ~3909 | Category: anthropic-skill) -->\n---\npackage: vllm\ngithub: vllm-project/vllm\nbranch_template: v${VERSION}\nupstream_paths:\n  - vllm/entrypoints/openai/\n  - vllm/lora/\n  - vllm/model_executor/model_loader/\n  - vllm/v1/worker/gpu_worker.py\n  - vllm/worker/worker.py\n  - vllm/reasoning/\n  - vllm/tool_parsers/\n  - vllm/distributed/parallel_state.py\n  - vllm/utils/argparse_utils.py\n---\n\n## Affected Files\n\n### Primary (engine layer — most likely to break)\n\n| File                                             | Imports / Usage                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                 |\n| ------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `areal/engine/vllm_ext/vllm_worker_extension.py` | `vllm.logger.init_logger`, `vllm.lora.lora_model.LoRAModel`, `vllm.lora.peft_helper.PEFTHelper`, `vllm.lora.request.LoRARequest`, `vllm.model_executor.model_loader.get_model_loader`; **private APIs**: `_adapter_manager._registered_adapters`, `._add_adapter`, `.activate_adapter`                                                                                                                                                                                                                                                                                                          |\n| `areal/engine/vllm_ext/areal_vllm_server.py`     | `vllm.entrypoints.openai.api_server.build_app`, `.run_server`; `vllm.entrypoints.openai.cli_args.make_arg_parser`, `.validate_parsed_serve_args`; `vllm.entrypoints.openai.completion.api_router.create_completion`; `vllm.entrypoints.openai.completion.protocol.CompletionRequest`; `vllm.entrypoints.openai.engine.protocol.ErrorResponse`, `.OpenAIBaseModel`; `vllm.entrypoints.openai.utils.validate_json_request`; `vllm.entrypoints.utils.cli_env_setup`, `.load_aware_call`, `.with_cancellation`; `vllm.lora.request.LoRARequest`; `vllm.utils.argparse_utils.FlexibleArgumentParser` |\n| `areal/engine/vllm_remote.py`                    | HTTP-only — builds vLLM launch commands via `vLLMConfig.build_cmd_from_args()`; sets `VLLM_ALLOW_RUNTIME_LORA_UPDATING=True`                                                                                                                                                                                                                                                                                                                                                                                                                                                                    |\n\n### Secondary (model / infra layer)\n\n| File                                            | Imports / Usage                                                                              |\n| ----------------------------------------------- | -------------------------------------------------------------------------------------------- |\n| `areal/infra/platforms/cuda.py`                 | `vllm.v1.worker.gpu_worker.Worker` (V1) with fallback to `vllm.worker.worker.Worker` (V0)    |\n| `areal/infra/platforms/unknown.py`              | same V1/V0 Worker import pattern                                                             |\n| `areal/experimental/openai/tool_call_parser.py` | `vllm.reasoning.ReasoningParserManager`, `vllm.tool_parsers.ToolParserManager` (conditional) |\n\n### Tertiary (tests, config)\n\n| File                                                 | Imports / Usage                          |\n| ---------------------------------------------------- | ---------------------------------------- |\n| `tests/experimental/openai/test_tool_call_parser.py` | unit tests with vLLM mocking             |\n| `tests/test_inference_engines.py`                    | integration tests for `RemotevLLMEngine` |\n\n### External (awex — separate subsystem)\n\n| File                                          | Imports / Usage                                                                                                                                                                |\n| --------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `asystem-awex/awex/vllm_plugin.py`            | `vllm.entrypoints.openai.api_server.router`, `vllm.distributed.parallel_state.*`, `vllm.v1.worker.worker_base.WorkerBase`, `vllm.model_executor.model_loader.get_model_loader` |\n| `asystem-awex/awex/awex_vllm_server.py`       | `vllm.entrypoints.openai.api_server.run_server`, cli_args, `FlexibleArgumentParser`                                                                                            |\n| `asystem-awex/awex/engine/vllm.py`            | VLLMEngine wrapper                                                                                                                                                             |\n| `asystem-awex/awex/vllm_awex_adapter.py`      | `AwexVLLMServerAdapter` for runtime weight updates                                                                                                                             |\n| `asystem-awex/awex/sharding/vllm_sharding.py` | sharding strategy helpers                                                                                                                                                      |\n\n______________________________________________________________________\n\n## API Usage Catalog\n\nFor each function/class below, verify the call signature against the upstream source at\nthe target version. Focus on: **missing new required parameters**, **removed old\nparameters**, **renamed parameters**, **changed return types**, **changed method\nsignatures on returned objects**, and **moved/renamed modules**.\n\n### 1. `vllm.entrypoints.openai.api_server.build_app` and `.run_server`\n\n**Source:** `vllm/entrypoints/openai/api_server.py`\n\nCalled in `areal/engine/vllm_ext/areal_vllm_server.py` (lines 10-11, 366-398):\n\n```python\nfrom vllm.entrypoints.openai.api_server import build_app as _original_build_app\nfrom vllm.entrypoints.openai.api_server import run_server\n\n# build_app is monkey-patched at line 388 to inject AReaL routes:\nimport vllm.entrypoints.openai.api_server as _api_server_module\n_api_server_module.build_app = _areal_build_app  # replaces the module-level reference\n\n# _areal_build_app calls the original with supported_tasks kwarg (line 371):\napp = _original_build_app(args, supported_tasks=supported_tasks)\n\n# run_server is called via uvloop (line 398):\nuvloop.run(run_server(args))\n```\n\n**Check:** Confirm both functions still exist at this path. Verify `build_app` accepts\n`(args, supported_tasks=...)` and returns a FastAPI `app`. Verify `run_server` is an\nasync function accepting `(args)`. Check that monkey-patching\n`_api_server_module.build_app` still works (i.e., `run_server` reads `build_app` from\nthe module, not a closure).\n\n______________________________________________________________________\n\n### 2. `vllm.entrypoints.openai.cli_args.make_arg_parser` and `.validate_parsed_serve_args`\n\n**Source:** `vllm/entrypoints/openai/cli_args.py`\n\nCalled in `areal/engine/vllm_ext/areal_vllm_server.py` (lines 12, 391-396):\n\n```python\nfrom vllm.entrypoints.openai.cli_args import make_arg_parser, validate_parsed_serve_args\n\nparser = FlexibleArgumentParser(description=\"vLLM OpenAI-Compatible RESTful API server.\")\nparser = make_arg_parser(parser)\nargs = parser.parse_args()\nvalidate_parsed_serve_args(args)\n```\n\n**Check:** Verify `make_arg_parser` still accepts a parser object and returns an\naugmented parser. Check `validate_parsed_serve_args` for new validation rules that might\nreject AReaL's custom args.\n\n______________________________________________________________________\n\n### 3. `vllm.entrypoints.openai.completion` — protocol and router\n\n**Source:** `vllm/entrypoints/openai/completion/`\n\nCalled in `areal/engine/vllm_ext/areal_vllm_server.py`:\n\n```python\nfrom vllm.entrypoints.openai.completion.api_router import create_completion as original_create_completion\nfrom vllm.entrypoints.openai.completion.protocol import CompletionRequest\nfrom vllm.entrypoints.openai.engine.protocol import ErrorResponse, OpenAIBaseModel\nfrom vllm.entrypoints.openai.utils import validate_json_request\n```\n\n**Check:** Verify the completion sub-package still exists (was restructured in some vLLM\nversions). Confirm `CompletionRequest` Pydantic model fields. Verify `ErrorResponse` and\n`OpenAIBaseModel` are still at `engine.protocol`. Check `validate_json_request` still\nreturns a FastAPI `Depends`.\n\n______________________________________________________________________\n\n### 4. `vllm.entrypoints.utils` — server utilities\n\n**Source:** `vllm/entrypoints/utils.py`\n\nCalled in `areal/engine/vllm_ext/areal_vllm_server.py` (lines 19, 349-350, 390):\n\n```python\nfrom vllm.entrypoints.utils import cli_env_setup, load_aware_call, with_cancellation\n\n# Used as stacked decorators on the create_completion endpoint (lines 349-350):\n@with_cancellation\n@load_aware_call\nasync def create_completion(request: CompletionRequest, raw_request: Request):\n    ...\n\n# Called at server startup (line 390):\ncli_env_setup()\n```\n\n**Check:** Confirm all three are still exported from `vllm.entrypoints.utils`. Verify\n`with_cancellation` is still a decorator compatible with async endpoints. Verify\n`load_aware_call` is still a decorator (not renamed to `load_aware`). Confirm\n`cli_env_setup()` is still a no-arg function.\n\n______________________________________________________________________\n\n### 5. `vllm.lora.request.LoRARequest`\n\n**Source:** `vllm/lora/request.py`\n\nCalled in `areal/engine/vllm_ext/vllm_worker_extension.py` (lines 10, 61-66) and\n`areal_vllm_server.py` (line 21):\n\n```python\nfrom vllm.lora.request import LoRARequest\n\nlora_request = LoRARequest(\n    lora_name=lora_name,\n    lora_int_id=lora_int_id,\n    lora_path=lora_model_path,\n    base_model_name=base_model_name,\n)\n```\n\n**Check:** Verify constructor kwargs unchanged. Confirm `base_model_name` is still\naccepted (vs `base_model_name_or_path`). Check for new required fields.\n\n______________________________________________________________________\n\n### 6. `vllm.lora.lora_model.LoRAModel.from_lora_tensors`\n\n**Source:** `vllm/lora/lora_model.py`\n\nCalled in `areal/engine/vllm_ext/vllm_worker_extension.py`:\n\n```python\nfrom vllm.lora.lora_model import LoRAModel\n\nlora_model = LoRAModel.from_lora_tensors(\n    lora_model_id=int_id,\n    tensors=tensors,\n    peft_helper=peft_helper,\n    device=device,\n    dtype=dtype,\n    model_vocab_size=vocab_size,\n    weights_mapper=weights_mapper,\n)\n```\n\n**Check:** Verify all keyword arguments. `from_lora_tensors` is a classmethod — confirm\nit still exists and returns a `LoRAModel`. Check `peft_helper` type (should be\n`PEFTHelper`). Verify `weights_mapper` is still accepted.\n\n______________________________________________________________________\n\n### 7. `vllm.lora.peft_helper.PEFTHelper.from_dict`\n\n**Source:** `vllm/lora/peft_helper.py`\n\nCalled in `areal/engine/vllm_ext/vllm_worker_extension.py`:\n\n```python\nfrom vllm.lora.peft_helper import PEFTHelper\n\npeft_helper = PEFTHelper.from_dict(peft_config_dict)\n```\n\n**Check:** Verify `from_dict` classmethod still exists. Confirm it accepts a dict with\nkeys `r`, `lora_alpha`, `target_modules`, `bias`. Check return type.\n\n______________________________________________________________________\n\n### 8. `vllm.model_executor.model_loader.get_model_loader`\n\n**Source:** `vllm/model_executor/model_loader/`\n\nCalled in `areal/engine/vllm_ext/vllm_worker_extension.py` (line 34):\n\n```python\nfrom vllm.model_executor.model_loader import get_model_loader\n\nmodel_loader = get_model_loader(self.model_runner.vllm_config.load_config)\nmodel_loader.load_weights(self.model_runner.model, model_config=self.model_runner.model_config)\n```\n\n**Check:** Verify function exists. Confirm it accepts `vllm_config.load_config` (type\n`LoadConfig`). Verify the returned loader has a `load_weights(model, model_config=...)`\nmethod (not `load_model`).\n\n______________________________________________________________________\n\n### 9. LoRA adapter manager private APIs (HIGH RISK)\n\n**Source:** `vllm/lora/` (internal, unstable)\n\nCalled in `areal/engine/vllm_ext/vllm_worker_extension.py` (lines 154-293):\n\n```python\n# Diagnostic read — public list_adapters() at line 179:\nadapter_ids = self.model_runner.lora_manager.list_adapters()\n\n# Private read — _registered_adapters at line 187:\nlora_model = self.model_runner.lora_manager._adapter_manager._registered_adapters[lora_int_id]\n\n# Public remove — remove_adapter() at line 270:\nself.model_runner.lora_manager.remove_adapter(lora_int_id)\n\n# Private write — _add_adapter / activate_adapter at lines 272-274:\nself.model_runner.lora_manager._adapter_manager._add_adapter(new_lora_model)\nself.model_runner.lora_manager._adapter_manager.activate_adapter(new_lora_model.id)\n```\n\nThe code comments at line 155-156 explicitly note: _\"This code relies on vLLM private\nAPIs: `_adapter_manager`, `_registered_adapters`, and `_add_adapter`/`activate_adapter`,\nwhich may change/breakdown due to newer vllm versions.\"_\n\n**Check:** These are **private APIs** — highest breakage risk. Verify `lora_manager`\nstill exposes `list_adapters()` (public), `remove_adapter()` (public), and\n`_adapter_manager` (private). Confirm `_registered_adapters` is still a dict-like\nmapping of `int_id -> LoRAModel`. Confirm `_add_adapter` and `activate_adapter` method\nsignatures. Check if public alternatives now exist that replace the private path.\n\n______________________________________________________________________\n\n### 10. `vllm.v1.worker.gpu_worker.Worker` / `vllm.worker.worker.Worker`\n\n**Source:** `vllm/v1/worker/gpu_worker.py`, `vllm/worker/worker.py`\n\nCalled in `areal/infra/platforms/cuda.py` and `unknown.py`:\n\n```python\ntry:\n    from vllm.v1.worker.gpu_worker import Worker  # V1\nexcept ImportError:\n    from vllm.worker.worker import Worker  # V0 fallback\n```\n\n**Check:** Confirm at least one of these import paths works. If V0 is being removed,\nupdate to V1 only. Check `Worker` class for method compatibility.\n\n______________________________________________________________________\n\n### 11. `vllm.reasoning.ReasoningParserManager` and `vllm.tool_parsers.ToolParserManager`\n\n**Source:** `vllm/reasoning/`, `vllm/tool_parsers/`\n\nCalled in `areal/experimental/openai/tool_call_parser.py` (lines 157-206, inside\n`_process_tool_calls_vllm`):\n\n```python\nfrom vllm.reasoning import ReasoningParserManager\nfrom vllm.tool_parsers import ToolParserManager\n\n# get_reasoning_parser returns a CLASS, then instantiated with tokenizer (line 164-167):\nreasoning_parser_cls = ReasoningParserManager.get_reasoning_parser(reasoning_parser)\nreasoning_parser_inst = reasoning_parser_cls(tokenizer)\n# Accessed attributes: reasoning_parser_inst.start_token, reasoning_parser_inst.end_token\n\n# get_tool_parser also returns a CLASS, then instantiated with tokenizer (line 191-206):\ntool_parser_cls = ToolParserManager.get_tool_parser(vllm_name)\ntool_parser = tool_parser_cls(tokenizer)\n# Called methods: tool_parser.extract_tool_calls(model_output, request)\n```\n\n**Check:** Confirm `get_reasoning_parser` and `get_tool_parser` return **classes** (not\ninstances). Verify the classes accept `(tokenizer)` in their constructor. Confirm\n`start_token`/`end_token` attributes on reasoning parser instances. Verify\n`extract_tool_calls` method signature on tool parser instances. These are used as\nfallback when SGLang parsers are unavailable.\n\n______________________________________________________________________\n\n### 12. `vllm.utils.argparse_utils.FlexibleArgumentParser`\n\n**Source:** `vllm/utils/argparse_utils.py`\n\nCalled in `areal/engine/vllm_ext/areal_vllm_server.py`:\n\n```python\nfrom vllm.utils.argparse_utils import FlexibleArgumentParser\n```\n\n**Check:** Verify this class still exists at this path (it was added in a specific vLLM\nversion). `asystem-awex/awex/awex_vllm_server.py` already has a try/except fallback for\nthis import.\n\n______________________________________________________________________\n\n### 13. `vllm.logger.init_logger`\n\n**Source:** `vllm/logger.py`\n\nCalled in `areal/engine/vllm_ext/vllm_worker_extension.py` and `areal_vllm_server.py`:\n\n```python\nfrom vllm.logger import init_logger\nlogger = init_logger(__name__)\n```\n\n**Check:** Verify function exists. Confirm it returns a standard\n`logging.Logger`-compatible object.\n\n______________________________________________________________________\n\n### 14. vLLM CLI flag compatibility\n\n**Source:** `vllm/engine/arg_utils.py`, `vllm/entrypoints/openai/cli_args.py`\n\nCalled indirectly via `vLLMConfig.build_cmd_from_args()` in `areal/api/cli_args.py`:\n\n```python\n# Assembles CLI flags: --model, --tensor-parallel-size, --gpu-memory-utilization,\n# --max-model-len, --enable-lora, --max-lora-rank, etc.\n```\n\n**Check:** For each field in `vLLMConfig`, confirm the corresponding vLLM CLI flag still\nexists. Pay attention to `--enable-lora` and related LoRA flags. Verify env var\n`VLLM_ALLOW_RUNTIME_LORA_UPDATING` is still respected.\n\n______________________________________________________________________\n\n## Version-Guarded Code\n\n- `areal/infra/platforms/cuda.py` and `unknown.py` — tries\n  `vllm.v1.worker.gpu_worker.Worker` first, falls back to `vllm.worker.worker.Worker`.\n  Once V0 support is dropped, remove the fallback.\n\n- `asystem-awex/awex/awex_vllm_server.py` — `FlexibleArgumentParser` wrapped in\n  try/except with fallback to `argparse.ArgumentParser`.\n\n- `asystem-awex/awex/vllm_plugin.py` — `get_pcp_group` import wrapped in try/except\n  (added in newer vLLM versions).\n\n<!-- File: .agents/skills/upgrade-deps/SKILL.md (Tokens: ~5558 | Category: anthropic-skill) -->\n---\nname: upgrade-deps\ndescription: Upgrade focused runtime dependencies in AReaL. First validates and updates per-package API checklists for structural completeness, then updates pyproject files, resolves conflicts, locks, updates the Dockerfile, and audits API compatibility against the checklists.\n---\n\n## Usage\n\n```text\n/upgrade-deps <package==version> [<package==version> ...]\n```\n\n**Arguments:** One or more pinned package versions, e.g.,\n`/upgrade-deps megatron-core==0.17.0 sglang==0.5.10 vllm==0.18.0 transformers==4.58.0`.\n\nIf a package is omitted, its current version can be preserved or upgraded, depending on\nthe resolution of `uv lock`.\n\n______________________________________________________________________\n\n## Architecture\n\n### Dual-Manifest Model\n\nAReaL maintains **two pyproject files** because SGLang and vLLM pin\nmutually-incompatible `torch` / `torchao` versions:\n\n| File                  | Inference backend | Lock file      |\n| --------------------- | ----------------- | -------------- |\n| `pyproject.toml`      | SGLang (default)  | `uv.lock`      |\n| `pyproject.vllm.toml` | vLLM              | `uv.vllm.lock` |\n\nBoth share the same core dependencies, megatron extras, and dev group. They diverge only\nin inference backend extras and `torch`/`torchao` version constraints.\n\nThe `Dockerfile` builds both variants from a single file using `ARG VARIANT` (`sglang`\nor `vllm`). The base image, torch install, and flash-attn wheels are all\nvariant-specific.\n\n### Focused Packages\n\nThe following packages are **focused** — their API usage in AReaL is cataloged, and any\nversion change triggers the API compatibility audit (Step 6):\n\n| Package           | Import path       |\n| ----------------- | ----------------- |\n| `megatron-core`   | `megatron.core`   |\n| `megatron-bridge` | `megatron.bridge` |\n| `mbridge`         | `mbridge`         |\n| `transformers`    | `transformers`    |\n| `sglang`          | `sglang`          |\n| `vllm`            | `vllm`            |\n| `peft`            | `peft`            |\n| `torchao`         | `torchao`         |\n\n`torch` is tracked in the Package Impact Matrix below for scope and Docker awareness,\nbut is **not** a focused package — it does not receive API auditing.\n\n### Package Impact Matrix\n\nEvery entry below has a **variant scope** that determines which files to edit, which\nlock files to regenerate, and whether the Dockerfile needs review. All focused packages\nplus `torch` (tracked for Docker impact only, not API-audited) are listed.\n\n| Package           | Scope            | pyproject.toml locations                                       | pyproject.vllm.toml locations                                  | Docker impact |\n| ----------------- | ---------------- | -------------------------------------------------------------- | -------------------------------------------------------------- | ------------- |\n| `sglang`          | sglang-only      | `[optional-deps].sglang`                                       | —                                                              | Base image    |\n| `vllm`            | vllm-only        | —                                                              | `[optional-deps].vllm`                                         | No            |\n| `megatron-core`   | shared           | `[optional-deps].megatron` + `[tool.uv].override-dependencies` | `[optional-deps].megatron` + `[tool.uv].override-dependencies` | No            |\n| `megatron-bridge` | shared           | `[optional-deps].megatron`                                     | `[optional-deps].megatron`                                     | No            |\n| `mbridge`         | shared           | `[optional-deps].megatron` (git pin)                           | `[optional-deps].megatron` (git pin)                           | No            |\n| `transformers`    | shared           | `[project].dependencies`                                       | `[project].dependencies`                                       | No            |\n| `peft`            | shared           | `[project].dependencies`                                       | `[project].dependencies`                                       | No            |\n| `torch`           | shared-divergent | `[project].dependencies` (≥2.9.1)                              | `[project].dependencies` (≥2.10.0)                             | Stage 1 torch |\n| `torchao`         | shared-divergent | `[tool.uv].override-dependencies` (==0.15.0)                   | `[project].dependencies` (==0.16.0)                            | No            |\n\n**Scope rules:**\n\n- **sglang-only / vllm-only**: Edit and lock only the affected variant.\n- **shared**: Edit both files; lock both variants.\n- **shared-divergent**: The two variants intentionally use different versions of the\n  same package. If the user supplies a single target version, **ask which variant(s) to\n  apply it to** rather than assuming convergence. If the user supplies two targets\n  (e.g., `torch==2.9.1@sglang torch==2.10.0@vllm`), apply each accordingly.\n\n### Upgrade Families\n\nSome packages are tightly coupled and should be upgraded together:\n\n| Family    | Members                                  | Reason                                                    |\n| --------- | ---------------------------------------- | --------------------------------------------------------- |\n| megatron  | `megatron-core`, `megatron-bridge`       | megatron-bridge wraps megatron-core; tightly coupled APIs |\n| inference | `sglang` or `vllm` + `torch` + `torchao` | Inference backends pin specific torch/torchao versions    |\n\nWhen the user upgrades one member, **check the checklist of all family members** for\nrequired co-upgrades and warn if a co-upgrade is needed but not requested.\n\n### Per-Package API Checklists\n\nEach focused package has a dedicated markdown file under `checklists/` that documents\nhow AReaL uses that package's APIs:\n\n```\n.agents/skills/upgrade-deps/\n├── SKILL.md\n└── checklists/\n    ├── megatron-core.md\n    ├── megatron-bridge.md\n    ├── vllm.md\n    ├── sglang.md\n    ├── transformers.md\n    ├── peft.md\n    └── torchao.md\n```\n\nEach checklist file MUST contain the following sections:\n\n````markdown\n---\npackage: <pip-package-name>\ngithub: <org/repo>                    # e.g., NVIDIA/Megatron-LM\nbranch_template: \"v${VERSION}\"        # how to construct the git branch/tag from version\nupstream_paths:                        # source paths to cross-reference\n  - path/to/relevant/file.py\n  - path/to/relevant/module/\n---\n\n## Affected Files\n\n### Primary (most likely to break)\n| File | Imports / Usage |\n| ---- | --------------- |\n| ...  | ...             |\n\n### Secondary\n...\n\n### Tertiary (tests, infra)\n...\n\n## API Usage Catalog\n\nFor each function/class, verify the call signature against the upstream source.\nFocus on: removed params, renamed params, new required params, changed return types,\nmoved/renamed modules.\n\n### 1. `module.submodule.function_or_class`\n\n**Source:** `upstream_path/file.py`\n\nCalled in `areal/path/to/file.py:LINE`:\n\\```python\nactual_call_site_code()\n\\```\n\n**Check:** [what to verify]\n\n### 2. ...\n\n## Version-Guarded Code (if any)\n- [file:line] description of version guard and when it can be removed\n````\n\nTo populate a checklist, catalog AReaL's usage of the package by grepping for imports\nand call sites, then fill in the template sections. Each checklist should be\nself-contained — all API signatures, file paths, and upstream references must be\nrecorded directly in the checklist file.\n\n______________________________________________________________________\n\n## Workflow\n\n### Step 0: Parse Input and Record Baseline\n\n1. Parse arguments. Each argument is `<package==version>` or, for shared-divergent\n   packages, `<package==version@variant>` (e.g., `torchao==0.16.0@vllm`).\n1. Validate that each package name is a recognized focused package (see Focused Packages\n   table). If unrecognized, warn but proceed — it may be a transitive dependency the\n   user wants to pin.\n1. For **shared-divergent** packages (`torch`, `torchao`): if the user supplies a single\n   version without a `@variant` qualifier, **ask which variant(s) to apply it to**\n   before proceeding.\n1. Record the current **resolved** versions of ALL focused packages from the applicable\n   lock file(s) as the baseline snapshot. Use `uv.lock` for sglang-scoped and shared\n   packages; `uv.vllm.lock` for vllm-scoped and shared packages. Packages scoped to a\n   single variant appear only in that variant's lock file. This baseline will be used\n   later to detect which packages actually changed — including transitive bumps caused\n   by `uv lock` resolution, not just explicit pin changes.\n\n### Step 0.5: Validate and Update Checklists\n\nBefore modifying any dependencies, verify that the API checklists for the packages being\nupgraded are structurally complete — i.e., they document all current AReaL import sites\nand call patterns. Stale checklists lead to missed breaking changes in Step 6.\n\nFor each **focused package explicitly requested** in the command that has a checklist\nfile under `checklists/`:\n\n1. **Discover all usages.** Grep the AReaL codebase (`areal/`, `tests/`, `examples/`)\n   for all imports matching the package's import path(s). See the Import Patterns table\n   in `CHECKLIST_MAINTENANCE.md` § 2 for package-specific grep patterns. For HTTP-based\n   integrations (sglang, vllm), also scan request-building code for endpoint paths and\n   JSON field names.\n\n1. **Compare against the checklist.** Diff the discovered files against the Affected\n   Files tables (Primary / Secondary / Tertiary) in the checklist. Identify:\n\n   - **Missing** — files that import the package but are not listed in any tier.\n   - **Stale** — files listed in the checklist that no longer import the package.\n   - **Changed** — files whose actual imports differ from what's documented.\n\n1. **Classify and update.** For each discrepancy, follow the Structural Validation\n   Procedure in `CHECKLIST_MAINTENANCE.md` § 3:\n\n   - Add missing files to the appropriate tier (Primary / Secondary / Tertiary).\n   - Add new API Usage Catalog entries for genuinely new call patterns.\n   - Remove stale entries.\n   - Renumber catalog entries if needed.\n\n1. **Update the Checklist File Status table** at the bottom of this file if entry counts\n   changed.\n\n1. **Report** changes before proceeding:\n\n   ```text\n   Checklist validation for <package>:\n   - Added N files to Affected Files (P primary, S secondary, T tertiary)\n   - Added M new API catalog entries: [brief list]\n   - Removed K stale entries: [brief list]\n   - No changes needed (if clean)\n   ```\n\nIf a requested focused package does **not** have a checklist file, create one from\n`checklists/_TEMPLATE.md` using the full procedure in `CHECKLIST_MAINTENANCE.md` § 5.\n\n**Scope note:** This step validates only the packages explicitly named in the command.\nPackages that are transitively bumped are identified later in Step 5; their checklists\nare validated at that point using the same procedure before the API audit in Step 6.\n\n### Step 1: Update Pyproject Files\n\nFor each requested package, using the Package Impact Matrix:\n\n1. Determine the **variant scope** (sglang-only, vllm-only, shared, shared-divergent).\n1. Edit the version pin in **all declaration locations** for each affected pyproject\n   file. A single package may appear in multiple locations:\n   - `[project].dependencies`\n   - `[project.optional-dependencies].<extra>`\n   - `[tool.uv].override-dependencies`\n1. For **upgrade families**: if `megatron-core` is being upgraded, also check whether\n   `megatron-bridge` needs a corresponding version bump. Warn if it does but was not\n   included in the command.\n\n### Step 2: Lock Dependencies (Variant-Aware)\n\nRegenerate lock files for each **affected variant**. A variant is affected if any of its\npyproject file's dependencies were modified. If conflicts arise during locking, do NOT\nattempt to resolve them in this step — just report them and defer resolution to Step 3.\n\n**SGLang variant** (if `pyproject.toml` was modified):\n\n```bash\nbash scripts/uv_lock.sh\n```\n\n**vLLM variant** (if `pyproject.vllm.toml` was modified):\n\n```bash\nbash scripts/uv_lock.sh\n```\n\nIf `uv lock` fails for either variant:\n\n1. Read the error message carefully.\n1. If it's a version conflict, proceed to Step 3 to resolve, then **return here to\n   re-lock**.\n1. Do NOT modify dependencies without user approval to make the lock succeed.\n\n**Validation:** After locking, verify both lock files exist and are non-empty.\n\n### Step 3: Resolve Dependency Conflicts\n\nResolve any conflicts from Step 2. After all conflicts are resolved, **return to Step 2\nand re-lock** the affected variant(s). Classify each conflict:\n\n**Auto-resolvable** — only AReaL's pin conflicts with an upstream package, and the\nupstream's required version is acceptable. Update AReaL's pin automatically.\n\n**Needs user input** — two upstream packages have mutual conflicts (e.g., sglang\nrequires `torch==2.9.1` but vllm requires `torch==2.10.0`). Summarize and ask the user.\n\nOutput format:\n\n```\nSummary\n\n---\nAuto-resolved (no action required):\n- <name>: <packageA> requires <versionA>, <packageB> requires <versionB>,\n  AReaL specified <oldVersion>, updated to <newVersion>\n- ...\n\n---\nConflicts (need user resolution):\n- <name>: <packageA> requires <versionA>, <packageB> requires <versionB>\n- ...\n```\n\nYou may use `override-dependencies` in `[tool.uv]` to force-pin versions where needed.\nRemember that `sglang` and `vllm` are **separate variants** maintained in different\npyproject files — they are never installed together in the same environment.\n\n### Step 4: Update Dockerfile (If Required)\n\nReview the Package Impact Matrix \"Docker impact\" column. Only proceed with Dockerfile\nchanges if an upgraded package has Docker impact. The most common triggers are:\n\n**Base image change** (triggered by `sglang` upgrade):\n\nThe Dockerfile base image is `lmsysorg/sglang:v{SGLANG_VERSION}-cu129-amd64-runtime`. If\n`sglang` was upgraded, update line 9 of the `Dockerfile`:\n\n```dockerfile\nFROM lmsysorg/sglang:v{NEW_SGLANG_VERSION}-cu129-amd64-runtime\n```\n\nVerify that the new base image tag exists on Docker Hub / GHCR before committing.\n\n**Torch version change** (triggered by `torch` upgrade):\n\nThe Dockerfile Stage 1 installs torch with variant-specific versions. Update the version\nmapping in the `RUN uv venv` command:\n\n```dockerfile\n&& if [ \"$VARIANT\" = \"vllm\" ]; then TORCH_VER=\"{VLLM_TORCH}\"; else TORCH_VER=\"{SGLANG_TORCH}\"; fi \\\n```\n\nAlso check flash-attn wheel compatibility — **both** flash-attn-2 and flash-attn-3\ninstalls use a `TORCH_TAG` that must match the torch major.minor version. Update both\noccurrences in the Dockerfile:\n\n```dockerfile\n# flash-attn-2 (first flash-attn RUN block)\n&& if [ \"$VARIANT\" = \"vllm\" ]; then TORCH_TAG=\"torch{VLLM_TORCH_MAJOR_MINOR}\"; else TORCH_TAG=\"torch{SGLANG_TORCH_MAJOR_MINOR}\"; fi \\\n\n# flash-attn-3 (second flash-attn RUN block — same TORCH_TAG pattern)\n&& if [ \"$VARIANT\" = \"vllm\" ]; then TORCH_TAG=\"torch{VLLM_TORCH_MAJOR_MINOR}\"; else TORCH_TAG=\"torch{SGLANG_TORCH_MAJOR_MINOR}\"; fi \\\n```\n\n**No Dockerfile changes needed** for: `megatron-core`, `megatron-bridge`,\n`transformers`, `peft`, `vllm`, `torchao`. These are installed via\n`uv pip install -r pyproject.toml` in Stage 3, which reads the updated pyproject\nautomatically.\n\n### Step 5: Identify Updated Focused Packages\n\nCompare the baseline snapshot (Step 0) against the **resolved** versions in the lock\nfiles (`uv.lock` and/or `uv.vllm.lock`) produced by Step 2 (or re-locked after Step 3\nconflict resolution). This catches not only explicitly requested upgrades but also\n**transitive version bumps** — e.g., upgrading `sglang` may pull in a newer\n`transformers` through dependency resolution.\n\nBuild a list of focused packages whose resolved version actually changed. This list\ndetermines which API checklists to audit in Step 6.\n\nThe focused packages to check are listed in the Focused Packages table (Architecture\nsection):\n\n- `megatron-core` (imports as `megatron.core`)\n- `megatron-bridge` (imports as `megatron.bridge`)\n- `transformers`\n- `sglang`\n- `vllm`\n- `peft`\n- `torchao`\n\nIf a package version did NOT change (even if it was in the user's input but resolved to\nthe same version), skip its API audit.\n\nFor any newly-identified package whose checklist was NOT already validated in Step 0.5,\nrun the same structural validation procedure (see `CHECKLIST_MAINTENANCE.md` § 3) on its\nchecklist before proceeding to Step 6.\n\n### Step 6: API Compatibility Audit\n\nFor each updated focused package that has a checklist file under `checklists/`:\n\n#### 6a. Clone upstream source\n\nRead the checklist frontmatter to get `github` and `branch_template`. Clone or checkout\nthe target version:\n\n```bash\nREPO_ROOT=$(pwd)\nPKG_DIR=\"${REPO_ROOT}/<package>-src\"\nVERSION=\"<target_version>\"\n# Validate VERSION to prevent command injection\nif [[ ! \"$VERSION\" =~ ^[a-zA-Z0-9._/-]+$ ]]; then\n  echo \"Error: Invalid version format: $VERSION\"; exit 1\nfi\nBRANCH=$(echo \"<branch_template>\" | sed \"s/\\${VERSION}/$VERSION/\")\nif [ ! -d \"$PKG_DIR\" ]; then\n  git clone --depth 1 --branch \"$BRANCH\" \"https://github.com/<github>.git\" \"$PKG_DIR\"\nelse\n  (cd \"$PKG_DIR\" && git fetch origin && git checkout \"$BRANCH\")\nfi\n```\n\nIf cloning fails (tag doesn't exist, etc.), report to the user immediately.\n\n#### 6b. Audit API signatures\n\nFor EACH entry in the checklist's API Usage Catalog:\n\n1. Open the upstream source file at the target version (paths listed in the checklist's\n   `upstream_paths` frontmatter).\n1. Compare the function/class signature against the current AReaL invocation.\n1. Flag any of:\n   - **Removed parameters** still passed by AReaL → must remove from call site\n   - **Renamed parameters** → must rename in call site\n   - **New required parameters** (no default) → must add to call site\n   - **New optional parameters** with useful defaults → document but skip\n   - **Changed return types** → must update consumers\n   - **Removed functions/classes** → must find replacement\n   - **Moved modules** → must update import paths\n   - **Changed method signatures** on returned objects → must update call sites\n1. Record findings per-file.\n\n#### 6c. Check version-guarded code\n\nIf the checklist has a \"Version-Guarded Code\" section, check whether any guards\nreference versions at or below the new target. If so, verify the upstream fix is present\nand note the dead code for cleanup.\n\n#### 6d. Apply code changes (if any)\n\nFor each flagged incompatibility:\n\n1. Update the call site in the affected AReaL file.\n1. **Preserve existing behavior** — do NOT refactor beyond what's required.\n1. If a function was removed, check the upstream migration guide or changelog.\n1. Priority order: engine layer → model layer → infra layer → test files.\n\nIf there are unresolvable breaking changes, **STOP and ask the user** before proceeding.\n\n#### 6e. Update checklist file\n\nUpdate the checklist file to reflect the post-upgrade state. Follow the Content Update\nProcedure in `CHECKLIST_MAINTENANCE.md` § 4:\n\n1. Update API signatures in catalog entries where upstream changed.\n1. Update call-site code snippets where Step 6d modified AReaL code.\n1. Update version-guarded code entries (remove cleaned-up guards, add new ones).\n1. Update frontmatter `upstream_paths` if source files moved.\n1. Update the entry count in the Checklist File Status table (bottom of this file).\n\nThis ensures the checklist remains an accurate reference for future upgrades.\n\n#### 6f. Clean up cloned repositories\n\nRemove the cloned upstream source directories to avoid cluttering the workspace:\n\n```bash\nrm -rf \"${REPO_ROOT}/<package>-src\"\n```\n\n### Step 7: Run Pre-Commit\n\n```bash\npre-commit run --all-files\n```\n\n### Step 8: Generate Upgrade Summary\n\nDump a formatted markdown summary to `upgrade-summary.md` in the repository root (this\nfile is gitignored and ephemeral). The summary MUST include:\n\n```markdown\n## Dependency Upgrade Summary\n\n**Date:** YYYY-MM-DD\n**Requested:** <original command>\n\n### Version Changes\n\n| Package | Old Version | New Version | Variant(s) |\n| ------- | ----------- | ----------- | ---------- |\n| ...     | ...         | ...         | ...        |\n\n### Dependency Resolution\n\n- <auto-resolved change description>\n- ...\n\n### Dockerfile Changes\n\n- <change description, or \"No changes required\">\n\n### API Compatibility Audit\n\n#### <package-name> (old → new)\n\n**Breaking changes found:**\n- [file:line] description of change\n\n**Module moves / renames:**\n- [old_path] → [new_path]\n\n**Version-guarded code:**\n- [file:line] status (still needed / can be removed)\n\n**No breaking changes found** _(if clean)_\n\n#### ...\n\n### Unresolved Issues (if any)\n\n- <description of issue and why it could not be auto-resolved>\n```\n\nIf the upgrade failed at any step, the summary should still be generated with the\nfailure reason clearly documented in the \"Unresolved Issues\" section.\n\n### Step 9: Create PR and Trigger CI (Optional)\n\nAsk the user if they want to create a PR.\n\nIf the user agrees:\n\n1. Load the `create-pr` skill to create the PR.\n1. Trigger the CI workflow manually via `.github/workflows/build-docker-image.yml` (only\n   if the Dockerfile was modified or inference backend versions changed).\n1. The Docker build CI builds both sglang and vllm images, then automatically triggers\n   testing on each. Debug until the overall workflow succeeds.\n1. If you encounter issues that cannot be resolved, ask the user for help.\n\n______________________________________________________________________\n\n## Checklist File Status\n\n| Package           | Checklist file                  | Status                                                                                                                                                                              |\n| ----------------- | ------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `megatron-core`   | `checklists/megatron-core.md`   | ✅ 23 API entries (parallel_state, DDP, optimizer, pipeline, checkpointing/async internals, transformer config, FP8, GPT/MTP, tensor_parallel, custom attention, layer specs, RoPE) |\n| `megatron-bridge` | `checklists/megatron-bridge.md` | ✅ 9 API entries (AutoBridge, LoRA, save/load HF, Qwen3-VL patch target, AutoMapping, monkey-patch guard)                                                                           |\n| `mbridge`         | `checklists/mbridge.md`         | ✅ 14 API entries (AutoBridge, Bridge properties, weight mappings, LLMBridge subclassing, register_model, monkey-patch target)                                                      |\n| `vllm`            | `checklists/vllm.md`            | ✅ 14 API entries (entrypoints, LoRA manager, worker V0/V1, tool parsers, CLI)                                                                                                      |\n| `sglang`          | `checklists/sglang.md`          | ✅ 14 API entries (HTTP endpoints, tool/reasoning parsers, CLI flags, version guards)                                                                                               |\n| `transformers`    | `checklists/transformers.md`    | ✅ 12 API entries (Auto\\* classes, tokenizer, flash attention monkey-patches, Qwen VL internals, LR schedulers)                                                                     |\n| `peft`            | `checklists/peft.md`            | ✅ 4 API entries (LoraConfig, TaskType, get_peft_model, weight key format)                                                                                                          |\n| `torchao`         | `checklists/torchao.md`         | ✅ 5 API entries (fp8_blockwise_mm, enable_fp8_linear/experts, shard validation, Triton kernels)                                                                                    |\n\n<!-- File: .claude/rules/api-config.md (Tokens: ~345 | Category: anthropic-skill) -->\n---\npaths:\n  - areal/api/**\n---\n\n# API & Config Rules\n\n## Dataclass Conventions\n\n```python\n@dataclass\nclass XxxConfig:\n    \"\"\"One-line description.\n\n    Attributes:\n        field_name: Description with default explained.\n    \"\"\"\n    # Required fields first (no default)\n    required_field: str\n\n    # Optional fields with defaults\n    optional_field: int = 32\n\n    # Internal fields last (underscore prefix)\n    _internal: str = field(default=\"\", repr=False)\n```\n\n## Field Ordering\n\n1. Required fields (no default)\n1. Common optional fields\n1. Advanced/rare optional fields\n1. Internal fields (`_prefix`)\n\n## Validation\n\n- Use `__post_init__` for validation\n- Raise `ValueError` with clear message:\n  ```python\n  def __post_init__(self):\n      if self.batch_size <= 0:\n          raise ValueError(f\"batch_size must be positive, got {self.batch_size}\")\n  ```\n\n## Backward Compatibility\n\n- **Adding fields**: Add with default value (safe)\n- **Removing fields**: Deprecate first, remove in next major version\n- **Renaming fields**: Add new field, keep old with deprecation warning\n- **Changing types**: Avoid; use Union if necessary\n\n## CLI Integration\n\n- Fields exposed to CLI must have clear `help` in metadata\n- Use `Literal` for enum-like choices\n- Avoid complex nested types in CLI-exposed configs\n\n## Documentation\n\n- All public configs must have docstring\n- Document constraints (e.g., \"must be power of 2\")\n- Include example values for non-obvious fields\n\n<!-- File: .claude/rules/code-style.md (Tokens: ~752 | Category: anthropic-skill) -->\n# Code Style Rules\n\nRules beyond pre-commit (ruff format/lint).\n\n## Design Patterns\n\n- **Prefer composition over inheritance**: Avoid deep class hierarchies\n  - Good: `Engine` holds a `Checkpointer` instance\n  - Avoid: `CheckpointableEngine(Engine)` →\n    `FSDPCheckpointableEngine(CheckpointableEngine)`\n- Keep inheritance shallow (≤2 levels when possible)\n- Use mixins sparingly; prefer explicit delegation\n\n## Logging\n\n- Use `areal.utils.logging.getLogger(name)` with PascalCase descriptive name, NOT\n  `print` or stdlib `logging`\n  - Good: `getLogger(\"RLVRWorkflow\")`, `getLogger(\"ArchonEngine\")`,\n    `getLogger(\"GSM8KReward\")`\n  - Avoid: `getLogger(__name__)` or dotted paths like `getLogger(\"areal.engine.fsdp\")`\n- For per-rank loggers: `[{Component} Rank {N}]` (e.g., `[FSDPEngine Rank 0]`)\n- Log levels:\n  - DEBUG: Detailed tracing (avoid in hot paths)\n  - INFO: Milestones (training start, checkpoint saved)\n  - WARNING: Recoverable issues\n  - ERROR: Failures requiring attention\n- Register new loggers in `areal/utils/logging.py` (`LOGGER_COLORS_EXACT` or\n  `LOGGER_PATTERNS`)\n- Test colors: `python -m areal.utils.logging`\n\n**Color Scheme:**\n\n| Color      | Category                               | Examples                           |\n| ---------- | -------------------------------------- | ---------------------------------- |\n| blue       | Infrastructure (Schedulers, Launchers) | `LocalScheduler`, `RayLauncher`    |\n| white      | Orchestration (Controllers, RPC)       | `TrainController`, `SGLangWrapper` |\n| purple     | RL-specific (Workflows, Rewards)       | `RLVRWorkflow`, `GSM8KReward`      |\n| green      | Data/Metrics (Stats, Dataset)          | `StatsLogger`, `RLTrainer`         |\n| cyan       | Compute backends (Engines, Platforms)  | `FSDPEngine`, `CUDAPlatform`       |\n| yellow/red | Warning/Error levels (override)        | Any logger at WARNING+             |\n\n## Performance Patterns\n\n- **Avoid GPU-CPU sync**: `.item()`, `.tolist()`, `print(tensor)` cause sync\n- **Prefer batch operations**: Avoid Python loops over tensor elements\n- **In-place ops**: Use when safe, but careful with autograd (`.add_()` vs `+`)\n\n## Naming Conventions\n\n| Type             | Pattern       | Example                             |\n| ---------------- | ------------- | ----------------------------------- |\n| Config dataclass | `XxxConfig`   | `GRPOConfig`, `FSDPConfig`          |\n| Engine class     | `XxxEngine`   | `FSDPEngine`, `ArchonEngine`        |\n| Workflow class   | `XxxWorkflow` | `RLVRWorkflow`, `MultiTurnWorkflow` |\n| Reward function  | `xxx_reward`  | `math_reward`, `code_reward`        |\n\n## Tensor Conventions\n\n- Shape convention: `[batch, seq_len, hidden]` or document clearly\n- Use `torch.Size` assertions for shape validation in debug\n- Prefer explicit dtype/device over implicit conversion\n\n## Import Style\n\n- Group: stdlib, third-party, areal (ruff handles order)\n- Avoid `from x import *` (CLAUDE.md rule)\n- Prefer explicit imports over module-level imports for large modules\n\n<!-- File: .claude/rules/distributed.md (Tokens: ~343 | Category: anthropic-skill) -->\n---\npaths:\n  - areal/engine/**\n  - areal/experimental/**\n  - areal/engine/fsdp_utils/**\n---\n\n# Distributed Code Rules\n\n## Process Group Management\n\n- **Never create global process group** in module-level code\n- Always pass `process_group` explicitly, don't rely on default\n- Use `dist.get_rank(group)` not `dist.get_rank()` when group matters\n- Clean up process groups in `__del__` or context manager\n\n## DeviceMesh & DTensor\n\n- Mesh dimension names must match `ArchonParallelDims`: `dp_shard`, `tp`, `cp`, `ep`,\n  `etp`\n- DTensor requires consistent mesh across all ranks\n- Use `DTensor.from_local()` with correct placements\n\n## Communication Patterns\n\n- **All-reduce**: Must be called by all ranks in the group\n- **Broadcast**: Specify `src` rank explicitly\n- **Barrier**: Avoid unless necessary (debugging only)\n- Check `NCCL_ASYNC_ERROR_HANDLING` for deadlock debugging\n\n## Common Pitfalls\n\n| Issue         | Cause                            | Fix                            |\n| ------------- | -------------------------------- | ------------------------------ |\n| Hang          | Mismatched collective calls      | Ensure all ranks call same op  |\n| Wrong results | Incorrect reduction op           | Check `ReduceOp` (SUM vs MEAN) |\n| OOM           | Unsharded tensor on wrong device | Verify DTensor placements      |\n\n## Debugging\n\n- Set `TORCH_DISTRIBUTED_DEBUG=DETAIL` for verbose logging\n- Use `NCCL_DEBUG=INFO` for NCCL-level issues\n- See `/debug-distributed` skill for comprehensive guide\n\n<!-- File: .claude/rules/testing.md (Tokens: ~387 | Category: anthropic-skill) -->\n---\npaths:\n  - '**/tests/**'\n  - '*_test.py'\n  - test_*.py\n---\n\n# Testing Rules\n\n## Pytest Markers\n\n| Marker                                  | When to Use          |\n| --------------------------------------- | -------------------- |\n| `@pytest.mark.slow`                     | Takes > 10 seconds   |\n| `@pytest.mark.asyncio`                  | Async test functions |\n| `@pytest.mark.skipif(cond, reason=...)` | Conditional skip     |\n| `@pytest.mark.parametrize(...)`         | Parameterized tests  |\n\n## Test Structure\n\n```python\ndef test_<what>_<condition>_<expected>():\n    \"\"\"Test that <what> does <expected> when <condition>.\"\"\"\n    # Arrange\n    ...\n    # Act\n    ...\n    # Assert\n    ...\n```\n\n## Mocking Distributed\n\n- Use `torch.distributed.fake_pg` for unit tests\n- Mock `dist.get_rank()` and `dist.get_world_size()` explicitly\n- Don't mock internals of FSDP/DTensor - use integration tests\n\n## GPU Test Constraints\n\n- **Always skip gracefully** when GPU unavailable:\n  ```python\n  CUDA_AVAILABLE = torch.cuda.is_available()\n\n  @pytest.mark.skipif(not CUDA_AVAILABLE, reason=\"CUDA not available\")\n  def test_gpu_feature():\n      ...\n  ```\n- Clean up GPU memory: `torch.cuda.empty_cache()` in fixtures\n- Use smallest possible model/batch for unit tests\n\n## Fixtures\n\n- Prefer `tmp_path` over manual temp directories\n- Use `monkeypatch` for environment variables\n- Scope expensive fixtures appropriately (`session` > `module` > `function`)\n\n## Assertions\n\n- Use `torch.testing.assert_close()` for tensor comparison\n- Specify `rtol`/`atol` explicitly for numerical tests\n- Avoid bare `assert tensor.equal()` - no useful error message\n\n<!-- ... +26 more modular skills available via /api/skills/inclusionAI/AReaL?path={path} -->\n</yakaai_skills>"}