Skip to content

Make agent-written reports drop-in ready for Kaggle Discussions - #16

Open
daxiongshu wants to merge 3 commits into
mainfrom
jl/kaggle-markdown-export
Open

Make agent-written reports drop-in ready for Kaggle Discussions#16
daxiongshu wants to merge 3 commits into
mainfrom
jl/kaggle-markdown-export

Conversation

@daxiongshu

Copy link
Copy Markdown
Collaborator

Two gaps surfaced while an agent wrote a top-kernel report meant to be pasted into a Kaggle Discussion:

  1. Reports weren't actually drop-in ready. A locally-rendered report embeds ![](plots/…) paths that Kaggle renders as broken images. The fix (kaggle_markdown_export.py, which rewrites local image links to public raw URLs and --verifys they return image bytes) lived only in research-brief.md — invisible to an agent routed to kernels.md, so it shipped local paths and called it done.

  2. Correct per-version scores still can't tell you why a score moved. kernel_archive.py already returns the verified LB for each version — but kernels can be stochastic, so identical code posts different scores across versions.

Changes

  • kaggle_markdown_export.py — rewrite local image links to a public base URL + --verify they return image bytes.
  • diff_kernel_versions.py — builds on kernel_archive.py: pulls each version's code, diffs it (.ipynb/.py), and labels IDENTICAL rerun vs CHANGED (+n/−m) so a score move is attributable to an edit or to noise; also flags shared-SHA reruns/forks.
  • SKILL.md — new always-loaded "drop-in ready for Kaggle" section + troubleshooting row.
  • kernels.md — surface both scripts and document version-diffing / fork-by-code-hash.
  • research-brief.md — the original export-step write-up.

Docs only + two self-contained scripts; no changes to existing behavior.

Reviewer and others added 3 commits July 3, 2026 17:48
…e paste

Kaggle's discussion editor renders standard Markdown plus GFM tables and
`![alt](url =WxH)` sizing (confirmed via the official announcement at
kaggle.com/product-feedback/82853), so a research-brief.md pastes in
almost unchanged. The one thing that doesn't survive unmodified is a
local `![alt](plots/foo.png)` reference: Kaggle fetches whatever the
target resolves to, so it needs to already be a public URL serving raw
image bytes, not a local path or an HTML share/viewer page.

kaggle_markdown_export.py rewrites local (non-http, non-data) image
targets to `<base-url>/<same-relative-path>` and leaves everything else
untouched, then `--verify` fetches each resulting URL and checks for an
image/* content-type - catching the exact share-page-vs-raw-link mistake
(e.g. a GitHub blob URL returns 200 text/html, not the image).

Experiment: ran it against the two existing rogii-wellbore brief.md
files in daxiongshu/competition-brief-demo (already public), pointing
--base-url at their real raw.githubusercontent.com paths. Both rewrote
cleanly (2 and 4 image refs respectively) and --verify confirmed every
resulting URL returns 200 image/png. Also confirmed the tool correctly
fails a broken base URL (404) and a GitHub blob URL (200 text/html)
rather than false-positive passing them.

Documented the workflow in research-brief.md under a new "Sharing the
brief as a Kaggle discussion post" section.

Co-Authored-By: Claude <noreply@anthropic.com>
…ernel workflow

Two gaps surfaced writing a top-kernel report: (1) the "make it paste-ready
for Kaggle" image-rewrite step lived only in research-brief.md, invisible to
an agent routed to kernels.md, so local image paths shipped unfixed; (2) there
was no tooling to tell a real code change from an identical stochastic rerun,
which per-version scores alone cannot reveal.

- SKILL.md: add a cross-cutting "Making any report paste-ready for a Kaggle
  post" section (always loaded) + a troubleshooting row keyed on the symptom.
- kernels.md: point the report-writing step at that section; document and
  surface version-diffing (real change vs identical rerun; fork-by-code-hash).
- scripts/diff_kernel_versions.py: new one-shot tool that downloads a kernel's
  versions, diffs code (ipynb/py), and labels IDENTICAL rerun vs CHANGED with
  +/- line counts and a shared-SHA fork/rerun detector.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…andle the no-host case

The paste-ready guidance leaned so hard on raw.githubusercontent.com that it read
as "GitHub required", never enumerated host options, and — the real gap — said
nothing about what to do when the user provides no public location at all.

- State plainly that any public URL returning image/* bytes works (the export
  step hosts nothing); list working hosts (GitHub raw/Pages, S3/GCS/R2, any CDN)
  and non-working ones (GitHub blob pages, Drive/Dropbox share links, local paths).
- Add the no-host path: don't silently ship local ![](plots/...) paths and call
  it paste-ready; deliver the report with local paths, name the remaining hosting
  step, offer options (incl. Kaggle's manual drag-drop upload), then rewrite+verify.

Applied to SKILL.md (canonical) and research-brief.md; docs only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

# Matches `![alt](path)` / `![alt](path =WxH)` where `path` is NOT already an
# absolute http(s) or data URL - i.e. exactly the references that need rewriting.
LOCAL_IMAGE_RE = re.compile(r"!\[([^\]]*)\]\(\s*(?!(?:https?://|data:))([^)\s]+)(\s+=\S+)?\s*\)")

@Jack-Yu-815 Jack-Yu-815 Jul 10, 2026

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice addition, confirming the finished document is a useful safeguard. One edge case is that these regexes miss valid markdown links such as:

![plot](plots/loss.png "validation loss")
![plot](<plots/validation loss.png>)

This could happen especially if created by agents. Can we cover these cases?

Comment on lines +92 to +96
for vnum in targets:
vmeta = by_num.get(vnum, {})
try:
archive_kernel_version(kernel_ref, str(output_dir), vnum,
include_outputs=include_outputs, force=True)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This works for short version histories, but the number of Kaggle API requests grow O(N^2) because archive_kernel_version is called n times, and each time it will call resolve_kernel_versions one time, which calls _get_view_model (sending post request) n times. So that's O(N^2) requests where N is the number of versions.
An example is cdeotte/titanic-wcg-xgboost-0-84688, which has 43 versions. Comparing all 43 with this flow makes roughly 2,000 internal Kaggle API calls before retries, so it’s likely to encounter rate limits or long network block, and even worse if versions is in the hundreds.

One possible solution:

  1. remove the resolve_kernel_versions call inside archive_kernel_version, and archive_kernel_version assumes that that the version_number is valid.
  2. use resolve_kernel_versions to enumerate the full set of valid kernel versions.
  3. loop over each vnum in targets:
    if vnum exists in the resolved version set, use the updated archive_kernel_version function to download that kernel version. The version number validity check happens at this step, instead of inside archive_kernel_version.

This should reduce the Kaggle requests to O(N). Hopefully this makes sense!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants