Skip to content

judge: add multi-pass problem support#1167

Open
pandadtdyy wants to merge 14 commits into
masterfrom
multi-pass
Open

judge: add multi-pass problem support#1167
pandadtdyy wants to merge 14 commits into
masterfrom
multi-pass

Conversation

@pandadtdyy
Copy link
Copy Markdown
Member

@pandadtdyy pandadtdyy commented May 25, 2026

Summary by CodeRabbit

  • New Features

    • Multi-pass problems: checkers may emit next-pass input/state and the judge will rerun up to the configured pass limit; messages indicate pass numbers and exceeding the limit yields an error.
  • Validation

    • Multi-pass must be an integer within allowed bounds; interactive problems require an interactor; default problems with multi-pass enforce allowed checker types.
  • Chores

    • UI: form toggle and numeric input for multi-pass; editor schema and Chinese locale updated.

Review Change Stack

@pandadtdyy pandadtdyy requested a review from Copilot May 25, 2026 12:10
@github-actions
Copy link
Copy Markdown
Contributor

github-actions Bot commented May 25, 2026

All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 25, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Adds multi-pass judging: ProblemConfigFile gains optional multi_pass; validation enforces interactor presence for interactive problems and multi_pass ∈ [2,20], and restricts default-problem multi-pass checkers to testlib/kattis. Checkers (testlib/kattis) can return nextPass (input and optional state). Default and interactive judges maintain per-case mp state, reuse mp.input/state across passes, annotate HYDRO_MULTI_PASS, and recursively re-run until max passes. UI adds a toggle and numeric input with YAML guards. Chinese locale adds "Max Passes".

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'judge: add multi-pass problem support' directly summarizes the main change—introducing multi-pass problem functionality across the judge system.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch multi-pass

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@pandadtdyy pandadtdyy marked this pull request as draft May 25, 2026 12:10
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces “multi-pass” judging, allowing a problem to be evaluated across multiple sequential passes where a checker/interactor can emit the next-pass input (and optional state) until completion or a configured pass limit.

Changes:

  • Add multi_pass to problem config types, editor serialization, Monaco YAML schema, and UI controls.
  • Extend default + interactive judges to support repeated execution passes and annotate result messages with pass context.
  • Extend testlib and kattis checkers to optionally emit nextpass.in and state.txt artifacts for subsequent passes.

Reviewed changes

Copilot reviewed 10 out of 10 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
packages/ui-default/locales/zh.yaml Adds Chinese strings for multi-pass UI controls.
packages/ui-default/locales/en.yaml Adds English strings for multi-pass UI controls.
packages/ui-default/components/problemconfig/ProblemType.tsx Adds UI toggle + numeric input for configuring multi_pass.
packages/ui-default/components/problemconfig/ProblemConfigEditor.tsx Includes multi_pass in YAML formatting/serialization (omits when disabled).
packages/ui-default/components/monaco/schema/problemconfig.ts Adds multi_pass to Monaco YAML schema for editor validation.
packages/hydrojudge/src/judge/interactive.ts Implements multi-pass support for interactive judging via interactor-emitted next input/state.
packages/hydrojudge/src/judge/default.ts Implements multi-pass support for default judging via checker-emitted next input/state.
packages/hydrojudge/src/checkers.ts Extends testlib/kattis checkers to return nextPass data from optional output files.
packages/hydrojudge/src/cases.ts Adds server-side validation for multi_pass and interactive interactor requirements.
packages/common/types.ts Adds multi_pass to the shared ProblemConfigFile type.
Comments suppressed due to low confidence (1)

packages/hydrojudge/src/judge/default.ts:111

  • The TLE rerun path calls runner(ctx, ctxSubtask) without passing the runner argument. With multi-pass enabled, the next pass relies on the third parameter (runner) for recursion; on a rerun this becomes undefined and runner! will crash when advancing to the next pass.
        if (runner && ctx.rerun && c.time <= 5000 && status === STATUS.STATUS_TIME_LIMIT_EXCEEDED) {
            ctx.rerun--;
            return await runner(ctx, ctxSubtask);
        }

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread packages/hydrojudge/src/judge/default.ts Outdated
Comment thread packages/hydrojudge/src/judge/default.ts
Comment thread packages/hydrojudge/src/judge/interactive.ts
Comment thread packages/ui-default/components/problemconfig/ProblemType.tsx Outdated
Comment thread packages/ui-default/components/monaco/schema/problemconfig.ts
Comment thread packages/hydrojudge/src/cases.ts
Comment thread packages/hydrojudge/src/cases.ts Outdated
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (3)
packages/ui-default/components/problemconfig/ProblemConfigEditor.tsx (1)

