From research
Generates Kaggle competition notebooks as Jupytext Python scripts with PyTorch Lightning, EDA→Baseline→Train→Inference pipeline, and per-stage sanity checks.
How this skill is triggered — by the user, by Claude, or both
Slash command
/research:kaggle <competition-name> [<url-or-description>] [--type classification|regression|segmentation|detection|tabular] [--eda-only] [--inference-only] [--offline-setup] [--resume <existing.py>] [--keep "<items>"]<competition-name> [<url-or-description>] [--type classification|regression|segmentation|detection|tabular] [--eda-only] [--inference-only] [--offline-setup] [--resume <existing.py>] [--keep "<items>"]This skill is limited to the following tools:
The summary Claude sees in its skill listing — used to decide when to auto-load this skill
<objective>
Generate Kaggle competition notebook script, Jupytext # %% format.
Follows user's ML research style distilled from past notebooks:
# ! bash over subprocess — package installs, nvidia-smi, ls -lh, # ! head submission.csvmetrics.csv after every training runNOT for writing Python packages, modules, production code — notebook scripts only.
NOT research literature survey — use /research:topic for SOTA literature search.
<competition-name> — short slug for output filename; generates blank template<competition-name> <url> — fetches competition overview from URL before generating<competition-name> "<description>" — inline description of problem and data--type <type> — hint: classification, regression, segmentation, detection, tabular (auto-detected when omitted)--eda-only — generate only EDA sections (no model/training/submission); always online (no offline setup)--inference-only — generate inference notebook from checkpoint (no EDA, no training); always offline (frozen packages pattern); loads checkpoint from PATH_CHECKPOINT constant; output suffix -inference.py--offline-setup — include offline package setup (frozen_packages pattern) in setup cell; auto-applied when --inference-only; ignored when --eda-only (EDA always online)--resume <path> — read existing .py script, extend/improve itOutput: .experiments/kaggle/<competition-name>.py
OUTPUT_DIR: .experiments/kaggle/
CELL_MARK: "# %%"
MD_CELL_MARK: "# %% [markdown]"
# NOTE: documentation-only — not referenced as shell vars across separate Bash() calls (state doesn't persist); keep values in sync with literal use sites (Steps 1, 3, 4).
Key boundary: end of Step 3 — notebook script generated by foundry:sw-engineer, written to OUTFILE.
Preserve: OUTFILE path (derived from TMPDIR keys), COMPETITION_NAME (TMPDIR key), mode flags (EDA_ONLY, INFERENCE_ONLY, OFFLINE_SETUP).
Clear at Step 1 start (stale prior run) and after Step 4 package-distillation gate resolves.
Task hygiene: call TaskList first; close orphaned tasks. Create tasks per phase.
# loads: compaction-contract.md
ARGS="$ARGUMENTS"
COMPETITION_NAME=$(echo "$ARGS" | awk '{print $1}')
RESUME_FLAG=""
EDA_ONLY=false
INFERENCE_ONLY=false
OFFLINE_SETUP=false
PROBLEM_TYPE=""
[[ "$ARGS" == *"--eda-only"* ]] && EDA_ONLY=true
[[ "$ARGS" == *"--inference-only"* ]] && INFERENCE_ONLY=true
[[ "$ARGS" == *"--offline-setup"* ]] && OFFLINE_SETUP=true
[[ "$ARGS" =~ --type[[:space:]]([a-z]+) ]] && PROBLEM_TYPE="${BASH_REMATCH[1]}"
[[ "$ARGS" =~ --resume[[:space:]]([^[:space:]]+) ]] && RESUME_FLAG="${BASH_REMATCH[1]}"
# inference always offline; EDA always online (overrides --offline-setup)
[ "$INFERENCE_ONLY" = "true" ] && OFFLINE_SETUP=true
[ "$EDA_ONLY" = "true" ] && OFFLINE_SETUP=false
echo "Competition: $COMPETITION_NAME"
echo "Type: ${PROBLEM_TYPE:-auto-detect}"
echo "EDA only: $EDA_ONLY | Inference only: $INFERENCE_ONLY | Offline setup: $OFFLINE_SETUP"
# Persist for Steps 3+4 (bash state lost across Bash() calls)
echo "$COMPETITION_NAME" > "${TMPDIR:-/tmp}/kaggle-competition-name"
echo "$EDA_ONLY" > "${TMPDIR:-/tmp}/kaggle-eda-only"
echo "$INFERENCE_ONLY" > "${TMPDIR:-/tmp}/kaggle-inference-only"
echo "$OFFLINE_SETUP" > "${TMPDIR:-/tmp}/kaggle-offline-setup"
mkdir -p .experiments/kaggle/ # timeout: 3000
KEEP_ITEMS=""
if [[ "$ARGS" =~ --keep[[:space:]]\"([^\"]+)\" ]]; then
KEEP_ITEMS="${BASH_REMATCH[1]}"
fi
# Clear stale contract from any prior incomplete run (compaction-contract.md §Lifecycle)
rm -f .claude/state/skill-contract.md # timeout: 5000
echo "${KEEP_ITEMS:-}" > "${TMPDIR:-/tmp}/kaggle-keep-items" # persist for Step 3 contract write
Flag mutual-exclusion check — if EDA_ONLY and INFERENCE_ONLY are both true (both --eda-only and --inference-only passed): print ! Conflicting flags: `--eda-only` and `--inference-only` are mutually exclusive (`--eda-only` is always-online with no training; `--inference-only` is always-offline/frozen-package with no EDA — see `foundation.md`). Pick one. then invoke AskUserQuestion — (a) Abort · (b) Continue ignoring both (falls back to full mode: neither eda-only nor inference-only applied). On Abort: stop.
Unsupported flag check — scan $ARGUMENTS for remaining --<token> tokens after supported flags extracted (--eda-only, --inference-only, --offline-setup, --type, --resume, --keep). Found: print ! Unknown flag(s): `--<token>`. Supported: `--eda-only`, `--inference-only`, `--offline-setup`, `--type <type>`, `--resume <path>`, `--keep "<items>"`. then invoke AskUserQuestion — (a) Abort · (b) Continue ignoring. On Abort: stop.
Context collection — run in parallel:
WebFetch competition page; extract problem description, target metric, data format, evaluation — read and quote actual text, never paraphrase from training knowledge--resume: read existing script (Read tool).experiments/kaggle/ (Glob pattern *.py) for prior scripts; read first 30 lines of each — find similar past competitions, use as structural referenceresources/competitors/ for .ipynb/.py files — found: read each, summarise approach (model choice, preprocessing, feature engineering, augmentation). Use findings to inform detection method and domain-specific preprocessing decisions in Step 2.Grounding protocol — mandatory before Step 2:
Build fact table. Each fact needs source: [fetched], [user], [past-notebook:<file>], or [inferred-from:<fact>]. Never mark fact [inferred] without citing prior fact it derives from.
| Fact | Value | Source |
|---|---|---|
| problem_type | ? | ? |
| input_modality | ? | ? |
| output_format | ? | ? |
| eval_metric | ? | ? |
| data schema (CSV columns / image format) | ? | ? |
| submission format | ? | ? |
Gaps — ask before generating:
After building fact table, count facts still marked ? or [inferred] without prior grounded fact. Any of these unknown:
input_modality — cannot generate Dataset classeval_metric — cannot choose torchmetricsubmission format — cannot generate Submission sectionInvoke AskUserQuestion with up to 4 questions covering all unknown required facts. Never guess or hallucinate competition-specific details (column names, file paths, data schema). State "unknown — will use placeholder" if user skips.
Acknowledge past-notebook similarity explicitly: "Found similar past notebook: <file> — reusing <pattern> from it."
From gathered context, determine:
| Property | Value |
|---|---|
problem_type | classification / regression / segmentation / detection / tabular |
input_modality | image-2d / image-3d / tabular / time-series / point-cloud / mixed |
output_format | label / scalar / mask / bboxes / rle |
eval_metric | AUC / F1 / RMSE / Dice / IoU / mAP / ... |
recommended_model | see §Model selection below |
use_ptl | true if DNN training; false for pure XGBoost/sklearn pipelines |
Model selection rules (best-fit, not default):
timm.create_model (EfficientNetV2, ConvNeXt, ViT-B) + PTLtimm.create_model backbone (num_classes=0) + PTL regression headsegmentation_models_pytorch (UNet/UNet++) + PTL; MONAI for 3Dtorchvision.models.detection or ultralytics YOLO + PTL wrapper if neededxgboost.XGBClassifier/Regressor with sklearn Pipeline; PTL only if DNN features neededpytorch3d; PTL alwaystorch.nn.LSTM or tsfresh features + XGBoost; PTL when DNNPTL rule: use PTL whenever training loop needed — even simple single-layer models. Exception: pure sklearn/XGBoost pipelines, no neural network component.
Foundry availability check — verify before spawning:
FOUNDRY_AVAILABLE=$(ls -td ~/.claude/plugins/cache/borda-ai-rig/foundry/*/agents/sw-engineer.md 2>/dev/null | head -1) # timeout: 5000
[ -z "$FOUNDRY_AVAILABLE" ] && { printf "⚠ foundry plugin not available — kaggle notebook generation requires foundry:sw-engineer\nInstall: claude plugin install foundry@borda-ai-rig\n"; exit 1; }
Spawn prompt assembled from the inline problem profile below plus exactly one resolved composition row:
# Re-hydrate flags persisted in Step 1 (bash state lost between Bash calls)
COMPETITION_NAME=$(cat "${TMPDIR:-/tmp}/kaggle-competition-name" 2>/dev/null || echo "$COMPETITION_NAME")
EDA_ONLY=$(cat "${TMPDIR:-/tmp}/kaggle-eda-only" 2>/dev/null || echo "false")
INFERENCE_ONLY=$(cat "${TMPDIR:-/tmp}/kaggle-inference-only" 2>/dev/null || echo "false")
_KAGGLE_MODES="${CLAUDE_PLUGIN_ROOT:-plugins/research}/skills/kaggle/modes"
COMPOSITION_FILE="$_KAGGLE_MODES/composition.md"
MODE="full"
[ "$EDA_ONLY" = "true" ] && MODE="eda-only"
[ "$INFERENCE_ONLY" = "true" ] && MODE="inference-only"
# Derive output filename from mode — must match the composition contract before spawning
OUTPUT_SUFFIX=""
[ "$INFERENCE_ONLY" = "true" ] && OUTPUT_SUFFIX="-inference"
OUTFILE=".experiments/kaggle/${COMPETITION_NAME}${OUTPUT_SUFFIX}.py"
echo "$MODE" > "${TMPDIR:-/tmp}/kaggle-mode"
echo "Mode: $MODE · Output: $OUTFILE"
Read $COMPOSITION_FILE and select the exact $MODE row. Then read each named contract once, from left to right, and read style-rules.md once. Read modality-dispatch.md only when a selected section requests a modality branch. Do not load unselected section contracts. Pass the selected row and resolved contract contents to foundry:sw-engineer after the problem profile block below.
Spawn foundry:sw-engineer with this prompt preamble (inline, then continue with the resolved composition contracts):
Write a complete Kaggle competition notebook script to `<OUTFILE>` (substitute expanded path from bash block above).
Format: Jupytext `# %%` Python script — every cell separated by `# %%` (code) or `# %% [markdown]` (markdown).
## Problem profile
- Competition: <competition-name>
- Problem type: <problem_type>
- Input: <input_modality>
- Output: <output_format>
- Metric: <eval_metric>
- Model: <recommended_model>
- Use PTL: <use_ptl>
- Description: <competition description if available>
[Continue with the selected row from composition.md, followed by the resolved section contracts in order, style-rules.md, and the selected modality branch when applicable.]
## Completion
Write `<OUTFILE>`. Return only:
{"status":"done","file":"<OUTFILE>","lines":N,"sections":N,"problem_type":"<type>","mode":"<MODE>","confidence":0.N}
Synchronous spawn note: foundry:sw-engineer spawned synchronously (not run_in_background=true), so CLAUDE.md §6 poll-based monitoring unreachable mid-call. After Agent() returns, check agent's output under .experiments/kaggle/; missing or empty → treat as timed out, surface with ⏱ marker — never silently omit.
# Compaction contract — boundary: after Step 3 notebook generated (compaction-contract.md §Lifecycle)
_COMPETITION=$(cat "${TMPDIR:-/tmp}/kaggle-competition-name" 2>/dev/null || echo "")
_INF=$(cat "${TMPDIR:-/tmp}/kaggle-inference-only" 2>/dev/null || echo "false")
_KEEP=$(cat "${TMPDIR:-/tmp}/kaggle-keep-items" 2>/dev/null || echo "")
_SUFFIX=""; [ "$_INF" = "true" ] && _SUFFIX="-inference"
_OUTFILE=".experiments/kaggle/${_COMPETITION}${_SUFFIX}.py"
_KEEP_APPEND=""; [ -n "$_KEEP" ] && _KEEP_APPEND="; user-keep: $_KEEP"
mkdir -p .claude/state # timeout: 5000
{
echo "## Active Skill Contract"
echo "- skill: research:kaggle · phase: verify (after Step 3 notebook generated)"
echo "- run-dir: .experiments/kaggle"
echo "- preserve: outfile=${_OUTFILE}, competition=${_COMPETITION}${_KEEP_APPEND}"
echo "- next: Step 4 verify structure, follow-up gate, package distillation"
} > .claude/state/skill-contract.md # timeout: 5000
After agent completes:
# %% structuregrep -c "^# %%" .experiments/kaggle/<name>.pycomposition.md; verify every listed section is present and no unlisted section was generated# Re-derive OUTFILE from flags persisted in Step 1 (bash state lost between steps)
COMPETITION_NAME=$(cat "${TMPDIR:-/tmp}/kaggle-competition-name" 2>/dev/null || echo "$COMPETITION_NAME")
INFERENCE_ONLY=$(cat "${TMPDIR:-/tmp}/kaggle-inference-only" 2>/dev/null || echo "false")
MODE=$(cat "${TMPDIR:-/tmp}/kaggle-mode" 2>/dev/null || echo "full")
OUTPUT_SUFFIX=""; [ "$INFERENCE_ONLY" = "true" ] && OUTPUT_SUFFIX="-inference"
OUTFILE=".experiments/kaggle/${COMPETITION_NAME}${OUTPUT_SUFFIX}.py"
echo "=== Composition ==="; echo "$MODE"
echo "=== Cell count ==="; grep -c "^# %%" "$OUTFILE" # timeout: 5000
echo "=== Sections ==="; grep "^# %% \[markdown\]" "$OUTFILE" # timeout: 5000
echo "=== File size ==="; wc -l "$OUTFILE" # timeout: 5000
Print to terminal:
$OUTFILE)⚠Invoke AskUserQuestion as follow-up gate:
! code $OUTFILEOn (a): run ! code "$OUTFILE" via Bash.
On (b): re-enter Step 3 with extension directive.
On (c): re-enter Step 2 with user-specified changes.
Package distillation gate — invoke after follow-up gate resolves to Done:
Benefits to state before asking: shared helpers tested once, used everywhere; wheel attachment on Kaggle faster than re-inlining; subsequent notebooks shorter; package tests catch regressions before submission.
Invoke AskUserQuestion:
src/<package>/ with extracted helpers + testsIf (a):
plt.show(), no tqdm callssrc/<package>/<module>.py with full Google-style docstring + Example: block — all standard coding patterns apply (doctests for pure functions, Args:/Returns: sections, full if __name__ == "__main__": guards where appropriate); these are package modules, not notebook cellstests/test_<module>.py covering each functionnotebooks/01_<competition-name>_pkg.py — inline definitions replaced by package imports; never modify validated baseline $OUTFILEIf (b): skip; repeat this gate offer after next notebook written.
rm -f .claude/state/skill-contract.md # clear contract — kaggle notebook complete (compaction-contract.md §Lifecycle) # timeout: 5000
npx claudepluginhub borda/ai-rig --plugin researchFetches Kaggle competition overviews, writeups, discussion/kernel research, submissions, and dataset uploads. Automates Kaggle competition workflows.
Integrates with Kaggle for account setup, competition research, dataset/model operations, notebook execution, submissions, discussions, benchmarks, and badge collection.
Analyzes Kaggle competition solutions across NLP, CV, time series, tabular, and multimodal domains. Extracts winning techniques, code templates, and best practices. Use when studying Kaggle competitions or looking for proven ML patterns.