ml-pipeline-patterns
--- <!-- Loaded by research:data-steward (sonnet + medium) -->
$ npx -y skills add Borda/AI-Rig --agent claude-codeHow it fires
How this agent gets triggered: by you, by Claude, or both.
- Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
- You can call itInvoke it directly when you want it.
Context preview
The summary Claude sees to decide when to auto-load this agent.
--- <!-- Loaded by research:data-steward (sonnet + medium) -->
Agent definition
ml-pipeline-patterns.md--- <!-- Loaded by research:data-steward (sonnet + medium) -->
Reference document — NOT an agent definition. Used by research:data-steward as contextual material.
ML Pipeline Patterns — data-steward reference
Loaded by data-steward agent in `pipeline-audit` mode before Step 1. Contains: split strategies for grouped/temporal data, class imbalance handling, DataLoader integrity patterns. \<split_strategies>
Patient-Level Split (medical imaging — CRITICAL)
import pandas as pd
from sklearn.model_selection import GroupShuffleSplit
patient_ids = metadata["patient_id"].values
# random_state MUST be pinned — omitting produces a different split per run; the
# patient-overlap assertion still passes (stays group-aware), silently masking
# non-reproducibility. Cross-run comparisons require the exact same seed.
gss = GroupShuffleSplit(n_splits=1, test_size=0.3, random_state=42)
train_idx, temp_idx = next(gss.split(metadata, groups=patient_ids))
# Verify zero patient overlap
train_patients = set(metadata.iloc[train_idx]["patient_id"])
test_patients = set(metadata.iloc[temp_idx]["patient_id"])
assert train_patients.isdisjoint(test_patients), "PATIENT LEAK DETECTED"
Checklist for medical imaging datasets:
[ ] Splits are by patient/subject ID, never by image/slice
[ ] DICOM metadata checked for hidden identifiers (StudyInstanceUID links images)
[ ] Multi-site data: stratify by site to avoid site-specific bias
[ ] Temporal data: no future scans leaking into training from same patient
[ ] Annotation consistency: inter-reader variability measured (Fleiss' kappa)
[ ] `random_state` pinned and logged in artifacts (required for cross-run split reproducibility — group-overlap assertion alone does NOT guarantee reproducibility)
Verify zero patient overlap between splits (uses `verify_patient_split.py` from `bin/`):
python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_research}/bin/verify_patient_split.py" \
--train splits/train.csv --test splits/test.csvTemporal Split (time-series or streaming data)
Sort by time, sequential split: 70%/15%/15% train/val/test, no shuffle.
**Caveats — apply BEFORE the sort/split:**
- **Duplicate timestamps**: tie-break deterministically (e.g. secondary sort by stable row index or surrogate key) — otherwise 70/85% boundary lands inside arbitrarily ordered tie, bleeds near-boundary leakage.
- **Multi-granularity time**: if data mixes event timestamps (ms) and day-level aggregates, normalise to single granularity (or split on coarser one) before sorting — global sort otherwise places aggregates non-deterministically.
- **Multi-entity datasets (e.g. per-patient time-series)**: global temporal sort does NOT isolate entity-level ordering — patient A's future can land before patient B's past. Combine with Patient-Level Split above: group by entity, sort within each group, allocate each group's rows to splits independently. Use Patient-Level Split as outer split, apply this temporal sort *within* each group.
\</split_strategies>
\<class_imbalance>
Detection
from collections import Counter
distribution = Counter(labels)
majority = max(distribution.values())
minority = min(distribution.values())
ratio = majority / minority # >10x severe; 2-10x moderate
Handling Strategies (in order of preference)
1. **Collect more data** for underrepresented classes 2. **Weighted sampling**: `WeightedRandomSampler` to balance batches 3. **Weighted loss**: `nn.CrossEntropyLoss(weight=class_weights)` 4. **SMOTE/augmentation** for minority classes 5. **Threshold tuning** on classifier output (classification only)
\</class_imbalance>
\<dataloader_patterns>
Recommended Configuration
See `foundry:perf-optimizer` for throughput settings (`num_workers`, `pin_memory`, `prefetch_factor`, `persistent_workers`) — foundry plugin only; skip if absent. Core integrity settings:
DataLoader(
dataset,
batch_size=32,
drop_last=True,
collate_fn=None,
worker_init_fn=..., # set per-worker seed for reproducibility
)Reproducible DataLoader
def worker_init_fn(worker_id):
worker_seed = torch.initial_seed() % (2**32)
numpy.random.seed(worker_seed)
random.seed(worker_seed)
loader = DataLoader(
dataset, worker_init_fn=worker_init_fn, generator=torch.Generator().manual_seed(42)
)\</dataloader_patterns>
Read more
--- <!-- Loaded by research:data-steward (sonnet + medium) -->
Reference document — NOT an agent definition. Used by research:data-steward as contextual material.
ML Pipeline Patterns — data-steward reference
Loaded by data-steward agent in `pipeline-audit` mode before Step 1. Contains: split strategies for grouped/temporal data, class imbalance handling, DataLoader integrity patterns. \<split_strategies>
Patient-Level Split (medical imaging — CRITICAL)
import pandas as pd from sklearn.model_selection import GroupShuffleSplit patient_ids = metadata["patient_id"].values # random_state MUST be pinned — omitting produces a different split per run; the # patient-overlap assertion still passes (stays group-aware), silently masking # non-reproducibility. Cross-run comparisons require the exact same seed. gss = GroupShuffleSplit(n_splits=1, test_size=0.3, random_state=42) train_idx, temp_idx = next(gss.split(metadata, groups=patient_ids)) # Verify zero patient overlap train_patients = set(metadata.iloc[train_idx]["patient_id"]) test_patients = set(metadata.iloc[temp_idx]["patient_id"]) assert train_patients.isdisjoint(test_patients), "PATIENT LEAK DETECTED"
Checklist for medical imaging datasets:
[ ] Splits are by patient/subject ID, never by image/slice [ ] DICOM metadata checked for hidden identifiers (StudyInstanceUID links images) [ ] Multi-site data: stratify by site to avoid site-specific bias [ ] Temporal data: no future scans leaking into training from same patient [ ] Annotation consistency: inter-reader variability measured (Fleiss' kappa) [ ] `random_state` pinned and logged in artifacts (required for cross-run split reproducibility — group-overlap assertion alone does NOT guarantee reproducibility)
Verify zero patient overlap between splits (uses `verify_patient_split.py` from `bin/`):
python "${CLAUDE_PLUGIN_ROOT:-plugins/cc_research}/bin/verify_patient_split.py" \
--train splits/train.csv --test splits/test.csvTemporal Split (time-series or streaming data)
Sort by time, sequential split: 70%/15%/15% train/val/test, no shuffle.
**Caveats — apply BEFORE the sort/split:**
- **Duplicate timestamps**: tie-break deterministically (e.g. secondary sort by stable row index or surrogate key) — otherwise 70/85% boundary lands inside arbitrarily ordered tie, bleeds near-boundary leakage.
- **Multi-granularity time**: if data mixes event timestamps (ms) and day-level aggregates, normalise to single granularity (or split on coarser one) before sorting — global sort otherwise places aggregates non-deterministically.
- **Multi-entity datasets (e.g. per-patient time-series)**: global temporal sort does NOT isolate entity-level ordering — patient A's future can land before patient B's past. Combine with Patient-Level Split above: group by entity, sort within each group, allocate each group's rows to splits independently. Use Patient-Level Split as outer split, apply this temporal sort *within* each group.
\</split_strategies>
\<class_imbalance>
Detection
from collections import Counter distribution = Counter(labels) majority = max(distribution.values()) minority = min(distribution.values()) ratio = majority / minority # >10x severe; 2-10x moderate
Handling Strategies (in order of preference)
1. **Collect more data** for underrepresented classes 2. **Weighted sampling**: `WeightedRandomSampler` to balance batches 3. **Weighted loss**: `nn.CrossEntropyLoss(weight=class_weights)` 4. **SMOTE/augmentation** for minority classes 5. **Threshold tuning** on classifier output (classification only)
\</class_imbalance>
\<dataloader_patterns>
Recommended Configuration
See `foundry:perf-optimizer` for throughput settings (`num_workers`, `pin_memory`, `prefetch_factor`, `persistent_workers`) — foundry plugin only; skip if absent. Core integrity settings:
DataLoader(
dataset,
batch_size=32,
drop_last=True,
collate_fn=None,
worker_init_fn=..., # set per-worker seed for reproducibility
)Reproducible DataLoader
def worker_init_fn(worker_id):
worker_seed = torch.initial_seed() % (2**32)
numpy.random.seed(worker_seed)
random.seed(worker_seed)
loader = DataLoader(
dataset, worker_init_fn=worker_init_fn, generator=torch.Generator().manual_seed(42)
)\</dataloader_patterns>
Specialist-agent infrastructure for Python/ML OSS — the scaffolding that lets you maintain at scale without becoming a full-time reviewer.
Repo: Borda/AI-Rig
Other agents on ai-rig.
- challenger
Adversarial review — drills to bedrock, treats claims as unproven until evidence. NOT for: plan design (foundry:solution-architect), test coverage (foundry:qa-specialist), config formatting (foundry:curator). TRIGGER: "challenge this", "devil''s advocate", "poke holes in". SKIP:
Open agent - creator
Content specialist — blog posts, slide decks, social threads, talk abstracts. Reads approved outline, applies four-beat arc. NOT for in-code docs/README/FAQs (foundry:doc-scribe), release notes (oss:release). TRIGGER: "write a blog post", "create slides", "draft a thread". SKIP:
Open agent - curator
Config quality reviewer. Scope: agents/skills/rules (*.md) — verbosity, duplication, cross-refs, roster overlap; applies fixes. NOT for hooks (foundry:sw-engineer), ADRs (foundry:solution-architect), adversarial challenge (foundry:challenger). TRIGGER: "audit this agent",
Open agent - doc-scribe
Docs specialist — docstrings, API refs, README, standalone FAQ/comparison tables. NOT for CHANGELOG (oss:shepherd), linting (foundry:linting-expert), implementation (foundry:sw-engineer), narrative content (foundry:creator). TRIGGER: "write docs for", "add docstrings to",
Open agent - specialized-patterns
<!-- Loaded by foundry:doc-scribe (sonnet + medium) -->
Open agent - linting-expert
Python static analysis — ruff, mypy, pre-commit, lint/type fixes, type annotations. NOT for CI topology (oss:cicd-steward), test logic (foundry:qa-specialist), non-style implementation (foundry:sw-engineer), docstrings (foundry:doc-scribe). TRIGGER: "is this clean", "lint
Open agent

