feat(cli): send bucket info to SRS API and render due list from worker

This commit is contained in:
Prad Nukala
2026-08-31 10:49:50 -04:00
parent 1622bf39b7
commit a3657732f6
2 changed files with 182 additions and 144 deletions
+26 -25
View File
@@ -19,14 +19,12 @@
* SRS_ADMIN_KEY for the D1 push (unset = skip it); SRS_API overrides
* the Worker URL for local `wrangler dev` runs.
*/
import { basename, join } from "node:path";
import { join } from "node:path";
import { github } from "./github.ts";
import { markdownTable, printTable, writeStepSummary } from "./report.ts";
import { isImplemented } from "./source.ts";
const ROOT = join(import.meta.dir, "..", "..");
const WORK = join(ROOT, "work");
import { WORK, parseWorkPath, type Bucket } from "./work.ts";
const DRY =
process.argv.includes("--dry-run") ||
@@ -41,27 +39,27 @@ const gh = await github();
interface WorkEntry {
files: string[];
/** Any bucket implemented — this is what closes the problem issue. */
implemented: boolean;
/** Buckets holding a real solution: 1 = first solve, 3/7 = the re-solves. */
solved: Set<Bucket>;
}
/** LC number -> solution files (a problem may have both js and py). */
/** LC number -> its solution files across every bucket (js and py both count). */
const work = new Map<number, WorkEntry>();
for await (const rel of new Bun.Glob("**/*.{js,py}").scan(WORK)) {
const m = basename(rel).match(/^(\d+)\.(.+)\.(?:js|py)$/);
if (!m) {
console.warn(`skip (unrecognized name): work/${rel}`);
const file = parseWorkPath(rel);
if (!file) {
console.warn(`skip (off-layout): work/${rel}`);
continue;
}
const num = Number(m[1]);
const src = await Bun.file(join(WORK, rel)).text();
const implemented = isImplemented(src, rel.endsWith(".py") ? "py" : "js");
const entry = work.get(num);
if (entry) {
entry.files.push(`work/${rel}`);
entry.implemented ||= implemented;
} else {
work.set(num, { files: [`work/${rel}`], implemented });
}
const entry = work.get(file.lc) ?? { files: [], implemented: false, solved: new Set<Bucket>() };
entry.files.push(`work/${rel}`);
entry.implemented ||= implemented;
if (implemented) entry.solved.add(file.bucket);
work.set(file.lc, entry);
}
// ── open problem issues ──────────────────────────────────────────
@@ -157,14 +155,17 @@ for (const num of [...work.keys()].sort((a, b) => a - b)) {
// or the charts, the digest's "already solved" ticks, and the drill/gate
// pools all keep treating it as untouched.
//
// The whole implemented set goes over, not just this run's closes: the
// Worker drops anything already on the ladder, so the call is a total
// recompute and backfills whatever earlier runs missed.
// Each entry carries the BUCKET the file sits in, which is the rung it
// settles: work/1 is the first solve, work/3 the 3-day review, work/7 the
// 7-day one. That is what makes a pushed re-solve advance the ladder instead
// of vanishing — and the Worker writes only when a problem is standing on the
// rung named, so the whole implemented set can go over on every push (a total
// recompute that backfills whatever earlier runs missed) and re-runs write
// nothing.
const SRS_API = process.env.SRS_API ?? "https://srs-api.prdlk.workers.dev";
const solved = [...work.entries()]
.filter(([, entry]) => entry.implemented)
.map(([lc]) => lc)
.sort((a, b) => a - b);
.flatMap(([lc, entry]) => [...entry.solved].sort().map((bucket) => ({ lc, bucket })))
.sort((a, b) => a.lc - b.lc || a.bucket - b.bucket);
let syncNote: string;
let syncFailed = false;
@@ -180,7 +181,7 @@ if (!process.env.SRS_ADMIN_KEY) {
authorization: `Bearer ${process.env.SRS_ADMIN_KEY}`,
"content-type": "application/json",
},
body: JSON.stringify({ lc: solved }),
body: JSON.stringify({ solved }),
});
if (!res.ok) throw new Error(`${res.status} ${await res.text()}`);
// Narrow the report payload instead of casting it; a shape change should
@@ -209,7 +210,7 @@ printTable(rows);
const actionable = report.filter((r) => r[3] !== "no open issue");
console.log(
`\n${work.size} in work/ · ${solved.length} implemented · ` +
`\n${work.size} problems in work/ · ${solved.length} solved files across buckets · ` +
`${actionable.length} matched an open issue · ` +
`${DRY ? `would close ${actionable.filter((r) => r[3] === "would close").length}` : `closed ${closed}`}` +
`\nd1: ${syncNote}`,
@@ -217,7 +218,7 @@ console.log(
await writeStepSummary(
`### close-solved${DRY ? " (dry run)" : ""}\n\n` +
`${work.size} files in \`work/\`, ${solved.length} implemented, ` +
`${work.size} problems in \`work/\`, ${solved.length} solved files across buckets, ` +
`${actionable.length} matched an open issue.\n\n` +
(actionable.length
? `${markdownTable([rows[0]!, ...actionable])}\n`