Skip to content

Commit 93cb66e

Browse files
RonTuretzkyclaude
andcommitted
merge main: the tend surface for every tree, and the brief probe's own 200
Two incoming commits from the parallel tree-menu work: 40e1d18 every tree is tended in the room's words — the panel is gone 87d0649 asking whether a study exists is not an error — the brief probe gets its own 200 The second one fixes MY code: the study probe I added fired a GET per imported tree at the brief route and read 404 as "no study", so every no-study import logged a console 404 on every load. It now asks /brief/exists, which answers 200 either way. ONE CONFLICT, in src/ui/App.tsx, and it was structural rather than semantic: I had DELETED the import-arrival effect (the "⚘ Plant it…" offer the operator asked to be rid of) while they EDITED the study probe that sat inside the same span. Resolved by keeping both intentions — their /brief/exists probe stays, the arrival effect and its evaporate timer stay gone. Verified after resolving: zero importPlantOffer/isFreshImportArrival references remain, and the /api/process/:upid/brief/exists route their probe calls really is there (app.ts:402). tsc clean, 1347 pass / 0 fail, build green. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2 parents bc0c791 + 87d0649 commit 93cb66e

3 files changed

Lines changed: 47 additions & 0 deletions

File tree

src/server/app.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -434,6 +434,13 @@ describe("POST /api/projects/import", () => {
434434
expect(built.previewUrl).toMatch(/^http:\/\/127\.0\.0\.1:\d+\//u);
435435
});
436436

437+
test("the brief EXISTENCE probe answers 200 either way — no console-404 per no-study import", async () => {
438+
const { app } = await makeApp();
439+
const response = await app.request("/api/process/upid-never-studied/brief/exists");
440+
expect(response.status).toBe(200);
441+
expect(((await response.json()) as { has: boolean }).has).toBe(false);
442+
});
443+
437444
test("A BARE REPO LINK IS STUDIED, NOT BUILT — and the brief route serves it", async () => {
438445
// The live miss this exists for: someone imported a repo, typed "just
439446
// study it first", and the room built it anyway because the description

src/server/app.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -394,6 +394,14 @@ export function createProjectorApp(runtime: ProjectorRuntime, options: Projector
394394
// tree's "📖 About this project" card. 404 for trees that were never
395395
// studied (a build-intent import, a local concept, the room itself), which
396396
// is how the wall decides whether to offer the row at all.
397+
// EXISTENCE PROBE — always 200. The wall asks this for EVERY import on
398+
// every load to gate the "📖 About this project" row; answering the
399+
// perfectly normal "no study" case with a 404 sprayed a console error per
400+
// import (Chromium logs every 404 fetch) and tripped the console-error-free
401+
// e2e on the seeded live path.
402+
app.get("/api/process/:upid/brief/exists", (context) => {
403+
return context.json({ has: runtime.projectBrief(context.req.param("upid")) !== null });
404+
});
397405
app.get("/api/process/:upid/brief", (context) => {
398406
const brief = runtime.projectBrief(context.req.param("upid"));
399407
if (brief === null) {

src/ui/App.tsx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,38 @@ export function ProjectorApp({ initialSnapshot, urlSearch, initialOverlay, initi
354354
window.addEventListener("storage", onStorage);
355355
return () => window.removeEventListener("storage", onStorage);
356356
}, []);
357+
// PROBE FOR STUDIES. The tree menu offers "📖 About this project" only when
358+
// a study exists behind it — a row that opens an empty card is worse than
359+
// no row. One HEAD-ish GET per imported tree, once, cached by upid.
360+
useEffect(() => {
361+
let cancelled = false;
362+
for (const process of snapshot.processes) {
363+
const kind = process.source?.kind;
364+
if (kind !== "github-import" && kind !== "phone-import") {
365+
continue;
366+
}
367+
if (process.upid in briefUpids) {
368+
continue;
369+
}
370+
void (async () => {
371+
let has = false;
372+
try {
373+
// The exists route answers 200 either way — probing the brief route
374+
// itself meant a console 404 per no-study import on every load.
375+
const response = await fetch(`/api/process/${encodeURIComponent(process.upid)}/brief/exists`);
376+
has = response.ok && ((await response.json()) as { has?: unknown }).has === true;
377+
} catch {
378+
has = false; // server down / route absent: no row, no lie
379+
}
380+
if (!cancelled) {
381+
setBriefUpids((current) => ({ ...current, [process.upid]: has }));
382+
}
383+
})();
384+
}
385+
return () => {
386+
cancelled = true;
387+
};
388+
}, [snapshot.processes, briefUpids]);
357389

358390
// ?zen=1 boots a dedicated display straight into the chrome-less scene.
359391
const [zenMode, setZenMode] = useState(urlConfig.zen);

0 commit comments

Comments
 (0)