From 288735319a7906c21248d72e2a55717f81fd788e Mon Sep 17 00:00:00 2001 From: Muse Sweep Date: Tue, 27 Oct 2026 05:27:21 +0000 Subject: [PATCH] fix(aggregator): program route 404 + researched project-page slices Closes #1186. Partial slices for #1124, #1125, #1126, #1128: cloud-program roadmap area, repo-fleet editorial refresh, id validation wording + source-doc links, lab-cluster status report + OCI gap. --- pm-stack/aggregator/README.md | 11 +- pm-stack/aggregator/src/project-config.ts | 135 +++++++++++++++++++--- pm-stack/aggregator/src/render.ts | 7 +- pm-stack/aggregator/src/server.ts | 5 +- pm-stack/aggregator/test/boot.test.ts | 11 ++ pm-stack/aggregator/test/program.test.ts | 82 ++++++++++++- 6 files changed, 223 insertions(+), 28 deletions(-) diff --git a/pm-stack/aggregator/README.md b/pm-stack/aggregator/README.md index 5a96db088..afa4888f5 100644 --- a/pm-stack/aggregator/README.md +++ b/pm-stack/aggregator/README.md @@ -93,8 +93,10 @@ project (target: under 30 minutes): `null` — the page then states honestly that it has no dedicated Area yet), `roadmapArea` (its Grist Program-roadmap area, or `null`), `homeDoc` (its `planning/roadmaps/.md` home, as - `{ repo, path, url }`), `dashboardLinks`, and `trackedRefs` (the - issues whose live state the page resolves). + `{ repo, path, url }`), an optional `statusReport` (`{ label, url }` + for the latest dated status report), `dashboardLinks` (dashboard + routes or https source-doc URLs, with an optional `note` caveat), + and `trackedRefs` (the issues whose live state the page resolves). 2. Optionally add `editorial` sections (`done` / `next` / `longTerm` / `gaps`, `ProgramEntry[]` — every entry should carry its tracker refs) and, for a program-level doc like pm-dashboard's, @@ -106,8 +108,9 @@ project (target: under 30 minutes): Validation is fail-soft at render and strict in tests: `validateProjectConfigs` (duplicate ids, duplicate pool/roadmap -areas, malformed slugs/refs, unusable home-doc URLs) is asserted -clean over the shipped registry in `test/program.test.ts`. +areas, malformed slugs/refs, unusable home-doc / status-report / +dashboard-link URLs) is asserted clean over the shipped registry in +`test/program.test.ts`. ### Dashboard comments (slice 3, #906) diff --git a/pm-stack/aggregator/src/project-config.ts b/pm-stack/aggregator/src/project-config.ts index 5734bc1ad..8454d9853 100644 --- a/pm-stack/aggregator/src/project-config.ts +++ b/pm-stack/aggregator/src/project-config.ts @@ -23,7 +23,15 @@ export interface ProjectHomeDoc { export interface ProjectDashboardLink { label: string; + /** A dashboard route ("/quota") or an absolute https URL (a source doc). */ route: string; + /** Optional caveat rendered beside the link (source-doc warnings). */ + note?: string; +} + +export interface ProjectStatusReport { + label: string; + url: string; } export interface ProjectEditorial { @@ -44,6 +52,8 @@ export interface ProjectConfig { /** Grist "Program roadmap" area value (RoadmapRow.area); null = none yet. */ roadmapArea: string | null; homeDoc?: ProjectHomeDoc; + /** Latest dated status report (spec layer 2), when one exists. */ + statusReport?: ProjectStatusReport; dashboardLinks?: ProjectDashboardLink[]; /** Issues whose live state the page resolves via Source.items(). */ trackedRefs: RefKey[]; @@ -55,6 +65,7 @@ export interface ProjectConfig { const CODE = "developing-today/code"; const NIX = "dezren39/nix"; const blob = (path: string): string => `https://github.com/${CODE}/blob/main/${path}`; +const tree = (path: string): string => `https://github.com/${CODE}/tree/main/${path}`; const home = (path: string): ProjectHomeDoc => ({ repo: CODE, path, url: blob(path) }); const ref = (repo: string, n: number): RefKey => `${repo}#${n}`; const code = (n: number): RefKey => ref(CODE, n); @@ -86,6 +97,10 @@ export const PROJECT_CONFIGS: ProjectConfig[] = [ poolArea: "lab-cluster", roadmapArea: "Basement cluster", homeDoc: home("planning/roadmaps/basement-cluster.md"), + statusReport: { + label: "Status report — 2026-10-27", + url: tree("doc/2026-10-27T03-14-08Z_status_basement-cluster"), + }, dashboardLinks: [{ label: "Projects pool (lab-cluster)", route: "/projects?area=lab-cluster" }], trackedRefs: [ code(1080), @@ -167,6 +182,12 @@ export const PROJECT_CONFIGS: ProjectConfig[] = [ detail: "Basement power and space block the full fleet; the target is 3–5 machines first. All current progress is design, inventory, and preparation.", }, + { + title: "OCI tenancy stuck in provisioning", + detail: + "The OCI tenancy has been stuck in provisioning since 2026-10-21 under Oracle ticket CPCOE-29381. That external blocker gates the cloud-extension epic until Oracle finishes provisioning or resolves the ticket.", + refs: [code(1091)], + }, { title: "ESP32 wing is deliberately an idea", detail: @@ -184,15 +205,43 @@ export const PROJECT_CONFIGS: ProjectConfig[] = [ poolArea: "id-train", roadmapArea: "id", homeDoc: home("planning/roadmaps/id.md"), - dashboardLinks: [{ label: "Projects pool (id-train)", route: "/projects?area=id-train" }], - trackedRefs: [code(534), code(796), code(1075), code(807), code(557), code(806), code(812), code(793)], + dashboardLinks: [ + { label: "Projects pool (id-train)", route: "/projects?area=id-train" }, + { + label: "pkgs/id/ARCHITECTURE.md", + route: blob("pkgs/id/ARCHITECTURE.md"), + note: "source caveat: it still frames id mainly as a file-sharing CLI; the worlds platform is barely covered", + }, + { label: "pkgs/id/README.md", route: blob("pkgs/id/README.md") }, + { + label: "pkgs/id/TODO.md", + route: blob("pkgs/id/TODO.md"), + note: "the file itself warns that it is idle planning — do not decide from it", + }, + ], + trackedRefs: [ + code(534), + code(796), + code(1075), + code(1076), + code(1077), + code(1078), + code(807), + code(557), + code(806), + code(812), + code(793), + code(1235), + code(1239), + code(1240), + ], editorial: { done: [ { - title: "Phases 9–11 delivered", + title: "Phases 9–11 delivered; validation in progress", detail: - "The worlds platform foundations shipped — faster than they were verified. Dedicated validation tasks now audit each delivered phase against its plan, with a tracked issue for every gap found.", - refs: [code(796), code(1075)], + "The home doc's careful wording is the status: Phases 9 and 10 were delivered faster than they were verified, and several Phase 11 items are claimed done on contradictory records. Dedicated validation audits (#1075–#1078, under umbrella #796) are in progress — delivered does not yet mean validated.", + refs: [code(796), code(1075), code(1076), code(1077), code(1078)], }, { title: "Phase 13 shells started", @@ -205,8 +254,8 @@ export const PROJECT_CONFIGS: ProjectConfig[] = [ { title: "The path to Phase 12 (Now)", detail: - "A CI signal that can be trusted, validation of the delivered phases, journal format management (must land before any new journal entry type), cost visibility baselines, and the Phase 12 designs run through the research workflow.", - refs: [code(229), code(1075), code(534)], + "A CI signal that can be trusted, validation of the delivered phases, journal format management (must land before any new journal entry type), cost visibility baselines, and the Phase 12 designs run through the research workflow. The ordered prerequisite queue is #1235.", + refs: [code(229), code(1075), code(534), code(1235)], }, { title: "Phase 12 — platform flexibility (Next)", @@ -252,7 +301,21 @@ export const PROJECT_CONFIGS: ProjectConfig[] = [ { label: "Needs you", route: "/attention" }, { label: "Projects pool (fleet-ops)", route: "/projects?area=fleet-ops" }, ], - trackedRefs: [code(911), code(899), code(287), code(288), code(257), code(292)], + trackedRefs: [ + code(911), + code(899), + code(287), + code(288), + code(1233), + code(1130), + code(1131), + code(1234), + code(1185), + code(1187), + code(1190), + code(310), + code(311), + ], editorial: { done: [ { @@ -261,6 +324,12 @@ export const PROJECT_CONFIGS: ProjectConfig[] = [ "Issue sweeps across all four repos, the hourly orchestration pass (queue triage, goal measurement, Project reconciliation), and the Jules keeper all run on schedule.", refs: [code(899)], }, + { + title: "Two earlier CI items are closed", + detail: + "The intermittent nix flake check out-of-disk failure (#257) and the roughly two-hour id workflow gate (#292) are closed in GitHub. They are recorded here as completed work, not presented as upcoming; keeping the id gate fast is now a standing measurement watch.", + refs: [code(257), code(292)], + }, ], next: [ { @@ -270,20 +339,38 @@ export const PROJECT_CONFIGS: ProjectConfig[] = [ refs: [code(911)], }, { - title: "CI hygiene (from the repo-and-ci roadmap)", + title: "CI hygiene and stale-PR triage (from the repo-and-ci roadmap)", + detail: + "Three Jules workflow files become one and the Go build cache is keyed by go.sum hash; in the same program, triage the overlapping Jules Bolt/Palette drafts and the stale Renovate/platform PRs instead of letting them age in place.", + refs: [code(287), code(288), code(310), code(311)], + }, + { + title: "Queue fleet reliability", + detail: + "Give every repo queue lane-style long-budget shifts, and certify shift completion from queue state rather than run self-report, so the fleet can run without operator repairs.", + refs: [code(1233), code(1130), code(1131)], + }, + { + title: "OpenChamber session dispatch + babysitter", detail: - "Three Jules workflow files become one; cache the Go build by go.sum hash; fix nix flake check dying out-of-disk in id-test/id-ci; triage stale PRs.", - refs: [code(287), code(288), code(257)], + "Run larger code tasks as OpenChamber sessions on plan pools, with admission control, hand-backs, stall nudges, and harvesting; one babysitter watches sessions, and the OpenClaw comment loop is the pilot for session control by issue comment.", + refs: [code(1234), code(1185), code(1187), code(1190)], }, ], longTerm: [ { - title: "The id workflow gate stays under 15 minutes", - detail: "The gate once took about two hours; it is fast now. Keep it that way — and measure it.", - refs: [code(292)], + title: "Fleet health judged from published state", + detail: + "The long-term direction is a fleet whose queue reliability, lifecycle labels, and sweep outcomes are visible from published queue and orchestration state — the same data this dashboard consumes — rather than from job self-reports.", + refs: [code(1233), code(1131)], }, ], gaps: [ + { + title: "The pool is a fleet-level slice, not the fleet backlog (2026-10-27)", + detail: + "As of 2026-10-27, this page's fleet-ops pool carries 4 items, while GitHub issue search counts 665 open issues across the four repos (developing-today/code 372, developing-today/opencode 51, developing-today/openchamber 64, dezren39/nix 178). Most work stays in each repo's own backlog; this page tracks the fleet-level program only.", + }, { title: "Home doc scope is CI/automation only", detail: @@ -357,7 +444,7 @@ export const PROJECT_CONFIGS: ProjectConfig[] = [ intro: "Every cloud account, plan, and subscription the fleet runs on — Cloudflare, Turso, OCI, the AI plans — and what each costs against its allotment. The Usage & billing hub renders the master tracker (workspace: ai-subscriptions) with dated reads and pace verdicts; the account-connection campaign wired API/OAuth access for nearly everything. The standing aim is ~100% of paid allotments, with pricing revalidated against official docs.", poolArea: null, - roadmapArea: null, + roadmapArea: "Cloud program", dashboardLinks: [ { label: "Usage & billing", route: "/quota" }, { label: "DNS register", route: "/dns" }, @@ -394,9 +481,9 @@ export const PROJECT_CONFIGS: ProjectConfig[] = [ ], gaps: [ { - title: "No Project Area or roadmap area", + title: "No Project Area yet", detail: - "This program is tracked in the ai-subscriptions workspace tracker and its hub epic, not in the org Project pool or the Grist roadmap — so this page's live pool/roadmap sections are honestly empty.", + "This program has a Grist roadmap area (its rows render in the live roadmap section above), but it is not in the org Project pool under its own Area yet — its work is tracked in the ai-subscriptions workspace tracker and its hub epic, so this page's live pool section is honestly empty.", }, { title: "Plaid / Citi still not linked", @@ -433,8 +520,9 @@ function checkEntries(entries: ProgramEntry[] | undefined, errors: string[], whe /** Validate a registry: returns human-readable errors (empty = valid). * Catches duplicate ids, duplicate pool/roadmap areas, malformed slugs - * and refs, and home docs without a usable URL. Pure — the shipped - * registry is asserted valid in tests. */ + * and refs, home docs / status reports without a usable URL, and + * dashboard links that are neither routes nor https URLs. Pure — the + * shipped registry is asserted valid in tests. */ export function validateProjectConfigs(configs: ProjectConfig[]): string[] { const errors: string[] = []; const seenIds = new Map(); @@ -457,6 +545,15 @@ export function validateProjectConfigs(configs: ProjectConfig[]): string[] { if (!c.title.trim()) errors.push(`${where}: title is empty`); if (!c.intro.trim()) errors.push(`${where}: intro is empty`); if (c.homeDoc && !/^https:\/\//.test(c.homeDoc.url)) errors.push(`${where}: homeDoc.url is not an https URL`); + if (c.statusReport) { + if (!c.statusReport.label.trim()) errors.push(`${where}: statusReport.label is empty`); + if (!/^https:\/\//.test(c.statusReport.url)) errors.push(`${where}: statusReport.url is not an https URL`); + } + for (const l of c.dashboardLinks ?? []) { + if (!l.label.trim()) errors.push(`${where}: dashboardLinks label is empty`); + if (!l.route.startsWith("/") && !/^https:\/\//.test(l.route)) + errors.push(`${where}: dashboardLinks route "${l.route}" is neither a dashboard route nor an https URL`); + } checkRefs(c.trackedRefs, errors, where); checkEntries(c.architecture, errors, where); checkEntries(c.editorial?.done, errors, where); diff --git a/pm-stack/aggregator/src/render.ts b/pm-stack/aggregator/src/render.ts index 953d0519e..bb168cc64 100644 --- a/pm-stack/aggregator/src/render.ts +++ b/pm-stack/aggregator/src/render.ts @@ -1272,8 +1272,13 @@ export function renderProgramProject( } else { linkBits.push(`No home doc configured for this project yet.`); } + if (config.statusReport) { + linkBits.push(`Status report: ${esc(config.statusReport.label)}`); + } for (const l of config.dashboardLinks ?? []) { - linkBits.push(`${esc(l.label)}`); + linkBits.push( + `${esc(l.label)}${l.note ? ` (${esc(l.note)})` : ""}`, + ); } linkBits.push(`JSON twin`); diff --git a/pm-stack/aggregator/src/server.ts b/pm-stack/aggregator/src/server.ts index bc4ebd20d..f813a3cb8 100644 --- a/pm-stack/aggregator/src/server.ts +++ b/pm-stack/aggregator/src/server.ts @@ -98,8 +98,8 @@ function json(body: unknown, status = 200): Response { }); } -function html(body: string): Response { - return new Response(body, { headers: { "content-type": "text/html; charset=utf-8" } }); +function html(body: string, status = 200): Response { + return new Response(body, { status, headers: { "content-type": "text/html; charset=utf-8" } }); } async function boardContext() { @@ -281,6 +281,7 @@ async function handler(req: Request): Promise { if (!config) { return html( `

