Skip to content

Commit 6b60552

Browse files
authored
admin: CI/security signals, honest Reach panel, releases pagination fix (#65)
* feat(admin): CI/security signals, honest Reach panel, releases pagination fix Adds latest CI run status and open Dependabot alert count to the admin collector and a new Project Health section. Replaces the two Reach tiles that were permanently dead for this npm-only project (bundle downloads, newest release pulls — GitHub release assets don't apply) with real GitHub-native people signals (contributors, watchers). Fixes a releases fetch capped at per_page=20 that silently dropped older releases (repo has 29). Makes the existing npm mirror-inflation exclusion from Reach an explicit, first-class note instead of a footnote. See docs/adr/0013 for the full rationale. * fix(admin): exclude owner/bots from contributorsCount, link stargazer names The new "contributors" Reach tile counted GitHub's raw contributor list verbatim, including the repo owner and dependabot[bot] — inconsistent with buildPeople(), which already excludes both from contributors/ stargazers/forks elsewhere in this file. Verified live: contributorsCount was reporting 3 (pacphi + dependabot[bot] + one real external contributor) when the honest count is 1. Also makes the 20 unnamed-but-dated-stargazer names clickable GitHub profile links, matching every other person-card on the page instead of rendering as plain "@login" text.
1 parent db2366a commit 6b60552

9 files changed

Lines changed: 303 additions & 29 deletions

File tree

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ What the verbs cover:
7070
| **status** | Per-subsystem ✓/⚠/✗ (versions, the kit's own version, **ruvnet-brain** (present + release drift, or "not installed"), natives (agentdb copies **and** ruflo's own memory runtime — the one `npx ruflo memory` loads — load-tested for a native better-sqlite3, not just the agentdb dirs), **memory-pin** (warns when `CLAUDE_FLOW_DB_PATH` points off the live DB), security, learning, aqe/RVF, **agentdb** (CLI present + coherent with ruflo's bundled version, or a store-skew warning), MCP, **hosts** (claude/codex — version + install method + **auth mode** (subscription $0 vs metered api-key), with the **primary** host marked and a *fail* when the primary host is absent), **providers** (host wiring + aqe fallback chain, or "drifted"/claude-only default), **routing** (per-activity Claude/Codex host+model policy, when dual-host — with drift vs the on-disk `agentOverrides`), daemons, guidance-file blocks (`~/.claude/CLAUDE.md`, project `AGENTS.md`, and `~/.codex/AGENTS.md` on codex machines), statusline), each drift row naming what `sync` would do about it — plus a **health-history** line that flags regressions since the last sync (learning shrank, native slots dropped, drift/security backslid). |
7171
| **sync** | The one convergence verb: upgrades first when a new release exists, then re-heals everything an upgrade wipes, then re-checks and reports. Included in that heal: it **installs any enabled frontier host** (claude/codex) that's entirely absent — never touching an external (mise/brew/native) install — and **re-applies provider wiring** (the `ENABLE_*` host env, the aqe fallback chain, and ruflo API providers) whenever it has drifted — and, on a dual-host project, **seeds/heals the per-activity routing policy** (materializing it into agentic-qe's `agentOverrides`, e.g. after an aqe upgrade first makes it eligible). It also **installs/repins the standalone `agentdb` CLI** to ruflo's bundled version (keeping the shared cognitive store coherent) and appends a **health-history snapshot** so `status` can flag regressions across syncs. It also **re-runs the RuvNet Brain installer** to pull the latest release when the on-disk KB has drifted (or installs it if absent, when enabled). It also **self-updates the kit**: when a newer `@pacphi/agentic-kit` exists it installs it as the *last* step (the new code applies from the next `ak` run, never mid-sync). Prerelease installs (`4.0.0-alpha.*`) track the `next` npm dist-tag as well as `latest`, so alphas see their successors; stable installs only ever follow `latest`. `--no-upgrade` skips the self-update along with the package upgrades. |
7272
| **dashboard** | Opens a read-only local web dashboard (`127.0.0.1:7431`, localhost-only, never detaches) with seven tabs: **Overview · Hosts & Routing · Providers · Runtime · Intelligence · Usage · Live**. The first five render `ak status` health and routing; Usage indexes local Claude/Codex transcripts on demand. Live groups work by project, then provider-branded root sessions with nested agent/worker threads, and pairs an interactive agent/tool execution canvas with a rich, server-masked transcript stream. Active sessions can be followed live or reviewed with synchronized play/pause/seek; completed sessions remain available for bounded playback. Live contains no chat or control plane. Ruflo, agentic-qe, and dual-run stores are not auto-discovered; register each trusted structured JSONL file with repeatable `--live-source 'surface=path'` (`surface` is `ruflo`, `aqe`, or `dual-run`). The page is self-contained and offline-first (no internet fetches; local files and loopback subprocesses/endpoints only). See [Live Sessions](docs/LIVE-SESSIONS.md) for coverage, syntax, and privacy limits. **Auto-opens your browser** (`--no-open` for headless/SSH); `--port N` changes the port; tabs deep-link (`#live`) and persist. Stop with Ctrl-C. (Also available as `ak x dashboard`.) |
73-
| **admin** | Opens the **maintainer admin** (`127.0.0.1:7432`, localhost-only, foreground) — the project-telemetry sibling of `dashboard`, with the same dark/light visual theme and persisted theme preference: unique repo visitors and cloners (GitHub traffic API, needs a push-access token via `GITHUB_TOKEN`/`GH_TOKEN`/`gh auth token` — panels degrade honestly without one), npm download momentum (last 7d vs prior 7d, sparklines), release pulls, a **"since you last looked"** delta strip over a local baseline, open issues/PRs from others (oldest first), and external humans ranked by recency (bots excluded). Access is gated by a **per-session token** carried in the URL fragment and sent header-only; the page makes **zero external fetches** (the server proxies GitHub/npm; your credential never reaches the page or the payload — ADR-0007). Where `dashboard` is offline-first, `admin` does deliberate GitHub/npm egress — that contract split is why they're siblings, not tabs. `--port N`, `--no-open`; Ctrl-C stops. (Also available as `ak x admin`.) |
73+
| **admin** | Opens the **maintainer admin** (`127.0.0.1:7432`, localhost-only, foreground) — the project-telemetry sibling of `dashboard`, with the same dark/light visual theme and persisted theme preference: unique repo visitors and cloners (GitHub traffic API, needs a push-access token via `GITHUB_TOKEN`/`GH_TOKEN`/`gh auth token` — panels degrade honestly without one), contributors and watchers, npm download momentum (last 7d vs prior 7d, sparklines — shown as trend only, never an absolute reach number, since mirrors/CI inflate the raw count), latest CI run status and open Dependabot alerts, a **"since you last looked"** delta strip over a local baseline, open issues/PRs from others (oldest first), and external humans ranked by recency (bots excluded). Access is gated by a **per-session token** carried in the URL fragment and sent header-only; the page makes **zero external fetches** (the server proxies GitHub/npm; your credential never reaches the page or the payload — ADR-0007, ADR-0013). Where `dashboard` is offline-first, `admin` does deliberate GitHub/npm egress — that contract split is why they're siblings, not tabs. `--port N`, `--no-open`; Ctrl-C stops. (Also available as `ak x admin`.) |
7474
| **dual** | Runs a **Claude + Codex collaboration swarm** using your per-activity routing policy: `ak dual run <template> "<task>"` materializes a dual-run config (each pipeline step assigned to the host + model your policy chose) and drives it via `claude-flow-codex`. Templates: `feature`, `security`, `refactor`, `packaging`, `release`. `--dry-run` prints the plan + config without running; `--route 'activity:host[:model]'` overrides one step for that run; `--escalate` retries once up the cross-vendor ladder on failure. Requires dual-host enabled (`ak setup --codex`, or `ak x provider pick --host claude,codex`). **Pre-flight refusal:** before spawning a worker, `dual run` refuses to start when ruflo's memory runtime lacks a native better-sqlite3 binding **and** the shared DB has an active native WAL (`-wal`/`-shm` sidecars) — the native WAL writer and the WASM `ruflo memory store` cannot share that DB without corrupting it; the fix is `ak sync` (builds the native binding), then retry. |
7575
| **uninstall** | Removes the kit's footprint (and any legacy shell-kit install); project data is never touched; `--purge` also offers to remove the global packages. |
7676

Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
# ADR-0013 — Admin: build/security signals, an honest Reach panel, and a pagination fix
2+
3+
- **Status:** Accepted
4+
- **Date:** 2026-07-28
5+
- **Deciders:** agentic-kit maintainers
6+
7+
## Context
8+
9+
ADR-0007 shipped `ak x admin` as a loopback, deliberate-egress maintainer console answering "how is
10+
this project actually doing" from GitHub + npm. A follow-up audit (live queries against the real
11+
`pacphi/agentic-kit` repo, with the same token the admin server itself resolves) found the page
12+
answered that question less completely than it could:
13+
14+
1. **Two of the four Reach hero tiles were permanently dead for this project.** "bundle downloads"
15+
and "newest release pulls" read from GitHub release **assets**. agentic-kit ships exclusively via
16+
npm — verified live, 0 of 29 releases carry any asset — so both tiles rendered `—` forever. That
17+
is correct behavior per the model's "unknown is not zero" rule (ADR-0007 §4.1), not a bug, but it
18+
is dead screen real estate for this project's actual distribution channel.
19+
2. **`admin-collect.mjs` capped the releases fetch at `per_page=20`** while the "bundle downloads"
20+
tile's own copy claimed "lifetime, **all releases**." The repo already has 29 releases; the oldest
21+
9 were silently dropped from both the releases list and `totalAssetDownloads`. Harmless today only
22+
because no release carries assets — the moment one does, the label becomes false with no
23+
truncation indicator.
24+
3. **No CI/build health or security-alert signal reached the page at all**, despite `ci`/`release`
25+
GitHub Actions workflows running on every push and Dependabot maintaining the dependency tree —
26+
both directly answer "how is this project doing" and both were one more `Promise.all` entry away.
27+
4. **Two already-collected fields were invisible in the UI**: `repo.watchers` (GitHub subscriber
28+
count) was computed in the collector and never rendered; `npm.lastWeek`/`npm.lastMonth` were
29+
computed and only ever consumed as a trend-arrow input in Momentum, never shown as an absolute
30+
number anywhere.
31+
5. On point 4's npm figures specifically: `admin-view.mjs` already carried a **deliberate** decision
32+
to keep npm's absolute download count out of the Reach panel, because `npm downloads are dominated
33+
by mirrors` (CI runners, cache warmers, and registry mirrors re-pull as often as a real install,
34+
and `api.npmjs.org` cannot distinguish them). That rationale was correct but stated in one
35+
half-sentence buried in a footnote — worth stating as its own first-class, explicit entry so a
36+
maintainer reading the Gaps tab understands *why* npm never appears as a reach number, not just
37+
that it doesn't.
38+
39+
## Decision
40+
41+
### 1. Fix the releases pagination bug
42+
43+
`admin-collect.mjs` now fetches `/releases?per_page=100` (GitHub's per-request ceiling), matching the
44+
same single-page-best-effort cap already used for issues/stargazers/forks in this file. This is a
45+
correctness fix, not a new pagination system — the file's existing house pattern (one page, capped at
46+
100, no further pages followed) is extended consistently rather than reinvented. The 100-item ceiling
47+
is now called out in a code comment at the fan-out site so a future reader does not mistake it for
48+
full pagination.
49+
50+
### 2. Replace the two dead Reach tiles with real, GitHub-native people signals
51+
52+
"bundle downloads" and "newest release pulls" are replaced with:
53+
54+
- **contributors** — `GET /repos/{slug}/contributors`, count only. A genuinely different circle from
55+
"people who filed issues/PRs" (buildPeople's `people.contributors`): this counts who has code in
56+
the tree via GitHub's own merge history.
57+
- **watching** — `repo.watchers` (already collected, previously unrendered). A standing-interest
58+
signal distinct from a one-time visitor.
59+
60+
Both stay inside GitHub's own numbers, so neither reopens the mirror-inflation problem npm carries.
61+
The npm absolute-download question raised by point 4 was decided explicitly: **npm downloads still do
62+
not appear in Reach.** The existing exclusion was correct; only the messaging around it was thin (see
63+
Decision 4).
64+
65+
### 3. A new "Project health" section: latest CI run + open Dependabot alerts
66+
67+
A new subsection in the Overview panel, fed by two more `Promise.all` entries:
68+
69+
- `GET /repos/{slug}/actions/runs?per_page=5` → latest run's `{name, status, conclusion, at, url}`
70+
plus a failure count over the fetched page. No runs found renders `—`, never a fabricated
71+
"passing" (the same unknown-is-not-zero discipline as everywhere else in this file).
72+
- `GET /repos/{slug}/dependabot/alerts?state=open&per_page=100` → open alert count. This endpoint
73+
needs a token with `security_events` scope (classic PAT) or equivalent fine-grained access; an
74+
unconfigured or under-scoped token 403s, which `ghJson` already folds to `null` — the tile
75+
degrades to "unknown" with an explanatory `why`, never a false "0 alerts."
76+
77+
Both are additive `Promise.all` entries in the existing single fan-out (ADR-0007 §3) — no new
78+
request pattern, no new auth flow, no new caching layer.
79+
80+
### 4. Make the npm mirror-inflation rationale a first-class, explicit statement
81+
82+
The Gaps tab (ADR-0007's "honesty section") gains a `design`-tagged entry stating plainly that npm
83+
download counts are shown as **trend only, never as a reach number**, and why: mirrors/CI/cache
84+
warmers are indistinguishable from real installs in npm's own data, so an absolute total would
85+
overstate reach. The Reach panel's footer note and the Momentum tile's note are both tightened to
86+
carry the same point concisely rather than as an aside.
87+
88+
## Consequences
89+
90+
- `admin-collect.mjs`'s single `Promise.all` grows from 9 to 12 entries: `contributors`,
91+
`actions/runs`, `dependabot/alerts` join the existing set. All three follow the file's existing
92+
discipline — internally try/caught, degrade to `null`/empty on any failure, never reject the batch,
93+
never appear in the payload if the credential is absent from the request itself (contributors and
94+
CI runs work unauthenticated on a public repo; Dependabot alerts do not and degrade honestly).
95+
- The payload contract gains `contributorsCount` (number|null), `ci: {latest, recentFailures,
96+
recentTotal}`, and `security: {dependabotAlerts}`. `defaultCollect`'s fail-soft path returns honest
97+
empty values for all three so a thrown collector error still yields a renderable payload.
98+
- The Reach panel's four tiles are now: unique repo visitors (hero), contributors, watching, opted-in
99+
installs (still an honest gap) — all either real headcounts or an explicit "not built" admission,
100+
none permanently dead for this project's npm-only distribution model.
101+
- `tests/admin.test.cjs` gained fixtures and assertions for the three new endpoints, including the
102+
same never-fabricate-a-zero discipline tested elsewhere (Dependabot 403 → `null`, no workflow runs
103+
→ `ci.latest: null`, a failing contributors fetch nulls only `contributorsCount`) and an explicit
104+
assertion that the releases fetch requests `per_page=100`.
105+
- `admin-model.mjs` is unchanged — every new field renders through the existing `metric()` helper, so
106+
the pure-model import-nothing constraint (ADR-0007 §5) is untouched.
107+
108+
## Alternatives considered
109+
110+
- **Show npm absolute downloads as a Reach tile instead of GitHub-native signals.** Rejected: it
111+
would reintroduce exactly the mirror-inflation overstatement the original design deliberately
112+
avoided (Context, point 5). GitHub-native signals (contributors, watchers) answer the same "kill
113+
the dead tiles" goal without that risk.
114+
- **Full pagination (follow `Link` headers) for releases/issues/stargazers/forks/contributors.**
115+
Rejected for now: none of these currently exceed the 100-item cap for this repo, and full
116+
pagination is a larger, inconsistent change against the file's established one-page-best-effort
117+
house pattern. The 100-item ceiling is now documented in code rather than silently assumed; revisit
118+
if/when any list genuinely exceeds it.
119+
- **Cache GitHub/npm responses between polls (ETag/`If-None-Match`) to cut request volume on
120+
auto-refresh.** Out of scope for this ADR — freshness-over-caching is ADR-0007's explicit contract
121+
(`Cache-Control: no-store` on every `/api/*` response), and today's request volume is nowhere near
122+
GitHub's rate ceiling. Worth its own ADR if auto-refresh usage patterns change that calculus.
123+
124+
## References
125+
126+
- ADR-0007 (maintainer admin: loopback telemetry with deliberate egress) — this ADR extends its
127+
collector/payload/Reach-panel decisions rather than superseding them.
128+
- `src/lib/admin-collect.mjs`, `src/lib/admin-view.mjs`, `src/lib/admin-server.mjs`,
129+
`src/lib/admin-styles.mjs`, `tests/admin.test.cjs`.

‎docs/adr/README.md‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ Consequences**, and cites the grounded source it rests on where relevant.
2121
| [0010](0010-provider-mediated-quota-reads.md) | Provider-mediated quota reads (the only honest denominators) | Accepted |
2222
| [0011](0011-local-model-provenance-zero-cost-and-transcript-fidelity.md) | Local models: provenance out-of-band, $0 per model, stated transcript fidelity | Proposed |
2323
| [0012](0012-live-sessions-observability.md) | Live sessions as local, evidence-graded observability | Accepted |
24+
| [0013](0013-admin-build-security-signals-and-honest-reach.md) | Admin: build/security signals, an honest Reach panel, and a pagination fix | Accepted |
2425

2526
Theme: ADRs **0001–0006** define **dual-host LLM routing and leadership** — how `ak` lets ruflo route
2627
each development activity (architecture, implementation, testing, review, …) to the right host (Claude
@@ -49,4 +50,8 @@ keep content out of broad topology snapshots/replay while preserving masked loca
4950
keeping chat/control absent. Claude/Codex collection is
5051
implemented; ruflo, agentic-qe, and dual-run require explicit, repeatable `--live-source`
5152
registration. Independent plugin/skill/MCP discovery and a measured frame-time budget remain
52-
documented limitations rather than implied capabilities.
53+
documented limitations rather than implied capabilities. **0013** extends 0007's admin collector with
54+
CI-run and Dependabot-alert signals, fixes a releases-pagination cap that silently dropped older
55+
releases, replaces two GitHub-release-asset Reach tiles that were permanently dead for this npm-only
56+
project with real GitHub-native people signals (contributors, watchers), and states the existing
57+
npm-mirror-inflation exclusion explicitly instead of as a footnote.

‎src/commands/x/admin.mjs‎

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,13 @@ export const options = {
1717
export const help = `ak admin — maintainer-only local telemetry admin (localhost only) [alias: ak x admin]
1818
1919
Serves a self-contained web panel showing how the project is actually doing —
20-
unique repo visitors, release-asset pulls, npm range, GitHub traffic, and the
21-
humans who filed issues, opened PRs, or forked. Unlike \`ak dashboard\` (which is
22-
offline-first and never leaves your machine), admin makes DELIBERATE network
23-
egress: the server fetches GitHub + npm on your behalf and reads a GitHub
24-
credential (GITHUB_TOKEN → GH_TOKEN → \`gh auth token\`, best-effort) at runtime.
25-
That credential is never persisted and never reaches the page.
20+
unique repo visitors, contributors, npm range, GitHub traffic, latest CI run,
21+
open Dependabot alerts, and the humans who filed issues, opened PRs, or
22+
forked. Unlike \`ak dashboard\` (which is offline-first and never leaves your
23+
machine), admin makes DELIBERATE network egress: the server fetches GitHub +
24+
npm on your behalf and reads a GitHub credential (GITHUB_TOKEN → GH_TOKEN →
25+
\`gh auth token\`, best-effort) at runtime. That credential is never persisted
26+
and never reaches the page.
2627
2728
Bound to 127.0.0.1. A fresh session token is minted at startup and carried into
2829
the browser in the launch URL's # fragment (never a query param, never logged);

0 commit comments

Comments
 (0)