Skip to content
Open
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
44 changes: 44 additions & 0 deletions challenge-milestone-progress-monitor/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# Challenge Milestone Progress Monitor

This is a focused Scientific Bounty System slice for SCIBASE issue #18. It audits multi-phase challenge execution after intake and before final arbitration, producing deterministic packets that show whether solver teams are ready for review, need action, or should be escalated.

## Scope

- Tracks required milestone evidence for each solver team.
- Detects overdue sponsor feedback against a configured SLA.
- Flags idle solver activity before a milestone goes stale.
- Detects expired resubmission windows.
- Checks reviewer packet completeness before routing to reviewers.
- Emits stable audit digests for sponsor and arbitration records.

It intentionally does not implement bounty intake, rubric scoring, anti-collusion, payout settlement, appeals, sponsor reliability, amendment consent, reviewer consensus, or IP redaction. Those slices are already represented by other submissions on the bounty thread.

## Run

```powershell
node challenge-milestone-progress-monitor/test.js
node challenge-milestone-progress-monitor/demo.js
```

The demo writes:

- `challenge-milestone-progress-monitor/demo-output/audit-packets.json`
- `challenge-milestone-progress-monitor/demo-output/demo.svg`

This PR also includes the required short MP4 demo artifact:

- `challenge-milestone-progress-monitor/demo-output/demo.mp4`

## API

```js
const {
auditChallengeMilestones,
buildProgressSummary,
createAuditDigest,
} = require("./challenge-milestone-progress-monitor");

const audit = auditChallengeMilestones({ challenge, submissions, generatedAt });
```

`auditChallengeMilestones` returns a sponsor-facing audit containing packet decisions, flags, concrete solver/sponsor/reviewer actions, summary counts, and deterministic packet digests.
27 changes: 27 additions & 0 deletions challenge-milestone-progress-monitor/acceptance-notes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Acceptance Notes

## What This Adds

- Dependency-free Node.js module under `challenge-milestone-progress-monitor/`.
- Deterministic milestone audit packets for scientific challenge teams.
- Tests for stalled packets, complete packets, sponsor-facing summary counts, and stable audit digests.
- Demo JSON, SVG, and MP4 artifacts for the bounty review requirement.

## Verification

Use these commands from the repository root:

```powershell
node challenge-milestone-progress-monitor/test.js
node challenge-milestone-progress-monitor/demo.js
node --check challenge-milestone-progress-monitor/index.js
node --check challenge-milestone-progress-monitor/test.js
node --check challenge-milestone-progress-monitor/demo.js
node --check challenge-milestone-progress-monitor/sample-data.js
ffprobe -v error -show_entries format=duration,size -show_entries stream=codec_name,width,height -of default=noprint_wrappers=1 challenge-milestone-progress-monitor/demo-output/demo.mp4
git diff --check
```

## AI Assistance Disclosure