53-53: ⚡ Quick win

Normalize multi_pass by problem type during YAML formatting.

Line 53 only checks the numeric value. Consider also guarding by config.type (and default checker compatibility) so stale multi_pass doesn’t leak into unsupported types.

💡 Suggested change
-      if (key === 'multi_pass' && (!config.multi_pass || config.multi_pass <= 1)) return;
+      if (key === 'multi_pass') {
+        const mp = Number(config.multi_pass || 0);
+        const allowed = config.type === 'interactive'
+          || (config.type === 'default' && ['testlib', 'kattis'].includes(config.checker_type as string));
+        if (!allowed || mp <= 1) return;
+      }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ui-default/components/problemconfig/ProblemConfigEditor.tsx` at line
53, The YAML formatter currently only checks key === 'multi_pass' and numeric
value (config.multi_pass) which can leave a stale multi_pass on problem types
that don't support it; update the guard in ProblemConfigEditor (the branch
handling key === 'multi_pass') to also check config.type and the problem's
checker compatibility before allowing multi_pass to persist—i.e., if config.type
is not a type that supports multi-pass or the selected checker is incompatible,
treat multi_pass as absent (skip or delete it) otherwise keep it when >1;
reference the existing key, config.multi_pass, and config.type checks to locate
where to add the extra condition.
packages/ui-default/components/problemconfig/ProblemType.tsx (1)

21-52: ⚡ Quick win

Gate default-type multi-pass behind compatible checker types.

For default problems, Line 147 currently shows multi-pass controls even when checker_type is not testlib/kattis. That allows users to set invalid combinations that backend validation will reject.

💡 Suggested change
-  const multiPassEnabled = (multiPass || 0) > 1;
+  const defaultSupportsMultiPass = ['testlib', 'kattis'].includes(checkerType || '');
+  const supportsMultiPass = Type === 'interactive' || (Type === 'default' && defaultSupportsMultiPass);
+  const multiPassEnabled = supportsMultiPass && (multiPass || 0) > 1;
@@
-          checked={multiPassEnabled}
+          checked={multiPassEnabled}
+          disabled={!supportsMultiPass}
@@
-          onChange={() => dispatch({
+          onChange={() => supportsMultiPass && dispatch({
             type: 'CONFIG_FORM_UPDATE',
             key: 'multi_pass',
             value: multiPassEnabled ? 0 : 2,
-          })}
+          })}
@@
-            {Type === 'default' && (
+            {Type === 'default' && (
               <div className="row">
                 {multiPassFields}
               </div>
             )}

Also applies to: 147-151

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ui-default/components/problemconfig/ProblemType.tsx` around lines 21
- 52, The multi-pass controls are currently shown for default problems
regardless of checker_type; update the UI to only allow multi-pass for
compatible checkers (testlib or kattis) by checking the form's checker_type
before rendering or enabling controls. Concretely, in the component that defines
multiPassFields use the existing multiPassEnabled and the checker_type value
(from props/state) to change the conditions and behavior: only render the
max-passes FormItem when multiPassEnabled && (checker_type === 'testlib' ||
checker_type === 'kattis'), and disable or no-op the Switch onChange when
checker_type is not one of those values (still use dispatch with type
'CONFIG_FORM_UPDATE' and key 'multi_pass' when valid). Ensure you reference
multiPassFields, multiPassEnabled, checker_type, dispatch and the
'CONFIG_FORM_UPDATE' action so the invalid combinations cannot be set in the UI.
packages/ui-default/components/monaco/schema/problemconfig.ts (1)

83-83: ⚡ Quick win

Constrain multi_pass to integer values in schema.

Using type: 'number' allows decimals in editor validation. This should be integer to match pass-count semantics.

