Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
18 changes: 18 additions & 0 deletions docs/reference/benchmark-campaign-report.md
Original file line number Diff line number Diff line change
Expand Up @@ -203,3 +203,21 @@ run enters `needs_makeup`, even with a valid score or a successful process exit.
Only a matching completed run with an audited normal end and a satisfied Goal
can use the early-finish allowance. Incomplete and unverified durations exclude a
case from the duration-qualified comparison. Retain raw results for diagnosis.

An operator can explicitly set `duration_policy.makeup_tolerance_ms` when small
terminal shortfalls do not warrant another run. It defaults to zero and must be
a nonnegative safe integer smaller than `target_ms`. For example, a tolerance of
`900000` waives at most fifteen minutes. After unioning the verified active
intervals, a terminal run with a positive observed duration and a remaining gap
at or below that tolerance is marked `within_tolerance`, rather than `covered`.
The report retains the actual `active_ms`, `remaining_ms` and selected policy.
Unknown duration still needs verification; short planned/running rows retain
their status, and no runtime deadline or stopping rule changes.

`within_tolerance` satisfies only the report's declared duration condition. It
does not change run status, scores, integrity/countability, or matched-comparison
eligibility. A cancelled result stays out of the descriptive comparison, and an
uncountable score stays out of `qualified_common_n` even when its duration qualifies.
The Markdown report labels the waiver and its threshold. Callers should use this
same projection when selecting makeup work rather than rounding the raw duration
or inventing a second threshold.
18 changes: 15 additions & 3 deletions scripts/benchmark-campaign-report.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export type CampaignInput = {
observed_at: string; title: string; case_ids: string[];
arms: { arm_id: string; label: string }[];
selection: Selection[]; incidents: Incident[]; notes: string[];
duration_policy?: { target_ms: number; allow_goal_completion: string[] };
duration_policy?: { target_ms: number; allow_goal_completion: string[]; makeup_tolerance_ms?: number };
execution_audit?: ExecutionAudit[];
};