This contribution was prepared with AI assistance from OpenAI Codex and reviewed through local deterministic tests and artifact checks before submission.
141 changes: 141 additions & 0 deletions challenge-milestone-progress-monitor/demo-output/audit-packets.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
{
"generatedAt": "2026-05-20T12:00:00.000Z",
"challengeId": "SCI-BIO-42",
"title": "Biomarker discovery challenge",
"packets": [
{
"teamId": "team-aurora",
"teamName": "Aurora Lab",
"milestoneId": "prototype",
"milestoneLabel": "Prototype package",
"generatedAt": "2026-05-20T12:00:00.000Z",
"decision": "hold_for_escalation",
"flags": [
"MISSING_MILESTONE_EVIDENCE",
"SPONSOR_FEEDBACK_OVERDUE",
"SOLVER_IDLE_RISK",
"RESUBMISSION_WINDOW_EXPIRED",
"REVIEW_PACKET_INCOMPLETE"
],
"riskScore": 13,
"deadlineState": {
"milestoneDueAt": "2026-05-18T12:00:00.000Z",
"feedbackSlaHours": 48,
"idleHours": 152,
"resubmissionHours": 72
},
"missingEvidence": [
"validation-report",
"artifact-manifest"
],
"overdueFeedbackRequests": [
{
"milestoneId": "prototype",
"requestedAt": "2026-05-17T06:00:00.000Z",
"hoursOpen": 78,
"slaHours": 48
}
],
"expiredResubmissionWindows": [
{
"milestoneId": "prototype",
"openedAt": "2026-05-16T00:00:00.000Z",
"hoursOpen": 108,
"resubmissionHours": 72
}
],
"missingReviewerPacketItems": [
"artifact-manifest",
"reproduction-notes"
],
"solverActions": [
"Submit validation-report, artifact-manifest evidence before milestone review",
"Post a solver progress update or request a scoped extension"
],
"sponsorActions": [
"Resolve overdue sponsor feedback for Prototype package before arbitration",
"Decide whether to close or extend Prototype package resubmission window"
],
"reviewerTasks": [
"Add artifact-manifest, reproduction-notes to reviewer packet before review routing"
],
"auditDigest": "cmpm_878fb4e07adaa84b4cae08c1"
},
{
"teamId": "team-nova",
"teamName": "Nova Institute",
"milestoneId": "prototype",
"milestoneLabel": "Prototype package",
"generatedAt": "2026-05-20T12:00:00.000Z",
"decision": "ready_for_review",
"flags": [],
"riskScore": 0,
"deadlineState": {
"milestoneDueAt": "2026-05-18T12:00:00.000Z",
"feedbackSlaHours": 48,
"idleHours": 4,
"resubmissionHours": 72
},
"missingEvidence": [],
"overdueFeedbackRequests": [],
"expiredResubmissionWindows": [],
"missingReviewerPacketItems": [],
"solverActions": [],
"sponsorActions": [],
"reviewerTasks": [
"Route complete milestone packet to reviewers"
],
"auditDigest": "cmpm_8d4e2d1f634b250de91e464d"
},
{
"teamId": "team-quasar",
"teamName": "Quasar Students",
"milestoneId": "final",
"milestoneLabel": "Final replication packet",
"generatedAt": "2026-05-20T12:00:00.000Z",
"decision": "needs_solver_action",
"flags": [
"MISSING_MILESTONE_EVIDENCE",
"REVIEW_PACKET_INCOMPLETE"
],
"riskScore": 5,
"deadlineState": {
"milestoneDueAt": "2026-06-10T12:00:00.000Z",
"feedbackSlaHours": 48,
"idleHours": 18,
"resubmissionHours": 96
},
"missingEvidence": [
"independent-replication"
],
"overdueFeedbackRequests": [],
"expiredResubmissionWindows": [],
"missingReviewerPacketItems": [
"replication-log",
"sponsor-feedback"
],
"solverActions": [
"Submit independent-replication evidence before milestone review"
],
"sponsorActions": [],
"reviewerTasks": [
"Add replication-log, sponsor-feedback to reviewer packet before review routing"
],
"auditDigest": "cmpm_6ddb98999ede6078045762d1"
}
],
"summary": {
"counts": {
"totalTeams": 3,
"readyForReview": 1,
"needsSolverAction": 1,
"escalations": 1,
"highRisk": 1
},
"nextActions": [
"Resolve 1 escalation packet before arbitration.",
"Request solver updates for 1 milestone packet.",
"Review 1 complete milestone packet."
]
}
}
Binary file not shown.
61 changes: 61 additions & 0 deletions challenge-milestone-progress-monitor/demo-output/demo.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
91 changes: 91 additions & 0 deletions challenge-milestone-progress-monitor/demo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
const fs = require("fs");
const path = require("path");

const { auditChallengeMilestones } = require("./index");
const { challenge, submissions } = require("./sample-data");

const generatedAt = "2026-05-20T12:00:00.000Z";
const outputDir = path.join(__dirname, "demo-output");

fs.mkdirSync(outputDir, { recursive: true });

const audit = auditChallengeMilestones({ challenge, submissions, generatedAt });
fs.writeFileSync(
path.join(outputDir, "audit-packets.json"),
`${JSON.stringify(audit, null, 2)}\n`
);
fs.writeFileSync(path.join(outputDir, "demo.svg"), buildSvg(audit));