Not found

no project with id ${programProjectMatch[1]} in the registry (src/project-config.ts) — back to the projects index

`, + 404, ); } const [items, pool, rows] = await Promise.all([ diff --git a/pm-stack/aggregator/test/boot.test.ts b/pm-stack/aggregator/test/boot.test.ts index 051a56762..e72ab622e 100644 --- a/pm-stack/aggregator/test/boot.test.ts +++ b/pm-stack/aggregator/test/boot.test.ts @@ -102,6 +102,17 @@ describe("server boots from fixtures", () => { expect(attention.length).toBeGreaterThan(0); }); + test("unknown project page is a styled 404; a known project stays 200 (#1186)", async () => { + const missing = await fetch(`http://127.0.0.1:${port}/program/nope`); + expect(missing.status).toBe(404); + const missingHtml = await missing.text(); + expect(missingHtml).toContain("Not found"); + expect(missingHtml).toContain("no project with id"); + const known = await fetch(`http://127.0.0.1:${port}/program/id`); + expect(known.status).toBe(200); + expect(await known.text()).toContain("id platform"); + }); + test("item drill-down resolves a board card", async () => { const board = (await (await fetch(`http://127.0.0.1:${port}/api/board.json`)).json()) as { board: { id: string }[]; diff --git a/pm-stack/aggregator/test/program.test.ts b/pm-stack/aggregator/test/program.test.ts index e61eaaf7a..2207826a6 100644 --- a/pm-stack/aggregator/test/program.test.ts +++ b/pm-stack/aggregator/test/program.test.ts @@ -185,6 +185,48 @@ describe("project registry", () => { expect(projectConfigById("nope")).toBeUndefined(); }); + test("researched editorial slices are in the shipped registry (#1124/#1125/#1126/#1128)", () => { + const lab = projectConfigById("lab-cluster")!; + expect(lab.statusReport?.label).toContain("2026-10-27"); + expect(lab.statusReport?.url).toContain("doc/2026-10-27T03-14-08Z_status_basement-cluster"); + const labGaps = (lab.editorial?.gaps ?? []).map((e) => `${e.title} ${e.detail}`).join("\n"); + expect(labGaps).toContain("CPCOE-29381"); + expect(labGaps).toContain("2026-10-21"); + expect(lab.editorial?.gaps.some((e) => (e.refs ?? []).includes("developing-today/code#1091"))).toBe(true); + + const id = projectConfigById("id")!; + for (const n of [1076, 1077, 1078, 1235, 1239, 1240]) { + expect(id.trackedRefs).toContain(`developing-today/code#${n}`); + } + const idLinks = (id.dashboardLinks ?? []).map((l) => l.route); + for (const doc of ["ARCHITECTURE.md", "README.md", "TODO.md"]) { + expect(idLinks.some((r) => r.endsWith(`pkgs/id/${doc}`))).toBe(true); + } + expect((id.editorial?.done ?? []).map((e) => e.title).join("\n")).toContain("validation in progress"); + expect((id.editorial?.done ?? []).map((e) => e.title)).not.toContain("Phases 9–11 delivered"); + + const fleet = projectConfigById("repo-fleet")!; + const fleetUpcoming = [...(fleet.editorial?.next ?? []), ...(fleet.editorial?.longTerm ?? [])]; + const fleetUpcomingText = fleetUpcoming.map((e) => `${e.title} ${e.detail}`).join("\n"); + for (const n of [1233, 1130, 1131, 1234, 1185, 1187, 1190, 310, 311]) { + expect(fleet.trackedRefs).toContain(`developing-today/code#${n}`); + } + expect(fleetUpcomingText).toContain("Queue fleet reliability"); + expect(fleetUpcomingText).toContain("OpenChamber session dispatch + babysitter"); + expect(fleetUpcomingText).toContain("stale-PR triage"); + // Closed #257/#292 are recorded as done, never presented as upcoming. + expect(fleetUpcoming.flatMap((e) => e.refs ?? [])).not.toContain("developing-today/code#257"); + expect(fleetUpcoming.flatMap((e) => e.refs ?? [])).not.toContain("developing-today/code#292"); + const fleetDone = (fleet.editorial?.done ?? []).map((e) => `${e.title} ${e.detail}`).join("\n"); + expect(fleetDone).toContain("closed in GitHub"); + const fleetGaps = (fleet.editorial?.gaps ?? []).map((e) => `${e.title} ${e.detail}`).join("\n"); + expect(fleetGaps).toContain("2026-10-27"); + expect(fleetGaps).toContain("665 open issues"); + + const cloud = projectConfigById("cloud-program")!; + expect(cloud.roadmapArea).toBe("Cloud program"); + }); + test("validation catches duplicates and malformed entries", () => { const base: ProjectConfig = { id: "x-proj", @@ -204,6 +246,17 @@ describe("project registry", () => { ]); expect(bad.some((e) => e.includes("not a slug"))).toBe(true); expect(bad.some((e) => e.includes("malformed ref"))).toBe(true); + const badLinks = validateProjectConfigs([ + { + ...base, + statusReport: { label: " ", url: "http://example.com/report" }, + dashboardLinks: [{ label: "", route: "example.com/doc" }], + }, + ]); + expect(badLinks.some((e) => e.includes("statusReport.label is empty"))).toBe(true); + expect(badLinks.some((e) => e.includes("statusReport.url is not an https URL"))).toBe(true); + expect(badLinks.some((e) => e.includes("dashboardLinks label is empty"))).toBe(true); + expect(badLinks.some((e) => e.includes("dashboardLinks route"))).toBe(true); }); test("summaries derive pool/roadmap counts per project; unset is counted once", () => { @@ -222,9 +275,13 @@ describe("project registry", () => { expect(byId.get("id")?.roadmapRows).toBe(7); // fixture seed area "id" expect(byId.get("repo-fleet")?.roadmapRows).toBe(4); expect(byId.get("lab-cluster")?.roadmapRows).toBe(6); - // Null-area projects claim nothing, however much pool exists. + // A null pool Area claims no pool items, however much pool exists. expect(byId.get("mcpx")?.poolTotal).toBe(0); - expect(byId.get("cloud-program")?.roadmapRows).toBe(0); + expect(byId.get("mcpx")?.roadmapRows).toBe(0); + // cloud-program has no pool Area, but its Grist area exists (#1128): + // the fixture seed's one "Cloud program" row counts. + expect(byId.get("cloud-program")?.poolTotal).toBe(0); + expect(byId.get("cloud-program")?.roadmapRows).toBe(1); expect(unsetPoolCount(pool)).toBe(1); }); @@ -263,6 +320,27 @@ describe("renderProgramProject", () => { const cloudHtml = renderProgramProject(buildProjectDetail(cloud, items, pool, rows), items, PROV); expect(cloudHtml).toContain("No home doc configured"); expect(cloudHtml).toContain("/quota"); + // Its Grist area exists (#1128): the seed's OCI row renders. + expect(cloudHtml).toContain("OCI Always Free — run the technical maximum"); + expect(cloudHtml).not.toContain("No Grist roadmap area yet"); + + const lab = projectConfigById("lab-cluster")!; + const labHtml = renderProgramProject(buildProjectDetail(lab, items, pool, rows), items, PROV); + expect(labHtml).toContain("Status report:"); + expect(labHtml).toContain("doc/2026-10-27T03-14-08Z_status_basement-cluster"); + expect(labHtml).toContain("CPCOE-29381"); + + const id = projectConfigById("id")!; + const idHtml = renderProgramProject(buildProjectDetail(id, items, pool, rows), items, PROV); + expect(idHtml).toContain("pkgs/id/ARCHITECTURE.md"); + expect(idHtml).toContain("pkgs/id/TODO.md"); + expect(idHtml).toContain("idle planning"); + expect(idHtml).toContain("Phases 9–11 delivered; validation in progress"); + + const fleet = projectConfigById("repo-fleet")!; + const fleetHtml = renderProgramProject(buildProjectDetail(fleet, items, pool, rows), items, PROV); + expect(fleetHtml).toContain("Queue fleet reliability"); + expect(fleetHtml).toContain("665 open issues"); }); test("pm-dashboard page retains the full #1046 document", async () => {