← back to build-pages
api/recent: fleet-wide most-recent commits, date-sorted + 3 smoke
86268fdf3fce9ee8dc617e998dc08c8d687f76a5 · 2026-05-13 14:32:46 -0700 · SteveStudio2
Files touched
M server.jsM test/smoke.js
Diff
commit 86268fdf3fce9ee8dc617e998dc08c8d687f76a5
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date: Wed May 13 14:32:46 2026 -0700
api/recent: fleet-wide most-recent commits, date-sorted + 3 smoke
---
server.js | 26 ++++++++++++++++++++++++++
test/smoke.js | 6 ++++++
2 files changed, 32 insertions(+)
diff --git a/server.js b/server.js
index ae6d0f0..76b53dd 100644
--- a/server.js
+++ b/server.js
@@ -516,6 +516,31 @@ app.get('/p/:slug/c/:hash', async (req, res, next) => {
} catch (e) { next(e); }
});
+// /api/recent — most recent N commits across the entire fleet, merged + sorted
+// by date desc. Powers a "today across the fleet" rail. Shares the bundle
+// cache so it's free after the first warm.
+app.get('/api/recent', async (req, res, next) => {
+ const limit = Math.min(Math.max(parseInt(req.query.limit, 10) || 20, 1), 200);
+ try {
+ const all = [];
+ for (const [slug, p] of Object.entries(PROJECTS)) {
+ const bundle = await getProjectBundle(slug, p).catch(() => null);
+ if (!bundle) continue;
+ // Each project's commits are already date-desc; take the leading slice
+ // up to `limit`. Final merge will sort the union back into date order.
+ for (const c of bundle.commits.slice(0, limit)) {
+ all.push({
+ slug, project: p.name,
+ hash: c.shortHash, date: c.dateISO, subject: c.subject,
+ });
+ }
+ }
+ all.sort((a, b) => (b.date < a.date ? -1 : b.date > a.date ? 1 : 0));
+ res.setHeader('Cache-Control', 'public, max-age=30, stale-while-revalidate=60');
+ res.json({ total: all.length, limit, results: all.slice(0, limit) });
+ } catch (e) { next(e); }
+});
+
// Cross-project search — case-insensitive substring match on commit
// subject + body across every registered project. Used to power a "search
// the whole fleet" experience on the home page. ?q= required (min 2 chars),
@@ -655,6 +680,7 @@ app.get('/api', (_req, res) => {
'GET /api/projects/:slug': 'Per-project stats — head SHA, commit_count, top agents/skills, first/last commit',
'GET /api/projects/:slug/commits': 'Paginated raw commits — ?limit=N&offset=N (1-500 default 100)',
'GET /api/search': 'Cross-project commit search — ?q=<term>&limit=N, round-robin + by_project breakdown',
+ 'GET /api/recent': 'Most recent N commits across the whole fleet — date-sorted, ?limit=20',
'GET /p/:slug': 'Project journal HTML page (?q= deep-link supported)',
'GET /p/:slug/c/:hash': 'Commit detail page — full diff + body',
'GET /p/:slug/feed.atom': 'Atom 1.0 feed — up to 50 recent commits, autodiscovered on project page',
diff --git a/test/smoke.js b/test/smoke.js
index 1873fc3..1ac3377 100644
--- a/test/smoke.js
+++ b/test/smoke.js
@@ -179,6 +179,12 @@ function check(name, cond, hint = '') {
check('404: branded HTML', /build-pages/.test(r.body));
check('404: back-link', /back to all projects/.test(r.body));
+ // 13b. /api/recent — fleet-wide recent commits
+ r = await fetch('/api/recent?limit=5');
+ check('recent: 200', r.status === 200);
+ check('recent: results array', /"results":\[/.test(r.body));
+ check('recent: hash field', /"hash":"[a-f0-9]{4,}/.test(r.body));
+
// 14. cross-project search (round-robin merge + per-project breakdown)
r = await fetch('/api/search?q=whisper&limit=10');
check('search: 200', r.status === 200);
← d1c2c50 commit page: diff +/- syntax highlighting (green add, red re
·
back to build-pages
·
home: 'Recent across the fleet' rail — server-rendered top 1 afcaaa3 →