💡 Suggested fix
-    multi_pass: { type: 'number', minimum: 0, maximum: 10 },
+    multi_pass: { type: 'integer', minimum: 0, maximum: 10 },
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ui-default/components/monaco/schema/problemconfig.ts` at line 83,
The schema currently defines multi_pass with type: 'number', which allows
decimals; update the multi_pass property to use type: 'integer' (keeping
minimum: 0 and maximum: 10) so editor validation enforces whole pass
counts—locate the multi_pass entry in problemconfig.ts and change the type to
'integer'.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/hydrojudge/src/cases.ts`:
- Around line 35-37: The error message in the interactive validation check is
missing a space; update the thrown FormatError in the block that checks
config.type === 'interactive' and !config.interactor to use a corrected
user-facing string, e.g. "Interactive problems require an interactor." so
FormatError contains the fixed spacing and reads clearly to users.
- Around line 38-40: The validation for config.multi_pass currently only checks
range but allows fractional values; update the check where config.multi_pass is
validated (referencing config.multi_pass and the thrown FormatError) to also
require Number.isInteger(config.multi_pass) so non-integer values (e.g., 2.5)
trigger the same FormatError message "Multi Pass must be between 0 and 10.";
ensure the combined condition rejects null/undefined appropriately and still
enforces 0 ≤ multi_pass ≤ 10.

In `@packages/hydrojudge/src/judge/default.ts`:
- Around line 33-35: When multiPass is active, route the per-pass state
(mp.stdin, mp.copyIn, mp.input) to the checker instead of mounting it into the
user sandbox: change the places that build the user process execution object
(the fields stdin and copyIn built from multiPass ? mp.stdin : ... and multiPass
? { ...ctx.execute.copyIn, ...mp.copyIn } : ctx.execute.copyIn) so the user
always gets the original run inputs (c.input and ctx.execute.copyIn), while the
checker invocation receives mp.stdin, mp.copyIn merged with ctx.execute.copyIn,
and uses mp.input (not c.input) for later passes; update all occurrences that
construct the checker call (where c.input and original copyIn are passed) to use
mp.* when multiPass is true.

In `@packages/hydrojudge/src/judge/interactive.ts`:
- Line 35: The current multi-pass logic passes mp.copyIn into the user sandbox
(copyIn: multiPass && mp.i ? { ...ctx.executeUser.copyIn, ...mp.copyIn } :
ctx.executeUser.copyIn), which leaks the interactor's state.txt; change the
merge so state.txt is excluded before supplying mp.copyIn to the user process
(e.g., create a filtered copy of mp.copyIn that removes any entry for
"state.txt" or only includes allowed files), and use that filtered object in
place of mp.copyIn when computing copyIn; update all similar sites (the other
copyIn usage at the same pattern and where mp.copyIn is constructed) so
mp.copyIn never exposes state.txt to the user sandbox.

---

Nitpick comments:
In `@packages/ui-default/components/monaco/schema/problemconfig.ts`:
- Line 83: The schema currently defines multi_pass with type: 'number', which
allows decimals; update the multi_pass property to use type: 'integer' (keeping
minimum: 0 and maximum: 10) so editor validation enforces whole pass
counts—locate the multi_pass entry in problemconfig.ts and change the type to
'integer'.

In `@packages/ui-default/components/problemconfig/ProblemConfigEditor.tsx`:
- Line 53: The YAML formatter currently only checks key === 'multi_pass' and
numeric value (config.multi_pass) which can leave a stale multi_pass on problem
types that don't support it; update the guard in ProblemConfigEditor (the branch
handling key === 'multi_pass') to also check config.type and the problem's
checker compatibility before allowing multi_pass to persist—i.e., if config.type
is not a type that supports multi-pass or the selected checker is incompatible,
treat multi_pass as absent (skip or delete it) otherwise keep it when >1;
reference the existing key, config.multi_pass, and config.type checks to locate
where to add the extra condition.

In `@packages/ui-default/components/problemconfig/ProblemType.tsx`:
- Around line 21-52: The multi-pass controls are currently shown for default
problems regardless of checker_type; update the UI to only allow multi-pass for
compatible checkers (testlib or kattis) by checking the form's checker_type
before rendering or enabling controls. Concretely, in the component that defines
multiPassFields use the existing multiPassEnabled and the checker_type value
(from props/state) to change the conditions and behavior: only render the
max-passes FormItem when multiPassEnabled && (checker_type === 'testlib' ||
checker_type === 'kattis'), and disable or no-op the Switch onChange when
checker_type is not one of those values (still use dispatch with type
'CONFIG_FORM_UPDATE' and key 'multi_pass' when valid). Ensure you reference
multiPassFields, multiPassEnabled, checker_type, dispatch and the
'CONFIG_FORM_UPDATE' action so the invalid combinations cannot be set in the UI.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 505d376f-e590-4802-b326-2880b3060277

