← back to build-pages
api: /api/fleet/velocity — rolling-window commit counts (last_24h + last_7d + by_project) + 4 smoke
ba6940af6273a6d53819f8340aa6b2111bf42e86 · 2026-05-13 17:11:47 -0700 · SteveStudio2
Files touched
M server.jsM test/smoke.js
Diff
commit ba6940af6273a6d53819f8340aa6b2111bf42e86
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date: Wed May 13 17:11:47 2026 -0700
api: /api/fleet/velocity — rolling-window commit counts (last_24h + last_7d + by_project) + 4 smoke
---
server.js | 28 ++++++++++++++++++++++++++++
test/smoke.js | 7 +++++++
2 files changed, 35 insertions(+)
diff --git a/server.js b/server.js
index 9c6ef68..80fb00a 100644
--- a/server.js
+++ b/server.js
@@ -621,6 +621,33 @@ app.get('/p/:slug/c/:hash', async (req, res, next) => {
} catch (e) { next(e); }
});
+// /api/fleet/velocity — commit counts in rolling windows across every project.
+// Returns total + per-project breakdown for last-24h and last-7d. Useful for
+// "is the work continuing?" dashboards. Same cache lifetime as /api/fleet/*.
+app.get('/api/fleet/velocity', async (_req, res, next) => {
+ try {
+ const now = Date.now();
+ const dayMs = 24 * 60 * 60 * 1000;
+ const out = { last_24h: 0, last_7d: 0, by_project: [] };
+ for (const [slug, p] of Object.entries(PROJECTS)) {
+ const bundle = await getProjectBundle(slug, p).catch(() => null);
+ if (!bundle) continue;
+ let d24 = 0, d7 = 0;
+ for (const c of bundle.commits) {
+ const age = now - new Date(c.dateISO).getTime();
+ if (age < dayMs) d24++;
+ if (age < 7 * dayMs) d7++;
+ }
+ out.last_24h += d24;
+ out.last_7d += d7;
+ out.by_project.push({ slug, name: p.name, last_24h: d24, last_7d: d7 });
+ }
+ out.by_project.sort((a, b) => b.last_7d - a.last_7d);
+ res.setHeader('Cache-Control', 'public, max-age=60, stale-while-revalidate=120');
+ res.json({ ...out, as_of: new Date().toISOString() });
+ } catch (e) { next(e); }
+});
+
// /api/fleet/skills + /api/fleet/agents — aggregated facet counts across every
// project. Merges per-project tallies into a fleet-wide ranking so the user
// can see "the secrets skill is used 17 times across the fleet" without
@@ -871,6 +898,7 @@ app.get('/api', (_req, res) => {
'GET /api/recent': 'Most recent N commits across the whole fleet — date-sorted, ?limit=20',
'GET /api/fleet/agents': 'Fleet-wide agent tallies merged across every project',
'GET /api/fleet/skills': 'Fleet-wide skill tallies merged across every project',
+ 'GET /api/fleet/velocity': 'Commits-per-rolling-window across the fleet — last 24h + last 7d, with per-project breakdown',
'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 e7b40c7..8e4c412 100644
--- a/test/smoke.js
+++ b/test/smoke.js
@@ -212,6 +212,13 @@ function check(name, cond, hint = '') {
check(`fleet/${f}: field`, new RegExp(`"${f}":\\[`).test(r.body));
}
+ // 13b3. fleet/velocity — rolling-window commit counts
+ r = await fetch('/api/fleet/velocity');
+ check('fleet/velocity: 200', r.status === 200);
+ check('fleet/velocity: last_24h',/"last_24h":\d+/.test(r.body));
+ check('fleet/velocity: last_7d', /"last_7d":\d+/.test(r.body));
+ check('fleet/velocity: by_project', /"by_project":\[/.test(r.body));
+
// 13c. per-project facet endpoints (agents / skills / ideas)
for (const facet of ['agents', 'skills', 'ideas']) {
r = await fetch(`/api/projects/butlr/${facet}`);
← 1af9c2a home: velocity counter (commits in last 24h + last 7d) — 200
·
back to build-pages
·
p/:slug: per-project velocity (last 24h + last 7d) in meta l 982149f →