diff --git a/apps/api/README.md b/apps/api/README.md index efd8210..c8bf37a 100644 --- a/apps/api/README.md +++ b/apps/api/README.md @@ -16,9 +16,13 @@ Live at `https://srs-api.prdlk.workers.dev`. never overwrites SRS-owned columns (`stage`, `next_review`). - **The repo owns the schedule** — `data/schedule.json`, human-edited, bundled at deploy. Git history is its audit log. -- The GitHub Project mirror (`Target Date`, `SRS Stage`, `First Attempt`) - is best-effort: failures log warnings, never block a D1 write. Topic rows - are never written. +- The GitHub Project mirror writes `Target Date` and nothing else: it is + best-effort, so failures log warnings and never block a D1 write, and topic + rows are never written. SRS stage and first-attempt result stay in D1 only — + the charts, digest and `/api/stats` read them from there, so mirroring them + into Project single-selects bought only drift. Do not re-add them. + `Set`/`Difficulty` are a projection of the issue labels, reconciled by + `bun run sync-project-fields` at the repo root, not by this Worker. ## The ladder diff --git a/apps/api/src/index.ts b/apps/api/src/index.ts index d8a4f55..ff5c3a4 100644 --- a/apps/api/src/index.ts +++ b/apps/api/src/index.ts @@ -30,22 +30,16 @@ import { buildStats } from "./stats.ts"; // ── shared side effects after a state write ────────────────────── -/** Project-field half of a mirror; shared by every write path. */ -async function mirrorFields(mirror: Mirror, outcome: LogOutcome): Promise { - await mirror.setStage(outcome.issue, outcome.stage); - await mirror.setTargetDate(outcome.issue, outcome.next_review); - if (outcome.first) await mirror.setFirstAttempt(outcome.issue, outcome.result); -} - /** - * Mirror one logged attempt into GitHub: Project fields always; on a + * Mirror one logged attempt into GitHub: the Project's Target Date always; on a * first-ever pass also close the problem's sub-issue (comment first — repo * convention). Runs inside ctx.waitUntil; failures are warnings. */ async function mirrorOutcome(env: Env, outcome: LogOutcome, source: string): Promise { if (outcome.error || outcome.duplicate) return; const gh = github(env.GH_PAT, env.REPO); - await mirrorFields(projectMirror(gh, env.REPO.split("/")[0]!), outcome); + const mirror = projectMirror(gh, env.REPO.split("/")[0]!); + await mirror.setTargetDate(outcome.issue, outcome.next_review); if (outcome.first && outcome.result === "pass") { try { await gh.closeIssue( @@ -248,7 +242,7 @@ async function handleSolved(request: Request, env: Env, date: string): Promise; -} - interface ProjectInfo { id: string; targetDate: string; - srsStage: SelectField; - firstAttempt: SelectField; } export interface Mirror { setTargetDate(issue: number, date: string | null): Promise; - setStage(issue: number, stage: string): Promise; - setFirstAttempt(issue: number, result: string): Promise; warnings: string[]; } @@ -47,7 +43,6 @@ export function projectMirror(gh: GitHub, owner: string): Mirror { fields(first: 30) { nodes { ... on ProjectV2FieldCommon { id name dataType } - ... on ProjectV2SingleSelectField { id name options { id name } } } } } @@ -62,7 +57,7 @@ export function projectMirror(gh: GitHub, owner: string): Mirror { id: string; title: string; fields: { - nodes: { id: string; name: string; options?: { id: string; name: string }[] }[]; + nodes: { id: string; name: string }[]; }; }[]; }; @@ -70,19 +65,9 @@ export function projectMirror(gh: GitHub, owner: string): Mirror { }; const node = data.user.projectsV2.nodes.find((n) => n.title === PROJECT_TITLE); if (!node) throw new Error(`project "${PROJECT_TITLE}" not found for @${owner}`); - const select = (name: string): SelectField => { - const f = node.fields.nodes.find((n) => n.name === name); - if (!f?.options) throw new Error(`single-select field "${name}" missing`); - return { id: f.id, options: Object.fromEntries(f.options.map((o) => [o.name, o.id])) }; - }; const date = node.fields.nodes.find((n) => n.name === "Target Date"); if (!date) throw new Error(`date field "Target Date" missing`); - info = { - id: node.id, - targetDate: date.id, - srsStage: select("SRS Stage"), - firstAttempt: select("First Attempt"), - }; + info = { id: node.id, targetDate: date.id }; return info; } @@ -154,19 +139,5 @@ export function projectMirror(gh: GitHub, owner: string): Mirror { await setField(issue, project.targetDate, { date }); } }), - setStage: (issue, stage) => - attempt(`SRS Stage #${issue}`, async () => { - const project = await resolve(); - const option = project.srsStage.options[stage]; - if (!option) throw new Error(`no option "${stage}"`); - await setField(issue, project.srsStage.id, { singleSelectOptionId: option }); - }), - setFirstAttempt: (issue, result) => - attempt(`First Attempt #${issue}`, async () => { - const project = await resolve(); - const option = project.firstAttempt.options[result]; - if (!option) throw new Error(`no option "${result}"`); - await setField(issue, project.firstAttempt.id, { singleSelectOptionId: option }); - }), }; }