📥 Commits

Reviewing files that changed from the base of the PR and between ce14b98 and fcd2afb.

📒 Files selected for processing (10)
  • packages/common/types.ts
  • packages/hydrojudge/src/cases.ts
  • packages/hydrojudge/src/checkers.ts
  • packages/hydrojudge/src/judge/default.ts
  • packages/hydrojudge/src/judge/interactive.ts
  • packages/ui-default/components/monaco/schema/problemconfig.ts
  • packages/ui-default/components/problemconfig/ProblemConfigEditor.tsx
  • packages/ui-default/components/problemconfig/ProblemType.tsx
  • packages/ui-default/locales/en.yaml
  • packages/ui-default/locales/zh.yaml

Comment thread packages/hydrojudge/src/cases.ts
Comment thread packages/hydrojudge/src/cases.ts Outdated
Comment thread packages/hydrojudge/src/judge/default.ts Outdated
Comment thread packages/hydrojudge/src/judge/interactive.ts Outdated
@pandadtdyy
Copy link
Copy Markdown
Member Author

I have read the CLA Document and I hereby sign the CLA

Comment thread packages/hydrojudge/src/judge/interactive.ts Fixed
@pandadtdyy pandadtdyy marked this pull request as ready for review May 25, 2026 16:39
@pandadtdyy pandadtdyy requested a review from Copilot May 25, 2026 16:41
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/hydrojudge/src/cases.ts (1)

38-43: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Close the validation gap at multi_pass = 1.

The current logic allows multi_pass = 1 to pass validation (integer, within 0–10) without triggering the testlib/kattis checker requirement (line 41 checks > 1). However, the UI only exposes 0 (disabled) or 2–10 (enabled), making multi_pass = 1 an inaccessible and ambiguous state.

Tighten the validation to match the UI constraints: reject multi_pass = 1 or clarify whether it has distinct semantics.

