Add a Problem to ROJ
SkillDev toolsImport programming problems into this repository's roj/ corpus from local problem directories, local contest roots, or an explicitly supplied OJ problem URL. Use this skill whenever the user asks to add, import, migrate, register, copy, or prepare a problem for roj, especially when they mention a local path, statement.md, 题面.md, down/下发文件, data/, std.cpp, or finding an unused roj problem ID. This skill governs the complete review-first workflow and must be used even when the user asks for only one part of the import.
Available today. Use it from your connected AI after setup.
No other account needed.
Connect ahel once, and every AI you use reads what you have installed.
Then ask your AI: use the Add a Problem to ROJ skill
What this skill tells your AI
The instructions your AI receives, as published by rainboyoj/problems in .agents/skills/add-roj-problem/SKILL.md and read by ahel’s review.
Use this skill to prepare one or more read-only problem records in the repository's
roj/ directory. The goal is a complete, reviewable problem record, not an online
judge integration. The website discovers numeric roj/<id>/ directories at startup.
Non-negotiable boundaries
- Work only inside the repository's
roj/directory and the user-supplied local source path. Never scan or copy an unrelated broad filesystem path. - Do not copy or expose registration, judging, or account data.
- Do not expose
std.cpp, generators, checkers, or other maintenance files. The website's download feature treats allowlisted files underdata/as public download candidates listed indata.json; the legacydown/directory is retained for compatibility but is not part of the new download UI. - Do not overwrite an existing
roj/<id>/directory. A collision is a stop-and-ask condition, not a reason to replace files. - Do not write anything before producing a preview and receiving explicit user confirmation for that import batch.
- Do not run
git add,git commit, orgit pushautomatically. - Do not put local absolute paths, credentials, or private source URLs into
config.jsonorcontent.md.
Accepted input
The primary input is a local path, for example:
/home/rainboy/tmp-暑假比赛
/home/rainboy/tmp-暑假比赛/08-25/A
Also support an explicitly supplied source platform plus problem ID or URL. If the user supplies only a title without a source or local path, ask for the missing source instead of guessing a problem.
Classify a local path as follows:
- If it contains a recognizable statement file, PDF, or an unambiguous problem record, treat it as one problem directory.
- Otherwise inspect its immediate child directories as candidate problems.
- Do not recursively treat every directory as a problem. Skip
.git,rainboy-解析,problem-analysis-workspace,data,down,下发文件,大样例, and directories without a statement. - If one contest-level statement appears to contain multiple problems, do not copy the same combined statement into every child. Put those candidates in the preview as requiring a manual split and wait for clarification.
ID allocation
Use the bundled helper instead of reimplementing directory scanning:
python3 .agents/skills/add-roj-problem/scripts/find-empty-roj-id.py
python3 .agents/skills/add-roj-problem/scripts/find-empty-roj-id.py --mode append
python3 .agents/skills/add-roj-problem/scripts/find-empty-roj-id.py --start 20000
python3 .agents/skills/add-roj-problem/scripts/find-empty-roj-id.py --start 30000
The helper is read-only and scans only direct numeric directories under roj/.
Its default mode is the first empty ID at or above 1000; --mode append returns
the next ID after the largest existing ID. Successful stdout contains only the
integer. For a batch, sort candidate source paths with a deterministic natural sort
(A, B, C; T1, T2, T10) and reserve IDs in that order. Re-scan immediately
before the first write to detect a concurrent collision.
Choose the ID range by problem type. Before allocating IDs, ask the user which type the candidate problem(s) belong to when it is not obvious from the source (a contest directory, a single standalone problem, etc.):
- 比赛题目 (contest problem, e.g. problems from 暑假比赛, 模拟赛, or other
contest/round sources): allocate from the
20000+range by runningfind-empty-roj-id.py --start 20000. This keeps contest imports grouped together and away from the legacy1000+problems. - 其他题目 (standalone/ordinary problems, e.g. textbook or OpenJudge-style
problems): use the default mode (first empty ID at or above
1000).
Use --mode append instead when the intent is to continue after the largest existing
ID regardless of range.
Review-first workflow
1. Discover and classify
Read the source tree and build a manifest before making directories. For every candidate, record its source path, proposed ID, title, statement carrier, source program candidate, public download files, official test files, image references, total bytes, and warnings.
Recognize statement carriers in this order:
- Existing
content.md statement.md,题面.md,problem.md- Another Markdown file only when it is clearly the sole statement (do not select
sol.md,题解.md, analysis notes, or workspace notes) content.pdfor a clearly identified statement PDF when no Markdown statement is available
If both Markdown and PDF statements exist, use Markdown for content.md and do not
copy the redundant PDF unless the user explicitly requests both. A PDF-only problem
is valid and should receive content.pdf.
If no statement Markdown/PDF can be identified, do not create a partial record. Report the candidate as missing a statement and continue with other candidates.
2. Choose the standard source
Select a source program only from the candidate problem directory's root:
std.cpp- A root-level
.cppwhose basename clearly matches the problem name - The only ordinary root-level
.cpp
Never select a file named or clearly used as brute, gen, checker, validator,
std2, a test helper, or a file inside data/, down/, 下发文件/, or
rainboy-解析/. Copy the selected source as std.cpp. If no unambiguous source
exists, do not invent an empty file; import may proceed only with a warning that
std.cpp is unavailable.
3. Derive metadata
Create a JSON object with no null values:
title: use a clear title from the statement; otherwise use the contest name plus original label, such as2026 暑假比赛 - T1. If still ambiguous, pause for the user's choice.time: parse an explicit per-problem limit, then a clearly stated contest-wide limit. Convert seconds to integer milliseconds.memory: parse an explicit per-problem limit, then a clearly stated contest-wide limit. Store the numeric value in MB.tags: use known string tags only; otherwise use[].source: use a human-readable contest/source label and original problem label, not an absolute local path.
Omit unknown time, memory, or source fields rather than guessing or writing
null. Serialize with UTF-8, two-space indentation, and a trailing newline. Validate
that the complete JSON tree contains no null values and that all tags are strings.
4. Normalize the statement
The ROJ renderer does not allow raw HTML. When a Markdown file contains HTML tags,
convert it to standard Markdown with pandoc (choose the input mode that matches the
file, such as Markdown with raw HTML or HTML) and --wrap=none. Preserve headings,
paragraphs, lists, tables, code blocks, LaTeX formulas, and links. Do not use a broad
regular-expression rewrite as a substitute for an HTML parser.
After conversion, inspect the output for leftover structural HTML, lost formulas,
collapsed code blocks, or broken entities. If conversion cannot be performed because
pandoc is unavailable or the result is ambiguous, stop that candidate and report it.
For local images referenced by the statement, resolve paths relative to the source statement, copy only the referenced files into the new problem directory, and keep their relative references. Preserve remote image URLs. Missing local images are a preview warning and require confirmation before importing that candidate.
5. Copy repository files
Build each candidate in a temporary directory outside roj/, then validate it before
moving it into the final numeric directory:
roj/<id>/
├── config.json
├── data.json # generated public-data manifest, even when empty
├── content.md # or content.pdf for PDF-only problems
├── std.cpp # only when a source was selected
├── data/ # public data candidates; web2 applies an extension filter
└── down/ # legacy compatibility data, not used by the new UI
Copy the source data/ directory recursively, preserving filenames and subdirectories.
The web2 download feature exposes only ordinary files with case-insensitive .in,
.out, .ans, .txt, or .dat extensions; source code, scripts, executables,
compiled artifacts, hidden files, caches, and symlinks are never public downloads.
Treat a clearly identified 大样例/ directory as data candidates only when the user
confirms it contains files intended for visitors; otherwise report it as unresolved.
Copy down/ and 下发文件/ into down/ only when preserving legacy source material
is part of the requested import, preserving their relative directory structure. This
directory is not linked from new contest/problem download UI. Copy only data-like
regular files such as .in, .out, .ans, .txt, or .dat; exclude source programs
and helper scripts. If a file has an unfamiliar extension, list it in the preview
instead of silently deciding its visibility.
Do not copy solutions, analysis workspaces, readmes, generators, checkers, or unrelated attachments. Do not follow symlinks outside the source problem directory.
Reject or pause a candidate when any single file intended for a GitHub Raw link is over 100 MB. GitHub cannot reliably serve such a file through the agreed link; wait for an explicit Git LFS or external storage decision.
6. Generate the public data manifest
Do not append a managed download-link block to content.md. Generate
roj/<id>/data.json with the bundled manifest tool after copying the public data:
python3 .agents/skills/add-roj-problem/scripts/generate-data-manifest.py \
--root roj --problem <id>
The manifest contains only a sorted files array of { "path", "size" } objects.
Generate { "files": [] } for a problem with no public data. The web2 API reads this
small manifest from bohai and gives the browser and roj.py configured GitHub Raw or
mirror source URLs; no VPS data URL is created. In the preview, report the public
data candidates, their relative paths, sizes, and the generated manifest.
Before finalizing an import, run the validator:
python3 .agents/skills/add-roj-problem/scripts/generate-data-manifest.py --check
The validator rejects missing or stale manifests, unsafe paths, unsupported extensions, symlinks, and any single public file over 100 MiB. Do not claim a Raw download URL works until the destination is committed and available on GitHub.
Existing legacy download-link blocks are not rewritten automatically. If the user asks
to migrate one, show a separate before/after preview and remove only links explicitly
covered by that request. Do not copy data/ or down/ into the bohai deployment; the
deployment script excludes both directories.
7. Preview and confirm
Present a flat preview before writing. Include proposed IDs, titles, statement type, source selection, data/down file counts and sizes, public data candidate count, and every warning or unresolved choice. Explicitly call out skipped directories and why.
Do not create final roj/<id>/ directories until the user confirms the complete
batch. Once confirmed, write each candidate to a temporary directory and atomically
rename it into place only after that candidate passes validation. Never overwrite a
pre-existing destination.
8. Validate and report
For every imported problem, verify:
config.jsonparses and contains nonullvalues; tags are strings- exactly one usable statement carrier exists (
content.mdorcontent.pdf) data.jsonexists, parses, and matches the copied public data (including an emptyfilesarray when no public data exists)content.mdhas no unapproved structural raw HTML- all local image references resolve to copied files
- every reported public data candidate maps to a copied
data/file std.cppexists when a source was selecteddata/anddown/contain no unexpected symlink escapes- no destination ID collided during the write
git diff --checkis clean for the new text files
If the ROJ web server is already running, request each imported /problem/<id> page
and each PDF URL that the current server supports. Verify the manifest endpoint and,
when public data exists, verify at least one configured GitHub source URL after the
destination is committed. Do not claim a download URL works before deployment.
Report imported IDs, skipped candidates, warnings, file counts, and validation results. Leave all Git operations to the user.
Common mistakes to avoid
- Do not use the broken
x.isnumericbound method from the oldbin/next.py; the bundled helper calls numeric checks correctly and ignoresinstall.shand other non-directory entries. - Do not infer a title from a generic heading such as
题目描述when a contest label is available. - Do not mistake
题解.md,sol.md, or analysis notes for the statement. - Do not expose non-allowlisted
data/files orstd.cppmerely because they were copied. - Do not claim a GitHub Raw download works before the files are committed to the repository at the generated paths.
Signals
- GitHub stars
- 34
- Forks
- 20
- Last commit
- Sep 2026
Advanced
- Catalog kind
- skill
- Gateway key
add-roj-problem- Source
- github.com/rainboyoj/problems