Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
f8d4892
docs: plan browser-run checker and interactive Test
TakalaWang Oct 7, 2026
0a68410
refactor: remove server-side Test judging
TakalaWang Oct 7, 2026
1fb2711
feat(problems): read a problem's checker or interactor source
TakalaWang Oct 7, 2026
eb6e499
docs: format the browser Test plan and spec
TakalaWang Oct 7, 2026
00f796f
docs: add the Test stopgap to the dead-code checklist
TakalaWang Oct 7, 2026
fa3f22d
fix(problems): match the judge-program window to its pages
TakalaWang Oct 7, 2026
c54372b
feat(web): prepare a checker in the browser when the editor opens
TakalaWang Oct 7, 2026
d0e187c
feat(web): judge checker samples in the browser
TakalaWang Oct 7, 2026
8b297c4
feat(problems): drop the hidden workspace file visibility
TakalaWang Oct 7, 2026
2dd5ab5
fix(web): queue browser engine work and keep judge-program failures v…
TakalaWang Oct 7, 2026
1574c8d
feat(web): judge interactive samples and custom cases in the browser
TakalaWang Oct 7, 2026
1ea0742
fix(web): merge interactive Test verdicts like official judging
TakalaWang Oct 7, 2026
9d40836
docs(decisions): Test runs entirely in the browser, judge programs in…
TakalaWang Oct 7, 2026
a4080a7
docs: describe browser-run checker and interactive Test
TakalaWang Oct 7, 2026
d1a0396
docs: delete the browser Test plan and spec
TakalaWang Oct 7, 2026
0650a5b
fix(web): let a contestant limit stop decide interactive Test
TakalaWang Oct 7, 2026
8a5d84a
feat(web): tell authors students can read their checker or interactor
TakalaWang Oct 7, 2026
59ed490
fix(web): describe judge_log and the validator timeout accurately
TakalaWang Oct 7, 2026
16790de
fix(web): drop a queued judge-program build when its editor closes
TakalaWang Oct 7, 2026
485a3bb
fix: align Test docs, interactor help and verdict helper with the code
TakalaWang Oct 7, 2026
f8d2b1c
Merge remote-tracking branch 'origin/main' into feat/browser-test-judge
TakalaWang Oct 7, 2026
69ff517
docs: link decision sources to #645
TakalaWang Oct 7, 2026
a874156
build(web): pin @wasm-oj/browser 0.2.4
TakalaWang Oct 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 1 addition & 9 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -73,16 +73,8 @@ SANDBOX_PIDS_LIMIT=64
# Worker
PORT=8080
WORKER_CONCURRENCY=4
# WORKER_MODE: "all" (default), "judge" (sandbox only), "platform" (lifecycle/plagiarism only),
# "test" (checker and interactive Test on the test-judge queue; needs both WASM-OJ dirs below)
# WORKER_MODE: "all" (default), "judge" (sandbox only), "platform" (lifecycle/plagiarism only)
WORKER_MODE=all
# Checker and interactive Test through the WASM-OJ test judge. "all" serves the
# test-judge queue only when both WASM-OJ dirs are set (docs/runbooks/getting-started.md).
TEST_JUDGE_ENABLED=false
# WASM_OJ_RUNTIME_DIR=/home/you/src/wasm-oj-forge/crates/runtime-core/target/release
# WASM_OJ_TOOLCHAIN_DIR=/home/you/.cache/nojv-wasm-oj-toolchains
# WASM_OJ_CACHE_DIR=/tmp/wasm-oj
# TEST_JUDGE_SLOTS=2
# Minutes a submission may stay pending/running before the platform sweeper marks
# it system_error and terminates its judge. Range 10–1440; defaults to 10.
SUBMISSION_PENDING_TIMEOUT_MINUTES=10
Expand Down
12 changes: 0 additions & 12 deletions .github/renovate.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,6 @@
"matchDepTypes": ["packageManager", "engines"],
"enabled": false
},
{
"description": "WASM-OJ packages are pinned to the forge release in the worker image and checked by the pin test; they move by hand with a forge upgrade",
"matchManagers": ["npm"],
"matchPackageNames": ["/^@wasm-oj\\//"],
"enabled": false
},
{
"matchManagers": ["github-actions"],
"schedule": ["* 9-11 1-7 * 1"],
Expand All @@ -122,12 +116,6 @@
"matchPackageNames": ["node"],
"enabled": false
},
{
"description": "The rust image only builds the pinned WASM-OJ runtime; each bump replaces an 82 MB worker layer, so it moves by hand with a forge upgrade",
"matchDatasources": ["docker"],
"matchPackageNames": ["rust"],
"enabled": false
},
{
"description": "alpine/k8s tracks the cluster's kubectl minor",
"matchDatasources": ["docker"],
Expand Down
43 changes: 14 additions & 29 deletions apps/web/messages/en.json
Original file line number Diff line number Diff line change
Expand Up @@ -270,13 +270,8 @@
"admin_announcementsUnpin": "Unpin",
"admin_announcementsUnpublish": "Unpublish",
"admin_cancel": "Cancel",
"admin_checkerHelpBody": "Your validator follows the DOMjudge output-validator interface and runs in its own isolated container — the student's program never sees the answer. It is invoked as `validator <input> <judge_answer> <feedback_dir>` with the student output on stdin. It renders an accept/reject verdict only (no partial scoring). Python: `judge_input`, `judge_answer`, `team_output` are pre-bound as strings, and helpers `accept(team_msg=\"\")`, `wrong(team_msg=\"\")`, `judge_log(msg)` write the right feedback files and exit. C++: read `argv[1]` / `argv[2]` and stdin yourself, write `teammessage.txt` into the `argv[3]` feedback dir, and exit 42 (accept) or 43 (wrong) — no judge header required. Feedback is shown to the student. Validator timeout is 30 seconds; a crash or hang becomes a system error and does not count against the student.",
"admin_checkerHelpBody": "Your validator follows the DOMjudge output-validator interface. On Submit it runs in the judge sandbox, apart from the student's program, which never sees the answer; on Test it runs in the student's browser and checks the samples. It is invoked as `validator <input> <judge_answer> <feedback_dir>` with the student output on stdin. It renders an accept/reject verdict only (no partial scoring). Python: `judge_input`, `judge_answer`, `team_output` are pre-bound as strings, and `accept(team_msg=\"\")` and `wrong(team_msg=\"\")` write the feedback and exit, and `judge_log(msg)` writes the staff-only judge message. C++: read `argv[1]` / `argv[2]` and stdin yourself, write `teammessage.txt` into the `argv[3]` feedback dir, and exit 42 (accept) or 43 (wrong) — no judge header required. Feedback is shown to the student. The validator gets the larger of 30 seconds and the time limit; a crash or hang becomes a system error and does not count against the student.",
"admin_checkerHelpTitle": "How to write a checker",
"admin_checkSamples": "Check samples with the checker",
"admin_checkSamplesRejected": "The checker must accept each sample's output, because students' Test uses it as the answer. Fix the checker or the sample.",
"admin_checkSamplesSaveFirst": "Save your changes first to check samples against them.",
"admin_checkSamplesUnavailable": "The test judge is unavailable right now. Try again later.",
"admin_checkingSamples": "Checking samples…",
"admin_compareCaseSensitive": "Case-sensitive comparison",
"admin_compareFloatTolerance": "Float tolerance (ε)",
"admin_compareFloatToleranceHint": "Leave empty for exact matching. When set, numeric tokens match within absolute OR relative error ε. For anything more, write a Checker.",
Expand Down Expand Up @@ -306,7 +301,6 @@
"admin_envRemove": "Remove env var",
"admin_envVars": "Environment variables",
"admin_fileEditable": "Editable",
"admin_fileHidden": "Hidden",
"admin_filePath": "Path",
"admin_filePathPlaceholder": "main.c or include/header.h",
"admin_fileReadonly": "Read-only",
Expand All @@ -317,16 +311,12 @@
"admin_inputFormatTooltip": "Describe the input format. Supports Markdown.",
"admin_interactionFormat": "Interaction notes",
"admin_interactionFormatTooltip": "Describe what the interactor reads from its input file and how it talks to the student's program. Shown to students. Supports Markdown.",
"admin_interactorHelpBody": "Your interactor follows the DOMjudge interactive interface and runs in its own isolated container, wired to the student program by a byte proxy: student stdout flows to interactor stdin, interactor stdout flows to student stdin. It is invoked as `interactor <input> <judge_answer> <feedback_dir>`. Python: `judge_input` and `judge_answer` are pre-bound strings; `read()` reads one line from the student (auto-rejecting if the stream closes) and `write(msg)` sends one line back (already flushed). Helpers `accept(team_msg=\"\")`, `wrong(team_msg=\"\")`, `set_score(x)`, `judge_log(msg)` write the feedback files and exit 42/43. C++: read `argv[1]`/`argv[2]`, talk to the solution over `cin`/`cout` (flush before reading), write `score.txt` / `teammessage.txt` into `argv[3]`, and exit 42 (accept) or 43 (wrong) — no judge header required. The student and interactor share the problem's time limit.",
"admin_interactorHelpBody": "Your interactor follows the DOMjudge interactive interface. On Submit it runs in the judge sandbox, and on Test in the student's browser; either way student stdout flows to interactor stdin, and interactor stdout flows to student stdin. It is invoked as `interactor <input> <judge_answer> <feedback_dir>`; Test passes an empty `judge_answer`, so read the secret from `judge_input`. Python: `judge_input` and `judge_answer` are pre-bound strings; `read()` reads one line from the student (auto-rejecting if the stream closes) and `write(msg)` sends one line back (already flushed). `accept(team_msg=\"\")` and `wrong(team_msg=\"\")` write the feedback and exit 42/43, and `judge_log(msg)` writes the staff-only judge message. C++: read `argv[1]`/`argv[2]`, talk to the solution over `cin`/`cout` (flush before reading), write `teammessage.txt` into `argv[3]`, and exit 42 (accept) or 43 (wrong) — no judge header required. The student and interactor share the problem's time limit.",
"admin_interactorHelpTitle": "How to write an interactor",
"admin_interactorLanguage": "Interactor language",
"admin_judgeChecker": "Checker script",
"admin_judgeInteractive": "Interactive",
"admin_judgeProgramDiagnostics": "Build output",
"admin_judgeProgramTestFailed": "This judge program can't run in Test. Submissions are still judged normally.",
"admin_judgeProgramTestPending": "Preparing this judge program for Test…",
"admin_judgeProgramTestReady": "Test can run this judge program.",
"admin_judgeProgramTestUnavailable": "Couldn't check this judge program right now.",
"admin_judgeProgramReadableNote": "Students can read this program when they press Test.",
"admin_judgeStandard": "Standard (stdin/stdout diff)",
"admin_judgeType": "Judge type",
"admin_judgeTypeHint": "How each testcase is evaluated.",
Expand Down Expand Up @@ -403,8 +393,6 @@
"admin_tabBasicInfo": "Basic Info",
"admin_tabReports": "Reports",
"admin_tabJudge": "Judge Settings",
"admin_testJudgeDisabled": "Test judging isn't enabled on this server.",
"admin_testPythonInteractorUnsupported": "Test doesn't support Python interactors yet.",
"admin_tabLocked": "Please complete and save basic info first",
"admin_tabLockedFields": "Fill in and save these in Basic Info first: {fields}",
"admin_tabOverview": "Overview",
Expand Down Expand Up @@ -472,7 +460,7 @@
"admin_workspaceEmptyFullSource": "No starter template for {language}. Leaving this blank is fine for single-file problems — students will see an empty editor.",
"admin_workspaceEmptyMultiFile": "No files for {language} yet. Multi-file problems need at least a main file named {filename}.",
"admin_workspaceFilesHintFullSource": "Optionally provide starter code per language. Students see this template when they open the editor. Leave blank for an empty editor.",
"admin_workspaceFilesHintMultiFile": "Provide one editable main file per allowed language, plus any helper files (readonly or hidden).",
"admin_workspaceFilesHintMultiFile": "Provide one editable main file per allowed language, plus any readonly helper files.",
"admin_workspaceFilesTitleFullSource": "Starter templates (optional)",
"admin_workspaceFilesTitleMultiFile": "Workspace files",
"admin_workspaceLanguageMissing": "Missing template",
Expand Down Expand Up @@ -1145,23 +1133,23 @@
"editor_invalidSource": "Source code must be nonblank and fit the submission limits: 50,000 characters for a single file, or 500,000 per editable workspace file. Check file paths as well.",
"editor_invalidRunCases": "Provide 1–10 testcases, with at most 200,000 characters in each input or expected output.",
"editor_requestTooLarge": "The combined source and testcase request exceeds 2 MiB. Reduce its size and try again.",
"admin_workspaceHiddenTestNote": "Browser Test cannot access hidden files. Public code must compile and run without them. If a dependency is safe to share, mark it read-only; otherwise redesign the public test workspace. Hidden files remain server-only.",
"editor_clientTestCustomImage": "This problem requires a custom container that cannot run in the browser. Ask the author to provide a browser-compatible Test workspace.",
"editor_clientTestLanguage": "The browser runtime for this language is unavailable. Test runs entirely on your device.",
"editor_testInteractiveLanguage": "Test can't run interactive problems in JavaScript or TypeScript yet. Switch to another language, or use Submit to have your code judged.",
"editor_testNoInteractiveSamples": "This problem has no sample with an interactor input, so there is nothing to test. Use Submit to have your code judged.",
"editor_testJudgeBusy": "Test is busy right now. Try again in a moment.",
"editor_testJudgeProgramBuildFailed": "This problem's checker or interactor can't run in Test. Submit still judges your code normally.",
"editor_testUnavailableForProblem": "Test isn't available for this problem right now. Submit still judges your code normally.",
"editor_testUnsupportedProblemType": "This problem type doesn't support Test. Use Submit to have your code judged.",
"editor_testTooLarge": "This Test request is too large to send to the server. Reduce your program's size or output, or use Submit to have your code judged.",
"editor_judgingOnServer": "Judging on the server…",
"editor_checkerPreparing": "Preparing checker...",
"editor_checkerBuildFailed": "This problem's checker failed to build.",
"editor_checkerLoadFailed": "Couldn't load this problem's checker. Reload the page to try again.",
"editor_checkerUnavailable": "Couldn't load this problem's checker.",
"editor_interactorPreparing": "Preparing interactor...",
"editor_interactorBuildFailed": "This problem's interactor failed to build.",
"editor_interactorLoadFailed": "Couldn't load this problem's interactor. Reload the page to try again.",
"editor_interactorUnavailable": "Couldn't load this problem's interactor.",
"editor_judgeSystemError": "The judge failed on this case. This is a problem on the judge's side, not with your program.",
"editor_interactiveTestNote": "Test runs your program against this problem's interactor on each sample's interactor input; the interactor judges your replies live. You can't add your own cases to an interactive problem.",
"editor_interactiveTestNote": "Test runs your program against this problem's interactor on each case's interactor input; the interactor judges your replies live. Cases you add are judged the same way.",
"editor_checkerCasesNote": "This problem's checker judges the samples only. Cases you add or change just run without a verdict, so check their output yourself.",
"editor_executed": "Executed",
"editor_executedNote": "This case ran without being judged. Check its output yourself.",
"editor_judgedOnServer": "Judged on the server",
"editor_judgeFeedback": "Feedback",
"editor_transcript": "Transcript",
"editor_transcriptFromInteractor": "From the interactor",
Expand Down Expand Up @@ -1596,7 +1584,7 @@
"problemDetail_loadingSource": "Loading source code...",
"problemDetail_memoryLimit": "Memory limit",
"problemDetail_multiFileBadge": "Multi-file",
"problemDetail_multiFileHelp": "The problem ships multiple files — one or more you edit plus read-only helpers (and sometimes hidden files). You edit the designated files in the browser and submit them together.",
"problemDetail_multiFileHelp": "The problem ships multiple files — one or more you edit plus read-only helpers. You edit the designated files in the browser and submit them together.",
"problemDetail_noSubmissions": "No submissions yet.",
"problemDetail_output": "Output",
"problemDetail_outputFormat": "Output Description",
Expand Down Expand Up @@ -1907,12 +1895,9 @@
"virtualContest_youLabel": "You",
"workspace_description": "Description",
"workspace_descriptionPlaceholder": "Describe what this file is for…",
"workspace_fileHidden": "This file is hidden",
"workspace_fileHiddenNoDescription": "(no description provided)",
"workspace_filesHeading": "Files",
"workspace_mustHaveMainFile": "Each enabled language must have an editable file named {filename}",
"workspace_visibilityEditable": "edit",
"workspace_visibilityHidden": "hidden",
"workspace_visibilityReadonly": "read",
"contestScoreboard_notStartedHint": "The scoreboard opens once the contest begins.",
"contestDetail_settingsDeleteConfirmBody": "This will permanently remove the contest draft.",
Expand Down
Loading
Loading