console.log(`Challenge milestone progress monitor demo`);
console.log(`Challenge: ${audit.title}`);
console.log(`Teams audited: ${audit.summary.counts.totalTeams}`);
console.log(`Ready for review: ${audit.summary.counts.readyForReview}`);
console.log(`Escalations: ${audit.summary.counts.escalations}`);
console.log(`Needs solver action: ${audit.summary.counts.needsSolverAction}`);
console.log(`Wrote ${path.join(outputDir, "audit-packets.json")}`);
console.log(`Wrote ${path.join(outputDir, "demo.svg")}`);

function buildSvg(audit) {
const rows = audit.packets
.map((packet, index) => {
const y = 198 + index * 82;
const color = packet.decision === "ready_for_review"
? "#1f8a5b"
: packet.decision === "hold_for_escalation"
? "#b42318"
: "#ad6f00";
const flags = packet.flags.length === 0 ? "No flags" : packet.flags.join(" | ");
return `<g transform="translate(64 ${y})">
<rect width="832" height="60" rx="8" fill="#ffffff" stroke="#cfd8dc"/>
<circle cx="26" cy="30" r="10" fill="${color}"/>
<text x="48" y="25" class="team">${escapeXml(packet.teamName)}</text>
<text x="48" y="45" class="meta">${escapeXml(packet.milestoneLabel)} - ${escapeXml(formatDecision(packet.decision))}</text>
<text x="360" y="25" class="meta">Risk ${packet.riskScore}</text>
<text x="360" y="45" class="small">${escapeXml(flags)}</text>
<text x="742" y="36" class="digest">${escapeXml(packet.auditDigest)}</text>
</g>`;
})
.join("");

return `<svg xmlns="http://www.w3.org/2000/svg" width="960" height="520" viewBox="0 0 960 520" role="img" aria-label="Challenge milestone progress monitor demo">
<style>
.title { font: 700 28px Arial, sans-serif; fill: #102027; }
.subtitle { font: 400 15px Arial, sans-serif; fill: #455a64; }
.metric { font: 700 24px Arial, sans-serif; }
.label { font: 400 13px Arial, sans-serif; fill: #546e7a; }
.team { font: 700 16px Arial, sans-serif; fill: #102027; }
.meta { font: 400 13px Arial, sans-serif; fill: #455a64; }
.small { font: 400 11px Arial, sans-serif; fill: #607d8b; }
.digest { font: 400 10px Consolas, monospace; fill: #607d8b; }
</style>
<rect width="960" height="520" fill="#f4f7f9"/>
<text x="64" y="58" class="title">Challenge Milestone Progress Monitor</text>
<text x="64" y="84" class="subtitle">${escapeXml(audit.title)} - ${escapeXml(audit.generatedAt)}</text>
${metricCard(64, 112, "Teams", audit.summary.counts.totalTeams, "#0b5fff")}
${metricCard(252, 112, "Ready", audit.summary.counts.readyForReview, "#1f8a5b")}
${metricCard(440, 112, "Escalations", audit.summary.counts.escalations, "#b42318")}
${metricCard(628, 112, "Solver action", audit.summary.counts.needsSolverAction, "#ad6f00")}
${rows}
<text x="64" y="478" class="subtitle">${escapeXml(audit.summary.nextActions.join(" "))}</text>
</svg>`;
}

function metricCard(x, y, label, value, color) {
return `<g transform="translate(${x} ${y})">
<rect width="150" height="58" rx="8" fill="#ffffff" stroke="#cfd8dc"/>
<text x="18" y="25" class="metric" fill="${color}">${value}</text>
<text x="18" y="45" class="label">${escapeXml(label)}</text>
</g>`;
}

function formatDecision(decision) {
return decision.split("_").map((part) => part[0].toUpperCase() + part.slice(1)).join(" ");
}

function escapeXml(value) {
return String(value)
.replace(/&/g, "&amp;")
.replace(/</g, "&lt;")
.replace(/>/g, "&gt;")
.replace(/"/g, "&quot;");
}
Loading