Add local submission history log to guard daily submission slots#17
Add local submission history log to guard daily submission slots#17k66inthesky wants to merge 4 commits into
Conversation
submit_kernel.py now appends every submission attempt and finished evaluation to data/submissions.jsonl, and a new submission_history.py renders the merged history. Before rerunning a submission, a workflow can check what previous runs already submitted — even when the run crashed mid-poll or its terminal output is gone — instead of blindly spending another daily submission slot. Logging is best-effort and can never break an in-flight submission; the history command is local-only and needs no Kaggle credentials. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR adds a local, append-only submission history log to the Kaggle skill so reruns can determine what has already been submitted/evaluated (and with what score) even if a prior submit_kernel.py run crashed or lost terminal output.
Changes:
- Add
submission_log.pyto append/read a JSONL log and merge submission+evaluation events into submission-attempt rows. - Hook
submit_kernel.pyto log submission attempts (accepted/failed) and evaluation outcomes (complete/error/timeout). - Add
submission_history.pyCLI and updatesubmission.md, plus offline unit tests covering log behavior and CLI output.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/test_submission_history.py | Adds offline unit tests for logging, merging, filtering/limits, and CLI JSON/empty-log behavior. |
| skills/nvidia-kaggle-skill/submission.md | Documents the new local submission history log and the submission_history.py command. |
| skills/nvidia-kaggle-skill/scripts/submit_kernel.py | Adds best-effort logging hooks for submission attempts and evaluation outcomes. |
| skills/nvidia-kaggle-skill/scripts/submission_log.py | Introduces the JSONL append/read utilities and event-to-attempt merge logic. |
| skills/nvidia-kaggle-skill/scripts/submission_history.py | Adds a Rich/JSON CLI to render merged submission history from the local log. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…gged_at A timeout is not a terminal Kaggle state — evaluation keeps running after we stop polling — so a later evaluation event for the same (competition, message) may now overwrite a prior timeout in the merged history. And logged_at is now set after the caller's record fields, so the log's own timestamp can never be overridden. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
| attempts.append(dict(record)) | ||
| elif event == EVALUATION_EVENT: | ||
| key = (record.get("competition"), record.get("message")) | ||
| for attempt in reversed(attempts): |
There was a problem hiding this comment.
Not sure here: should submission_attempts() skip failed submit attempts when folding evaluation events back into attempts ? Right now the scan matches the newest prior (competition, message) with no eval_status, regardless of whether Kaggle accepted that submit.
A possible bad sequence:
- Run A submits message
"baseline v1"successfully. - Run B retries with the same message and the submit call fails, e.g. quota/rejection, so it logs
accepted: false. - Run A’s polling later writes the evaluation event for
"baseline v1".
The current merge will attach Run A’s score to Run B’s failed submit row, because Run B is now the newest matching attempt. That makes the history misleading. I think the merge should ignore attempts where accepted is False, plus add a regression test for accepted submit -> failed submit with same message -> evaluation.
There was a problem hiding this comment.
Good catch — fixed in 04f3941: the merge now skips attempts with a falsy accepted, and added the regression test you suggested (accepted submit → failed retry with same message → evaluation lands on the accepted attempt).
|
Hi @k66inthesky thanks for opening this PR! The PR looks nice overall, I left a couple of small comments 🙂 |
- submission_attempts() no longer folds an evaluation into a submit attempt Kaggle rejected, so a failed retry reusing the same message cannot swallow the score of the earlier accepted submission. - submission_history --as-json now always emits valid JSON, printing [] for an empty log instead of a Rich warning. - Add regression tests for both. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
@alessiodevoto Both comments are addressed in 04f3941 — thanks for the catch!
Local tests are fully passing: |
Why
submission.mdalready warns "never rerun this script blindly — each successful submission call can spend one competition submission slot" and the skill's troubleshooting guidance tells agents to read existing logs before retrying, but nothing writes a structured log. When asubmit_kernel.pyrun crashes mid-poll or its terminal output is gone, an agent has no local way to tell whether the daily slot was already spent, so the safe-retry guidance can't actually be followed.This complements
submission_quota.py(#9): quota asks Kaggle how many slots remain today; the history log shows what this workspace already submitted and how it scored, with no API call.What
submit_kernel.pyappends each submission attempt (accepted/failed, with the error text) and each finished evaluation (complete/error/timeout, with the public score) todata/submissions.jsonl— the same project-rootdata/convention as the kernel and discussion caches. Logging is best-effort and can never break an in-flight submission.scripts/submission_log.pyholds the append/read/merge logic as pure functions; evaluation events fold into their submission attempt by the same(competition, message)keysubmit_kernel.pyuses to match submissions on Kaggle.scripts/submission_history.pyrenders the merged history (Rich table, or--as-json), filterable by competition slug/URL, kernel ref, and limit. Local-only: no Kaggle API calls, no credentials.submission.mddocuments the log, the history command, and points the "never rerun blindly" guidance at it.Testing
tests/test_submission_history.py: append/read roundtrip, malformed-line tolerance, merge semantics (including reused messages), filtering/limits, thesubmit_kernel.pyhooks via a fake API object, and the CLI's JSON/empty-log output.uv run pytest: 55 passed, 12 skipped (baseline before this change: 42 passed, 12 skipped). No live-API tests needed.claude plugin validate .: passed.🤖 Generated with Claude Code