Suggested fix
-    if (config.multi_pass && (!Number.isInteger(config.multi_pass) || config.multi_pass < 0 || config.multi_pass > 10)) {
-        throw new FormatError('Multi Pass must be between 0 and 10.');
+    if (config.multi_pass && (!Number.isInteger(config.multi_pass) || config.multi_pass < 0 || config.multi_pass === 1 || config.multi_pass > 10)) {
+        throw new FormatError('Multi Pass must be 0 or an integer between 2 and 10.');
     }
     if (config.multi_pass > 1 && config.type === 'default' && !['testlib', 'kattis'].includes(config.checker_type)) {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/hydrojudge/src/cases.ts` around lines 38 - 43, The validation
currently allows multi_pass = 1, which is unsupported by the UI; update the
validation around config.multi_pass in the conditional that throws FormatError
so that 1 is rejected (i.e., only allow 0 or 2–10). Modify the first if that
checks config.multi_pass to also treat 1 as invalid (or change the numeric range
check to exclude 1), keeping the subsequent checker requirement for multi_pass >
1 intact; reference config.multi_pass and the FormatError throw in this file to
locate and fix the logic.
🧹 Nitpick comments (1)
packages/ui-default/components/problemconfig/ProblemConfigForm.tsx (1)

50-53: ⚡ Quick win

Clarify toggle logic to avoid relying on stale closure.

The current code depends on enabled being stale when dispatch runs (since setEnabled is async). Capture the new value explicitly to make the intent clear and reduce fragility.

Refactor for clarity
               onChange={() => {
-                setEnabled(!enabled);
-                dispatch({ type: 'CONFIG_FORM_UPDATE', key: 'multi_pass', value: enabled ? 0 : 2 });
+                const newEnabled = !enabled;
+                setEnabled(newEnabled);
+                dispatch({ type: 'CONFIG_FORM_UPDATE', key: 'multi_pass', value: newEnabled ? 2 : 0 });
               }}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ui-default/components/problemconfig/ProblemConfigForm.tsx` around
lines 50 - 53, The toggle handler relies on the old enabled value due to
setEnabled being async; capture the new boolean first (e.g., newEnabled =
!enabled), call setEnabled(newEnabled), then dispatch({ type:
'CONFIG_FORM_UPDATE', key: 'multi_pass', value: newEnabled ? 2 : 0 }) so the
dispatched value uses the explicit new state instead of the possibly stale
enabled closure; update the handler around setEnabled/dispatch in
ProblemConfigForm.tsx (references: setEnabled, enabled, dispatch,
CONFIG_FORM_UPDATE, key 'multi_pass').
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/hydrojudge/src/judge/default.ts`:
- Around line 67-72: The multi-pass path mutates mp and calls runner!(...) but
the TLE retry branch does not forward the recursive callback, so retries can
resume with runner === undefined and crash; update the TLE retry code to accept
and pass the same runner callback through (preserve runner when creating
ctxSubtask/retrying and when re-invoking the judge/rerun logic) so that when
nextPass is returned the subsequent call uses the original runner (refer to
symbols mp, nextPass, runner, ctxSubtask and the retry/TLE branch in
default.ts).

In `@packages/ui-default/components/problemconfig/ProblemConfigForm.tsx`:
- Line 57: The FormItem in ProblemConfigForm is passing a translated string via
i18n('Max passes') which causes double translation because FormItem (see
BasicForm's label handling) already wraps the label with i18n; fix this by
removing the i18n call and pass the raw label key directly (e.g., change
label={i18n('Max passes')} to label={'Max passes'} or the project's canonical
raw key) on the FormItem component so only one translation is applied.

---

Outside diff comments:
In `@packages/hydrojudge/src/cases.ts`:
- Around line 38-43: The validation currently allows multi_pass = 1, which is
unsupported by the UI; update the validation around config.multi_pass in the
conditional that throws FormatError so that 1 is rejected (i.e., only allow 0 or
2–10). Modify the first if that checks config.multi_pass to also treat 1 as
invalid (or change the numeric range check to exclude 1), keeping the subsequent
checker requirement for multi_pass > 1 intact; reference config.multi_pass and
the FormatError throw in this file to locate and fix the logic.

---

Nitpick comments:
In `@packages/ui-default/components/problemconfig/ProblemConfigForm.tsx`:
- Around line 50-53: The toggle handler relies on the old enabled value due to
setEnabled being async; capture the new boolean first (e.g., newEnabled =
!enabled), call setEnabled(newEnabled), then dispatch({ type:
'CONFIG_FORM_UPDATE', key: 'multi_pass', value: newEnabled ? 2 : 0 }) so the
dispatched value uses the explicit new state instead of the possibly stale
enabled closure; update the handler around setEnabled/dispatch in
ProblemConfigForm.tsx (references: setEnabled, enabled, dispatch,
CONFIG_FORM_UPDATE, key 'multi_pass').
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 2e9f84f2-d496-4d34-8268-ed77fd459393

📥 Commits

Reviewing files that changed from the base of the PR and between fcd2afb and 6dd366c.

📒 Files selected for processing (9)
  • packages/hydrojudge/src/cases.ts
  • packages/hydrojudge/src/checkers.ts
  • packages/hydrojudge/src/judge/default.ts
  • packages/hydrojudge/src/judge/interactive.ts
  • packages/hydrojudge/src/judge/interface.ts
  • packages/ui-default/components/monaco/schema/problemconfig.ts
  • packages/ui-default/components/problemconfig/ProblemConfigEditor.tsx
  • packages/ui-default/components/problemconfig/ProblemConfigForm.tsx
  • packages/ui-default/locales/zh.yaml

Comment thread packages/hydrojudge/src/judge/default.ts Outdated
Comment thread packages/ui-default/components/problemconfig/ProblemConfigForm.tsx Outdated
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 10 out of 10 changed files in this pull request and generated 6 comments.

Comment thread packages/ui-default/components/problemconfig/ProblemConfigForm.tsx
Comment thread packages/hydrojudge/src/judge/interactive.ts Outdated
Comment thread packages/hydrojudge/src/judge/interactive.ts Outdated
Comment thread packages/hydrojudge/src/cases.ts Outdated
Comment thread packages/ui-default/components/problemconfig/ProblemConfigForm.tsx
Comment thread packages/hydrojudge/src/judge/default.ts
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/hydrojudge/src/judge/default.ts`:
- Line 72: The code calls the optional parameter runner without guarding (return
await runner(ctx, ctxSubtask, runner);) which can cause a TypeError; update the
call site in default.ts to either (a) check that runner is defined before
invoking it (e.g., if (!runner) handle/throw or return a sensible fallback) or
(b) if the architecture guarantees runner is always present for multi-pass
problems, use a non-null assertion to call runner! to match interactive.ts;
reference the symbol runner and the recursive call in default.ts when making the
change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: a8d73608-a49d-44b3-a0f6-3b13d96f4e4e

📥 Commits

Reviewing files that changed from the base of the PR and between 6dd366c and 8fbbc3b.

📒 Files selected for processing (4)
  • packages/hydrojudge/src/cases.ts
  • packages/hydrojudge/src/judge/default.ts
  • packages/hydrojudge/src/judge/interactive.ts
  • packages/ui-default/components/problemconfig/ProblemConfigForm.tsx

mp.input = nextPass.input;
mp.state = nextPass.state ?? undefined;
mp.i++;
return await runner(ctx, ctxSubtask, runner);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Missing null guard on runner before recursive call.

runner is typed as optional (runner?: Function), but line 72 calls it without checking. Line 78 correctly guards with if (runner && ...). If nextPass is present but the caller didn't supply runner, this will throw a TypeError at runtime.

Proposed fix
         if (nextPass) {
             if (mp.i < ctx.config.multi_pass) {
                 mp.input = nextPass.input;
                 mp.state = nextPass.state ?? undefined;
                 mp.i++;
+                if (!runner) {
+                    return {
+                        id: c.id,
+                        subtaskId: ctxSubtask.subtask.id,
+                        status: STATUS.STATUS_SYSTEM_ERROR,
+                        score: 0,
+                        time,
+                        memory,
+                        message: 'Multi-pass requires runner callback',
+                    };
+                }
                 return await runner(ctx, ctxSubtask, runner);

Alternatively, use the non-null assertion (runner!) to match interactive.ts if the architecture guarantees runner is always provided for multi-pass problems.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return await runner(ctx, ctxSubtask, runner);
if (nextPass) {
if (mp.i < ctx.config.multi_pass) {
mp.input = nextPass.input;
mp.state = nextPass.state ?? undefined;
mp.i++;
if (!runner) {
return {
id: c.id,
subtaskId: ctxSubtask.subtask.id,
status: STATUS.STATUS_SYSTEM_ERROR,
score: 0,
time,
memory,
message: 'Multi-pass requires runner callback',
};
}
return await runner(ctx, ctxSubtask, runner);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/hydrojudge/src/judge/default.ts` at line 72, The code calls the
optional parameter runner without guarding (return await runner(ctx, ctxSubtask,
runner);) which can cause a TypeError; update the call site in default.ts to
either (a) check that runner is defined before invoking it (e.g., if (!runner)
handle/throw or return a sensible fallback) or (b) if the architecture
guarantees runner is always present for multi-pass problems, use a non-null
assertion to call runner! to match interactive.ts; reference the symbol runner
and the recursive call in default.ts when making the change.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

♻️ Duplicate comments (3)
packages/hydrojudge/src/cases.ts (1)

38-39: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Reject falsy invalid multi_pass values in backend validation.

The truthy guard allows invalid provided values like 0 to bypass this check. Validate on presence (!= null) instead, so every provided value must satisfy integer + range constraints.

💡 Suggested fix
-    if (config.multi_pass && (!Number.isInteger(config.multi_pass) || config.multi_pass < 2 || config.multi_pass > 20)) {
+    if (config.multi_pass != null && (!Number.isInteger(config.multi_pass) || config.multi_pass < 2 || config.multi_pass > 20)) {
         throw new FormatError('Multi Pass must be between 2 and 20.');
     }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/hydrojudge/src/cases.ts` around lines 38 - 39, The current
validation uses a truthy guard on config.multi_pass which lets falsy but
provided values (e.g., 0) bypass checks; change the conditional to check
presence with != null (or !== undefined) and then enforce
Number.isInteger(config.multi_pass) and the range 2..20, throwing
FormatError('Multi Pass must be between 2 and 20.') when the provided value is
invalid; update the validation around config.multi_pass in the same block where
FormatError is thrown to ensure any provided value fails unless it is an integer
within the allowed range.
packages/hydrojudge/src/judge/default.ts (1)

74-74: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Add null assertion or guard on runner before recursive call.

runner is typed as optional (runner?: Function), but line 74 calls it directly without a check. If nextPass is present but the caller didn't supply runner, this will throw TypeError at runtime. The equivalent call in interactive.ts uses runner! (non-null assertion).

For consistency and to document the assumption that runner is always provided for multi-pass problems, use the non-null assertion:

-                return await runner(ctx, ctxSubtask, runner);
+                return await runner!(ctx, ctxSubtask, runner);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/hydrojudge/src/judge/default.ts` at line 74, The call to runner in
default.ts (return await runner(ctx, ctxSubtask, runner)) uses the optional
parameter runner?: Function without a null check; change it to use a non-null
assertion (runner!) like in interactive.ts so the recursive call becomes return
await runner!(ctx, ctxSubtask, runner) — this documents the invariant that a
runner must be supplied for multi-pass problems and avoids a potential TypeError
at runtime.
packages/hydrojudge/src/judge/interactive.ts (1)

19-19: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Avoid leaking interactor state to user sandbox.

Line 19 merges mp.state into the user process copyIn. Since mp.state contains state.txt (from lines 69-71), this mounts the interactor's internal state file into the user sandbox across passes. This could leak privileged interactor information to submissions.

The user process should not receive mp.state; only the interactor needs it:

-                copyIn: { ...ctx.executeUser.copyIn, ...mp.state },
+                copyIn: ctx.executeUser.copyIn,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/hydrojudge/src/judge/interactive.ts` at line 19, The current merge
of mp.state into the user process copyIn leaks interactor internals
(mp.state/state.txt) into the sandbox; remove mp.state from being merged into
ctx.executeUser.copyIn and instead ensure mp.state is only passed to the
interactor process (e.g., merge mp.state into the interactor's copyIn/copyOut or
the data structure used by the interactor runner); update the code around the
identifiers copyIn, ctx.executeUser.copyIn and mp.state in interactive.ts so
that user execution receives only ctx.executeUser.copyIn (and other safe mounts)
while mp.state is kept private to the interactor.
🧹 Nitpick comments (1)
packages/ui-default/components/monaco/schema/problemconfig.ts (1)

83-83: ⚡ Quick win

Mirror backend multi-pass checker constraints in schema.

multi_pass range is correct, but the schema still misses the backend rule for default problems (checker_type must be testlib/kattis when multi-pass is set). Adding an if/then constraint keeps UI and server validation aligned.

💡 Suggested schema addition
 const problemConfigSchema: JSONSchema7 = {
   type: 'object',
+  allOf: [
+    {
+      if: {
+        properties: {
+          type: { const: 'default' },
+          multi_pass: { type: 'integer', minimum: 2 },
+        },
+        required: ['type', 'multi_pass'],
+      },
+      then: {
+        properties: {
+          checker_type: { enum: ['testlib', 'kattis'] },
+        },
+        required: ['checker_type'],
+      },
+    },
+  ],
   definitions: {
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/ui-default/components/monaco/schema/problemconfig.ts` at line 83,
Add a JSON Schema conditional to enforce that when multi_pass is present/greater
than 1 the checker_type must be one of the backend-allowed values for default
problems (i.e., "testlib" or "kattis"); modify the schema around the multi_pass
entry to include an if: { required: ["multi_pass"] } (or if: { properties: {
multi_pass: { minimum: 2 } } }) and then: { required: ["checker_type"],
properties: { checker_type: { enum: ["testlib","kattis"] } } } so the UI
validation for multi_pass aligns with the backend rule that checker_type must be
testlib/kattis.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Duplicate comments:
In `@packages/hydrojudge/src/cases.ts`:
- Around line 38-39: The current validation uses a truthy guard on
config.multi_pass which lets falsy but provided values (e.g., 0) bypass checks;
change the conditional to check presence with != null (or !== undefined) and
then enforce Number.isInteger(config.multi_pass) and the range 2..20, throwing
FormatError('Multi Pass must be between 2 and 20.') when the provided value is
invalid; update the validation around config.multi_pass in the same block where
FormatError is thrown to ensure any provided value fails unless it is an integer
within the allowed range.

In `@packages/hydrojudge/src/judge/default.ts`:
- Line 74: The call to runner in default.ts (return await runner(ctx,
ctxSubtask, runner)) uses the optional parameter runner?: Function without a
null check; change it to use a non-null assertion (runner!) like in
interactive.ts so the recursive call becomes return await runner!(ctx,
ctxSubtask, runner) — this documents the invariant that a runner must be
supplied for multi-pass problems and avoids a potential TypeError at runtime.

In `@packages/hydrojudge/src/judge/interactive.ts`:
- Line 19: The current merge of mp.state into the user process copyIn leaks
interactor internals (mp.state/state.txt) into the sandbox; remove mp.state from
being merged into ctx.executeUser.copyIn and instead ensure mp.state is only
passed to the interactor process (e.g., merge mp.state into the interactor's
copyIn/copyOut or the data structure used by the interactor runner); update the
code around the identifiers copyIn, ctx.executeUser.copyIn and mp.state in
interactive.ts so that user execution receives only ctx.executeUser.copyIn (and
other safe mounts) while mp.state is kept private to the interactor.

---

Nitpick comments:
In `@packages/ui-default/components/monaco/schema/problemconfig.ts`:
- Line 83: Add a JSON Schema conditional to enforce that when multi_pass is
present/greater than 1 the checker_type must be one of the backend-allowed
values for default problems (i.e., "testlib" or "kattis"); modify the schema
around the multi_pass entry to include an if: { required: ["multi_pass"] } (or
if: { properties: { multi_pass: { minimum: 2 } } }) and then: { required:
["checker_type"], properties: { checker_type: { enum: ["testlib","kattis"] } } }
so the UI validation for multi_pass aligns with the backend rule that
checker_type must be testlib/kattis.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 56cb3a6e-8506-40b7-bba6-48a221047631

📥 Commits

Reviewing files that changed from the base of the PR and between 8fbbc3b and c804f38.

📒 Files selected for processing (4)
  • packages/hydrojudge/src/cases.ts
  • packages/hydrojudge/src/judge/default.ts
  • packages/hydrojudge/src/judge/interactive.ts
  • packages/ui-default/components/monaco/schema/problemconfig.ts

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
packages/hydrojudge/src/judge/default.ts (1)

65-68: 💤 Low value

Signal message loses pass prefix.

When a signal is caught (line 66), the message is overwritten with signals[code], losing the [Pass N] prefix. The non-signal branch (line 67) preserves the prefix. Consider prepending the pass info for consistency.

Suggested fix
-        if (code < 32 && signalled) message = signals[code];
+        if (code < 32 && signalled) message = `${message}${signals[code]}`;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/hydrojudge/src/judge/default.ts` around lines 65 - 68, The
runtime-error branch for STATUS.STATUS_RUNTIME_ERROR currently overwrites
message with signals[code] when signalled, losing the original "[Pass N]"
prefix; update the signalled branch in the same conditional (where variables
code, signalled, message, and signals are used and ctx.config.detail === 'full')
to preserve the pass prefix by prepending the existing message/prefix to
signals[code] (or by constructing an object similar to the non-signalled branch
that combines the prefix and the signal text), so the final message retains the
pass info plus the signal description.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@packages/hydrojudge/src/judge/default.ts`:
- Around line 65-68: The runtime-error branch for STATUS.STATUS_RUNTIME_ERROR
currently overwrites message with signals[code] when signalled, losing the
original "[Pass N]" prefix; update the signalled branch in the same conditional
(where variables code, signalled, message, and signals are used and
ctx.config.detail === 'full') to preserve the pass prefix by prepending the
existing message/prefix to signals[code] (or by constructing an object similar
to the non-signalled branch that combines the prefix and the signal text), so
the final message retains the pass info plus the signal description.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e14d2da1-64ec-40cd-8972-9cd321d26d49

📥 Commits

Reviewing files that changed from the base of the PR and between c804f38 and a5e4a68.

📒 Files selected for processing (1)
  • packages/hydrojudge/src/judge/default.ts

@pandadtdyy pandadtdyy requested a review from undefined-moe May 25, 2026 19:46
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.

3 participants