Expand Down Expand Up @@ -94,6 +94,9 @@ export function validateInput(raw: unknown): CampaignInput {
if (d.duration_policy !== undefined) {
const policy = d.duration_policy;
requireThat(policy && Number.isSafeInteger(policy.target_ms) && policy.target_ms > 0, 'positive active duration target required');
requireThat(policy.makeup_tolerance_ms === undefined ||
(Number.isSafeInteger(policy.makeup_tolerance_ms) && policy.makeup_tolerance_ms >= 0 &&
policy.makeup_tolerance_ms < policy.target_ms), 'makeup tolerance must be a nonnegative integer below target');
requireThat(Array.isArray(policy.allow_goal_completion) && distinct(policy.allow_goal_completion) &&
policy.allow_goal_completion.every(id => armIds.has(id)), 'unknown or duplicate early completion arm');
}
Expand Down Expand Up @@ -192,6 +195,7 @@ function durationCoverage(d: CampaignInput, policy: NonNullable<CampaignInput['d
else if (acceptedGoal) disposition = 'goal_completed';
else if (row.status === 'planned' || row.status === 'running') disposition = row.status;
else if (remaining_ms === null) disposition = 'verify_duration';
else if (active_ms !== null && active_ms > 0 && remaining_ms <= (policy.makeup_tolerance_ms ?? 0)) disposition = 'within_tolerance';
else disposition = 'needs_makeup';
return { case_id, arm_id, run_id, active_ms, remaining_ms, disposition };
});
Expand All @@ -200,7 +204,7 @@ function durationCoverage(d: CampaignInput, policy: NonNullable<CampaignInput['d
export function buildReport(raw: unknown) {
const d = validateInput(raw);
const coverage = d.duration_policy && durationCoverage(d, d.duration_policy);
const covered = new Set(coverage?.filter(row => ['covered', 'goal_completed'].includes(row.disposition)).map(key));
const covered = new Set(coverage?.filter(row => ['covered', 'goal_completed', 'within_tolerance'].includes(row.disposition)).map(key));
const selected = new Map(d.selection.map(r => [key(r), r]));
const summary = d.arms.map(a => {
const rows = d.selection.filter(r => r.arm_id === a.arm_id);
Expand Down Expand Up @@ -296,6 +300,11 @@ export function buildReport(raw: unknown) {
return { schema_version: 'benchmark_campaign_report_v1', input_sha256: digest(d),
observed_at: d.observed_at, title: d.title, summary, comparisons, selection: d.selection,
...(coverage ? { duration_coverage: coverage } : {}),
...(d.duration_policy?.makeup_tolerance_ms !== undefined ? { duration_policy: {
target_ms: d.duration_policy.target_ms,
allow_goal_completion: d.duration_policy.allow_goal_completion,
makeup_tolerance_ms: d.duration_policy.makeup_tolerance_ms,
} } : {}),
configuration_profiles, score_time_series, incidents: d.incidents,
execution_audit: d.execution_audit ?? [], notes: d.notes };
}
Expand Down Expand Up @@ -331,7 +340,10 @@ export function renderMarkdown(r: Report): string {
table(['实验组','运行数','Source','Scorer','Model','预算(h)','主动提交冷却(s)','后台评测(s)','资源档位','证据覆盖'],r.configuration_profiles.map(p => [labels.get(p.arm_id),p.run_count,known(p.source_revision_id),known(p.scorer_revision_id),known(p.model_id),hours(p.budget_ms),seconds(p.active_submission_cooldown_ms),seconds(p.background_evaluation_interval_ms),known(p.resource_profile),`${p.configuration_supplied ? p.run_count : 0}/${p.run_count}`])) +
'\n\n均分使用相同题目交集、相同归一化规则,尺度 0–100。未出分不填 0;授权的故障判零单独保留来源。以下为描述性比较;源版本、协议、资源差异和完整性复核仍需独立审查。\n\n' +
table(['对比范围','共同题数',...r.summary.map(a => a.label),'第二组−第一组','共同题中双方/各方可计数'],r.comparisons.map(c => [c.arms.map(a => labels.get(a)).join(' / '),c.n,...r.summary.map(a => number(c.means[a.arm_id] ?? null)),number(c.delta_second_minus_first),c.qualified_common_n])) +
(r.duration_coverage ? '\n\n## 各臂有效运行时间\n\n' + table(['题目','实验臂','已运行(小时)','距目标(小时)','状态'],
(r.duration_coverage ? '\n\n## 各臂有效运行时间\n\n' +
((r.duration_policy?.makeup_tolerance_ms ?? 0) > 0
? `已终止运行的补时容差:${r.duration_policy!.makeup_tolerance_ms! / 60000} 分钟。within_tolerance 表示容差内免补;实际时长和缺口保持原值,不提前停止正在运行的任务。\n\n` : '') +
table(['题目','实验臂','已运行(小时)','距目标(小时)','状态'],
r.duration_coverage.map(row => [row.case_id, labels.get(row.arm_id), hours(row.active_ms), hours(row.remaining_ms), row.disposition])) : '') +
'\n\n' + r.notes.map(n => '- ' + cell(n)).join('\n') + '\n\n## 共同题目与排除记录\n\n' +
r.comparisons.map(c => `- ${c.arms.map(a => labels.get(a)).join(' / ')}:${c.case_ids.join('、') || '无'}。`).join('\n') +
Expand Down
64 changes: 64 additions & 0 deletions tests/control_plane_ts/benchmark_campaign_report.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,3 +216,67 @@ test('duration policy checks every selected arm and only accepts verified volunt
input.selection[0].active_intervals[0].end_ms = -1;
assert.throws(() => buildReport(input),/observed solver interval/);
});

test('makeup tolerance waives only known terminal gaps, retaining actual duration and qualification gates', () => {
const input = fixture();
input.duration_policy = {target_ms: 100, allow_goal_completion: [], makeup_tolerance_ms: 15};
for (const [i, elapsed] of [85, 84, 100, 86, 0, 85].entries()) {
input.selection[i].active_intervals = [{start_ms: 0, end_ms: elapsed, evidence_ref: 'solver:segment'}];
}
// Overlap is not extra active time: 50 + 45 - 10 = 85.
input.selection[0].active_intervals = [[0,50],[40,85]].map(([start_ms,end_ms]) =>
({start_ms,end_ms,evidence_ref:'solver:segment'}));
let report = buildReport(input);
assert.deepEqual(report.duration_coverage!.map(row => row.disposition),
['within_tolerance', 'needs_makeup', 'covered', 'within_tolerance', 'needs_makeup', 'within_tolerance']);
assert.equal(report.duration_coverage![0].active_ms, 85);
assert.equal(report.duration_coverage![0].remaining_ms, 15);
assert.equal(report.comparisons[0].qualified_common_n, 2);
assert.deepEqual(report.duration_policy, input.duration_policy);
assert.match(renderMarkdown(report), /容差内免补/);
input.selection[0].score!.countable = false;
report = buildReport(input);
assert.equal(report.comparisons[0].qualified_common_n, 1);
input.selection[0].status = 'cancelled';
assert.equal(buildReport(input).comparisons[0].n, 2);
input.selection[0].status = 'running';
assert.equal(buildReport(input).duration_coverage![0].disposition, 'running');
input.selection[0].status = 'planned';
assert.equal(buildReport(input).duration_coverage![0].disposition, 'planned');
input.selection[0].status = 'completed';
delete input.selection[0].active_intervals;
assert.equal(buildReport(input).duration_coverage![0].disposition, 'verify_duration');
input.selection[0].active_intervals = [{start_ms:0,end_ms:85,evidence_ref:'solver:segment'}];
delete input.duration_policy.makeup_tolerance_ms;
assert.equal(buildReport(input).duration_coverage![0].disposition, 'needs_makeup');
});

test('duration tolerance rejects invalid policy and real CLI retains the declared waiver', () => {
const input = fixture();
input.duration_policy = {target_ms: 100, allow_goal_completion: []};
for (const bad of [-1, 100, 101, 1.5, NaN, Infinity, true, null]) {
input.duration_policy.makeup_tolerance_ms = bad as number;
assert.throws(() => buildReport(input), /makeup tolerance/);
}
input.duration_policy.makeup_tolerance_ms = 15;
(input.duration_policy as typeof input.duration_policy & {operator_annotation: string}).operator_annotation =
'must not enter report output';
input.selection[0].active_intervals = [{start_ms:0,end_ms:85,evidence_ref:'solver:segment'}];
const dir = mkdtempSync(join(tmpdir(), 'duration-tolerance-'));
try {
const path = join(dir, 'input.json'), prefix = join(dir, 'report');
writeFileSync(path, JSON.stringify(input));
const run = spawnSync(process.execPath, ['--experimental-strip-types', resolve('scripts/benchmark-campaign-report.ts'), path, prefix], {encoding:'utf8'});
assert.equal(run.status, 0, run.stderr);
const actual = JSON.parse(readFileSync(prefix + '.json', 'utf8'));
assert.equal(actual.duration_policy.makeup_tolerance_ms, 15);
assert.deepEqual(actual.duration_policy, {
target_ms: 100,
allow_goal_completion: [],
makeup_tolerance_ms: 15,
});
assert.equal(actual.duration_coverage[0].disposition, 'within_tolerance');
assert.equal(actual.duration_coverage[0].remaining_ms, 15);
assert.match(readFileSync(prefix + '.md', 'utf8'), /容差内免补/);
} finally { rmSync(dir, {recursive: true, force: true}); }
});
Loading