sigrank 0.0.180 → 0.0.182
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +23 -22
- package/adapters/index.mjs +55 -22
- package/adapters/tokenpull.mjs +20 -28
- package/analytics/cascade.mjs +27 -0
- package/identity/keystore.mjs +1 -12
- package/lib/constants.mjs +111 -0
- package/lib/pkg-version.mjs +59 -0
- package/manifest.json +46 -0
- package/package.json +3 -1
- package/presentation/cli.mjs +49 -51
- package/presentation/tui.mjs +36 -39
- package/resources/class-tiers.md +51 -12
- package/resources/privacy-model.md +8 -5
- package/submit/index.mjs +8 -21
- package/tokscale_analytics.mjs +5 -16
- package/tools/_framing.mjs +94 -17
- package/tools/_helpers.mjs +5 -10
- package/tools/_schemas.mjs +68 -38
- package/tools/compare-operators.mjs +2 -1
- package/tools/compare-self.mjs +4 -2
- package/tools/describe-power-user.mjs +17 -6
- package/tools/enroll.mjs +1 -3
- package/tools/get-best-operator.mjs +2 -1
- package/tools/get-leaderboard.mjs +3 -2
- package/tools/get-operator.mjs +1 -1
- package/tools/index.mjs +4 -42
- package/tools/optimize-efficiency.mjs +15 -5
- package/tools/rank-paste.mjs +1 -1
- package/tools/submit-paste.mjs +13 -10
- package/tools/tokenpull-submit.mjs +11 -10
- package/tools/tokscale-breakdown.mjs +2 -42
package/presentation/cli.mjs
CHANGED
|
@@ -23,9 +23,11 @@
|
|
|
23
23
|
*/
|
|
24
24
|
|
|
25
25
|
import { callTool, DEFAULT_API_BASE, pullActivePlatforms } from "../tools.mjs";
|
|
26
|
-
import { classify } from "../cascade.mjs";
|
|
26
|
+
import { cascade, classify, CLASS_TIERS, UNCLASSED } from "../cascade.mjs";
|
|
27
|
+
import { ALL_PLATFORMS } from "../adapters.mjs";
|
|
27
28
|
import { ensureIdentity, keystorePath } from "../keystore.mjs";
|
|
28
29
|
import { submitSignedWindow } from "../submit.mjs";
|
|
30
|
+
import { LEADERBOARD_METRIC } from "../lib/constants.mjs";
|
|
29
31
|
import { execFile } from "child_process";
|
|
30
32
|
import { existsSync, readFileSync } from "fs";
|
|
31
33
|
import os from "os";
|
|
@@ -88,6 +90,9 @@ const green = (str) => paint(c.green, str);
|
|
|
88
90
|
const red = (str) => paint(c.red, str);
|
|
89
91
|
|
|
90
92
|
// ── Class tier → color ─────────────────────────────────────────────────────
|
|
93
|
+
// Built from the canonical CLASS_TIERS list (single source of truth). The dead
|
|
94
|
+
// BEARER entry (no tier by that name exists) was removed; UNCLASSED added for
|
|
95
|
+
// the degenerate no-data case.
|
|
91
96
|
const CLASS_COLOR = {
|
|
92
97
|
TRANSMITTER: (s) => paint(c.boldGold, s),
|
|
93
98
|
"ARCH+": (s) => paint(c.boldCyan, s),
|
|
@@ -96,10 +101,17 @@ const CLASS_COLOR = {
|
|
|
96
101
|
BASE: (s) => paint(c.white, s),
|
|
97
102
|
SEEKER: (s) => paint(c.magenta, s),
|
|
98
103
|
REFINER: (s) => paint(c.blue, s),
|
|
99
|
-
BEARER: (s) => paint(c.dim, s),
|
|
100
104
|
IGNITER: (s) => paint(c.dim, s),
|
|
105
|
+
[UNCLASSED]: (s) => paint(c.dim, s),
|
|
101
106
|
};
|
|
102
107
|
const colorClass = (cls) => (CLASS_COLOR[cls] ?? ((s) => s))(cls);
|
|
108
|
+
// Guard: every canonical tier must have a color here.
|
|
109
|
+
for (const t of CLASS_TIERS) {
|
|
110
|
+
if (!CLASS_COLOR[t]) {
|
|
111
|
+
// eslint-disable-next-line no-console
|
|
112
|
+
console.warn(`[cli] CLASS_COLOR map missing color for tier "${t}"`);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
103
115
|
|
|
104
116
|
// ── Terminal utils ──────────────────────────────────────────────────────────
|
|
105
117
|
const CLEAR_SCREEN = `${ESC}2J${ESC}H`;
|
|
@@ -234,7 +246,7 @@ function renderBoardHeader(window = "30d") {
|
|
|
234
246
|
|
|
235
247
|
async function fetchBoard(window = "30d") {
|
|
236
248
|
const res = await fetch(
|
|
237
|
-
`${DEFAULT_API_BASE}/api/v1/leaderboard?window=${window}&metric
|
|
249
|
+
`${DEFAULT_API_BASE}/api/v1/leaderboard?window=${window}&metric=${LEADERBOARD_METRIC}`,
|
|
238
250
|
{
|
|
239
251
|
headers: {
|
|
240
252
|
accept: "application/json",
|
|
@@ -469,8 +481,10 @@ async function tokscalePillars(platform = "claude") {
|
|
|
469
481
|
}
|
|
470
482
|
|
|
471
483
|
function appPillars() {
|
|
472
|
-
// App numbers from screenshots
|
|
473
|
-
//
|
|
484
|
+
// ⚠ STALE HARDCODED DATA — App numbers from screenshots, all-time, per model
|
|
485
|
+
// (no cache fields). Captured 2026-06-23. NOT refreshed automatically. The
|
|
486
|
+
// compare view uses this only as a static reference point; it does NOT feed
|
|
487
|
+
// the ranked submit path. Update manually when a fresh screenshot is taken.
|
|
474
488
|
return {
|
|
475
489
|
all: {
|
|
476
490
|
input: 6_378_000, // sum of all models: 5.6M + 102.1K + 92.9K + 130.3K + 418.9K + 33.5K
|
|
@@ -544,39 +558,32 @@ function fmtDelta(a, b) {
|
|
|
544
558
|
return d > 0 ? green(label) : red(label);
|
|
545
559
|
}
|
|
546
560
|
|
|
547
|
-
// Compute cascade metrics from raw pillars
|
|
561
|
+
// Compute cascade metrics from raw pillars. Fix 3: this was a third copy of
|
|
562
|
+
// the cascade math with its own divide-by-zero policy (safeI = max(i,1) clamp),
|
|
563
|
+
// which let the CLI's Υ drift from the MCP tools' Υ for the same tokens. It now
|
|
564
|
+
// forwards to the single shared cascade() from analytics/cascade.mjs (null-guard
|
|
565
|
+
// policy, identical to rank_paste / submit_verified) and only adds the
|
|
566
|
+
// CLI-display-only `efficiency` + `total` fields the compare view renders.
|
|
548
567
|
function cascadeFromPillars(p) {
|
|
549
568
|
if (!p) return null;
|
|
569
|
+
const c = cascade(p);
|
|
550
570
|
const i = p.input ?? 0;
|
|
551
571
|
const o = p.output ?? 0;
|
|
552
572
|
const cw = p.cacheCreate ?? 0;
|
|
553
573
|
const cr = p.cacheRead ?? 0;
|
|
554
|
-
if (i === 0 && o === 0) return null;
|
|
555
|
-
const safeI = Math.max(i, 1);
|
|
556
574
|
const total = i + o + cw + cr;
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
const
|
|
561
|
-
// dev10x = log10(T × C × R) — only when all four pillars present
|
|
562
|
-
let dev10x = null;
|
|
563
|
-
if (cw > 0 && o > 0 && i > 0 && cr > 0) {
|
|
564
|
-
const T = o / i,
|
|
565
|
-
C = cw / o,
|
|
566
|
-
R = cr / cw;
|
|
567
|
-
dev10x = Math.log10(T * C * R);
|
|
568
|
-
}
|
|
569
|
-
// efficiency = ((cr+cw+o)/i) / 4.0
|
|
570
|
-
const efficiency = (cr + cw + o) / safeI / 4.0;
|
|
571
|
-
const cls = classify(yield_, dev10x);
|
|
575
|
+
// efficiency = ((cr+cw+o)/i)/4.0 — display-only derived metric (not in the
|
|
576
|
+
// canonical cascade). Null when input is 0 (matches the old safeI→0 behavior
|
|
577
|
+
// would have been div-by-zero; the shared cascade already nulls yield there).
|
|
578
|
+
const efficiency = i > 0 ? (cr + cw + o) / i / 4.0 : null;
|
|
572
579
|
return {
|
|
573
|
-
yield:
|
|
574
|
-
velocity,
|
|
575
|
-
leverage,
|
|
576
|
-
snr,
|
|
577
|
-
dev10x,
|
|
580
|
+
yield: c.yield,
|
|
581
|
+
velocity: c.velocity,
|
|
582
|
+
leverage: c.leverage,
|
|
583
|
+
snr: c.snr,
|
|
584
|
+
dev10x: c.dev10x,
|
|
578
585
|
efficiency,
|
|
579
|
-
class:
|
|
586
|
+
class: c.class,
|
|
580
587
|
total,
|
|
581
588
|
};
|
|
582
589
|
}
|
|
@@ -868,11 +875,18 @@ async function runWatch({
|
|
|
868
875
|
id,
|
|
869
876
|
{ platform: plat },
|
|
870
877
|
);
|
|
878
|
+
// Pass 6: gate the ✓ on r.ranked (verified + persisted), NOT on
|
|
879
|
+
// r.status === "received". An unenrolled/revoked device gets HTTP
|
|
880
|
+
// 202 "received" but is NEVER ranked — gating on "received" would
|
|
881
|
+
// show a green ✓ to an operator who didn't actually make the board.
|
|
882
|
+
// Mirrors the isRankedAck() predicate used by every submit path.
|
|
871
883
|
lastSubmitMsg.set(
|
|
872
884
|
key,
|
|
873
|
-
r.
|
|
874
|
-
? green(`✓ tier=${r.verification_tier || "—"}`)
|
|
875
|
-
: red(
|
|
885
|
+
r.ranked
|
|
886
|
+
? green(`✓ tier=${r.verification_tier || "—"} (ranked)`)
|
|
887
|
+
: red(
|
|
888
|
+
`✗ ${r.reason || r.status || "not ranked"}${r.verification_tier ? ` (tier=${r.verification_tier})` : ""}`,
|
|
889
|
+
),
|
|
876
890
|
);
|
|
877
891
|
} catch (e) {
|
|
878
892
|
lastSubmitMsg.set(key, red(`✗ ${e.message}`));
|
|
@@ -934,25 +948,9 @@ async function runWatch({
|
|
|
934
948
|
// - live board position
|
|
935
949
|
// - [S] submit [B] board [Q] quit
|
|
936
950
|
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
"devin",
|
|
941
|
-
"amp",
|
|
942
|
-
"gemini",
|
|
943
|
-
"kimi",
|
|
944
|
-
"qwen",
|
|
945
|
-
"goose",
|
|
946
|
-
"kilo",
|
|
947
|
-
"hermes",
|
|
948
|
-
"droid",
|
|
949
|
-
"codebuff",
|
|
950
|
-
"copilot",
|
|
951
|
-
"opencode",
|
|
952
|
-
"openclaw",
|
|
953
|
-
"pi",
|
|
954
|
-
"other",
|
|
955
|
-
];
|
|
951
|
+
// ALL_PLATFORMS is imported from ../adapters.mjs (single source of truth —
|
|
952
|
+
// was redefined inline here, which drifted from the adapter registry whenever
|
|
953
|
+
// a platform was added there but not in this list).
|
|
956
954
|
|
|
957
955
|
async function runSigRank() {
|
|
958
956
|
write(HIDE_CURSOR);
|
package/presentation/tui.mjs
CHANGED
|
@@ -14,6 +14,7 @@
|
|
|
14
14
|
|
|
15
15
|
import { callTool, DEFAULT_API_BASE, pullActivePlatforms } from "../tools.mjs";
|
|
16
16
|
import { ALL_PLATFORMS } from "../adapters.mjs";
|
|
17
|
+
import { cascade, CLASS_TIERS, UNCLASSED } from "../cascade.mjs";
|
|
17
18
|
import { freshVerifierPillars } from "../tokenpull.mjs";
|
|
18
19
|
import { isSignedIn, isCodeChar } from "../connect.mjs";
|
|
19
20
|
import { loadIdentity, clearIdentity } from "../keystore.mjs";
|
|
@@ -21,17 +22,12 @@ import { execFile } from "child_process";
|
|
|
21
22
|
import { existsSync, readFileSync } from "fs";
|
|
22
23
|
import os from "os";
|
|
23
24
|
import path from "path";
|
|
24
|
-
import {
|
|
25
|
+
import { pkgVersion } from "../lib/pkg-version.mjs";
|
|
26
|
+
import { LEADERBOARD_METRIC } from "../lib/constants.mjs";
|
|
25
27
|
|
|
26
|
-
// Version read from
|
|
27
|
-
// never hardcode, that's what caused the version drift).
|
|
28
|
-
const VERSION = (
|
|
29
|
-
try {
|
|
30
|
-
return createRequire(import.meta.url)("./package.json").version;
|
|
31
|
-
} catch {
|
|
32
|
-
return "?";
|
|
33
|
-
}
|
|
34
|
-
})();
|
|
28
|
+
// Version read from the shared pkg-version helper (single source of truth —
|
|
29
|
+
// matches cli.mjs; never hardcode, that's what caused the version drift).
|
|
30
|
+
const VERSION = pkgVersion();
|
|
35
31
|
|
|
36
32
|
// ── ANSI ───────────────────────────────────────────────────────────────────
|
|
37
33
|
const ESC = "\x1b[";
|
|
@@ -205,6 +201,9 @@ const fmtMov = (n) =>
|
|
|
205
201
|
n == null || n === 0 ? dim("—") : n > 0 ? green(`+${n}`) : red(`${n}`);
|
|
206
202
|
|
|
207
203
|
// ── Class tier colors ────────────────────────────────────────────────────────
|
|
204
|
+
// Built from the canonical CLASS_TIERS list (single source of truth) so a new
|
|
205
|
+
// tier added in analytics/cascade.mjs can't silently lack a color here. The
|
|
206
|
+
// dead BEARER entry (no tier by that name exists) was removed.
|
|
208
207
|
const CLS = {
|
|
209
208
|
TRANSMITTER: (s) => paint(c.boldGold, s),
|
|
210
209
|
"ARCH+": (s) => paint(c.boldCyan, s),
|
|
@@ -213,37 +212,30 @@ const CLS = {
|
|
|
213
212
|
BASE: (s) => paint(c.white, s),
|
|
214
213
|
SEEKER: (s) => paint(c.magenta, s),
|
|
215
214
|
REFINER: (s) => paint(c.blue, s),
|
|
216
|
-
BEARER: (s) => paint(c.dim, s),
|
|
217
215
|
IGNITER: (s) => paint(c.dim, s),
|
|
216
|
+
[UNCLASSED]: (s) => paint(c.dim, s),
|
|
218
217
|
};
|
|
219
218
|
const colorCls = (cls) => (CLS[cls] ?? ((s) => s))(cls);
|
|
220
219
|
|
|
221
|
-
//
|
|
220
|
+
// Guard: every canonical tier must have a color, so a new tier added in
|
|
221
|
+
// analytics/cascade.mjs can't silently render uncolored here.
|
|
222
|
+
for (const t of CLASS_TIERS) {
|
|
223
|
+
if (!CLS[t]) {
|
|
224
|
+
// eslint-disable-next-line no-console
|
|
225
|
+
console.warn(`[tui] CLS map missing color for tier "${t}"`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// ── Cascade math ───────────────────────────────────────────────────────────
|
|
230
|
+
// Fix 3: the inline cascadeFrom() (a third copy of the cascade math with its
|
|
231
|
+
// own null-guard policy) is replaced by the single shared cascade() from
|
|
232
|
+
// analytics/cascade.mjs. CLI, TUI, and MCP tools now report identical Υ for
|
|
233
|
+
// identical tokens. cascadeFrom() keeps the old name as a thin alias so the
|
|
234
|
+
// many call sites below don't need rewriting; it forwards to the shared
|
|
235
|
+
// implementation and returns null only when there are no pillars at all.
|
|
222
236
|
function cascadeFrom(p) {
|
|
223
237
|
if (!p) return null;
|
|
224
|
-
|
|
225
|
-
o = p.output ?? 0,
|
|
226
|
-
cr = p.cacheRead ?? 0;
|
|
227
|
-
if (i === 0 || o === 0) return null;
|
|
228
|
-
const leverage = cr / i;
|
|
229
|
-
const velocity = o / i;
|
|
230
|
-
const yld = leverage * velocity;
|
|
231
|
-
const dev10x =
|
|
232
|
-
i > 0 && o > 0 && (p.cacheCreate ?? 0) > 0 && cr > 0
|
|
233
|
-
? Math.log10(cr / i)
|
|
234
|
-
: null;
|
|
235
|
-
const snr = o / (i + o);
|
|
236
|
-
|
|
237
|
-
let cls = "IGNITER";
|
|
238
|
-
if (yld >= 1000 || dev10x >= 3) cls = "TRANSMITTER";
|
|
239
|
-
else if (dev10x != null && dev10x >= 1.45) cls = "ARCH+";
|
|
240
|
-
else if (dev10x != null && dev10x >= 1.35) cls = "ARCH";
|
|
241
|
-
else if (dev10x != null && dev10x >= 1.2) cls = "POWER";
|
|
242
|
-
else if (dev10x != null && dev10x >= 1.0) cls = "BASE";
|
|
243
|
-
else if (dev10x != null && dev10x >= 0) cls = "SEEKER";
|
|
244
|
-
else if (dev10x != null && dev10x >= -0.3) cls = "REFINER";
|
|
245
|
-
|
|
246
|
-
return { yield: yld, snr, leverage, velocity, dev10x, class: cls };
|
|
238
|
+
return cascade(p);
|
|
247
239
|
}
|
|
248
240
|
|
|
249
241
|
// ── Unicode bar chart (no dep) ───────────────────────────────────────────────
|
|
@@ -309,7 +301,7 @@ function sparkline(values) {
|
|
|
309
301
|
|
|
310
302
|
async function loadDashboardData() {
|
|
311
303
|
const boardPromise = Promise.race([
|
|
312
|
-
fetch(`${DEFAULT_API_BASE}/api/v1/leaderboard?window=30d&metric
|
|
304
|
+
fetch(`${DEFAULT_API_BASE}/api/v1/leaderboard?window=30d&metric=${LEADERBOARD_METRIC}`, {
|
|
313
305
|
headers: { accept: "application/json" },
|
|
314
306
|
})
|
|
315
307
|
.then((r) => (r.ok ? r.json() : null))
|
|
@@ -386,7 +378,7 @@ async function loadBoardData(window = "30d") {
|
|
|
386
378
|
// Primary: submissions endpoint (ranked submission rows).
|
|
387
379
|
try {
|
|
388
380
|
const res = await fetch(
|
|
389
|
-
`${DEFAULT_API_BASE}/api/v1/submissions?window=${window}&metric
|
|
381
|
+
`${DEFAULT_API_BASE}/api/v1/submissions?window=${window}&metric=${LEADERBOARD_METRIC}`,
|
|
390
382
|
{
|
|
391
383
|
headers: { accept: "application/json" },
|
|
392
384
|
},
|
|
@@ -401,7 +393,7 @@ async function loadBoardData(window = "30d") {
|
|
|
401
393
|
// Fallback: existing leaderboard endpoint + current render (pre-deploy safety).
|
|
402
394
|
try {
|
|
403
395
|
const res = await fetch(
|
|
404
|
-
`${DEFAULT_API_BASE}/api/v1/leaderboard?window=${window}&metric
|
|
396
|
+
`${DEFAULT_API_BASE}/api/v1/leaderboard?window=${window}&metric=${LEADERBOARD_METRIC}`,
|
|
405
397
|
{
|
|
406
398
|
headers: { accept: "application/json" },
|
|
407
399
|
},
|
|
@@ -796,7 +788,12 @@ function renderDashboard(data, status = "", scrollOffset = 0) {
|
|
|
796
788
|
const wk = firstWin[d.platform];
|
|
797
789
|
const wd = d.windows?.find((x) => x.window === wk);
|
|
798
790
|
const cas = wd && cascadeFrom(wd.pillars);
|
|
799
|
-
|
|
791
|
+
// cascadeFrom returns null only when wd.pillars is falsy; otherwise it
|
|
792
|
+
// forwards to cascade() which always returns an object (degenerate input
|
|
793
|
+
// surfaces as null fields + warnings[], not a null return). The guard
|
|
794
|
+
// below skips rows with no pillars at all; downstream `chk(val == null)`
|
|
795
|
+
// guards handle degenerate-but-present pillars.
|
|
796
|
+
if (cas) return cas;
|
|
800
797
|
}
|
|
801
798
|
return null;
|
|
802
799
|
})();
|
package/resources/class-tiers.md
CHANGED
|
@@ -1,23 +1,62 @@
|
|
|
1
1
|
# SigRank Class Tiers
|
|
2
2
|
|
|
3
|
-
Every operator is classified into one of
|
|
3
|
+
Every operator is classified into one of eight tiers (the **dev10x** taxonomy)
|
|
4
|
+
based on their Yield (Υ = Cache Reads × Output / Input²) and 10xDEV score
|
|
5
|
+
(`log10(T × C × R)` where T = output/input, C = cacheCreate/output,
|
|
6
|
+
R = cacheRead/cacheCreate). The classifier is `classify()` in
|
|
7
|
+
`analytics/cascade.mjs` — the canonical source of truth. The TUI, CLI, MCP
|
|
8
|
+
output schemas, and this doc all read from that one list.
|
|
4
9
|
|
|
5
|
-
|
|
10
|
+
A ninth value, **UNCLASSED**, is returned when both Υ and 10xDEV are null
|
|
11
|
+
(empty / all-zero session). It is distinct from IGNITER so callers can tell
|
|
12
|
+
"no data" from "bottom tier".
|
|
13
|
+
|
|
14
|
+
## TRANSMITTER (Υ ≥ 1000 or 10xDEV ≥ 3)
|
|
15
|
+
- **Profile:** Cascade-optimized operators. High cache reads, minimal input, efficient output.
|
|
16
|
+
- **Behavior:** Surgical — minimal new input, maximum cache reuse, high-yield output.
|
|
17
|
+
- **Typical:** Power users with long-running sessions, tight context windows, and aggressive cache strategies.
|
|
18
|
+
- **Maintain:** Keep cache hit rate high. Avoid context bloat. Monitor with watch_tokenpull.
|
|
19
|
+
|
|
20
|
+
## ARCH+ (10xDEV ≥ 1.45)
|
|
21
|
+
- **Profile:** High-leverage architect. Strong compounding loop.
|
|
22
|
+
- **Behavior:** Builds forward aggressively while reusing prior context.
|
|
23
|
+
- **Fix:** Push cache reads higher to cross into TRANSMITTER.
|
|
24
|
+
|
|
25
|
+
## ARCH (1.35 ≤ 10xDEV < 1.45)
|
|
26
|
+
- **Profile:** Established architect. Solid compounding, room to tighten.
|
|
27
|
+
- **Behavior:** Reliable reuse + generation balance.
|
|
28
|
+
- **Fix:** Increase cacheCreate quality (better summaries, tighter prompts) to reach ARCH+.
|
|
29
|
+
|
|
30
|
+
## POWER (1.2 ≤ 10xDEV < 1.35)
|
|
31
|
+
- **Profile:** Productive operator with a forming compounding loop.
|
|
32
|
+
- **Behavior:** Moderate cache leverage, decent output efficiency.
|
|
33
|
+
- **Fix:** Reuse sessions more aggressively to lift cache reads.
|
|
34
|
+
|
|
35
|
+
## BASE (1.0 ≤ 10xDEV < 1.2)
|
|
36
|
+
- **Profile:** Balanced operator. Some cache, decent output efficiency.
|
|
37
|
+
- **Behavior:** Productive — reasonable input-to-output ratio, some cache leverage.
|
|
38
|
+
- **Typical:** Experienced AI coders who use CLAUDE.md, project context, and session continuity.
|
|
39
|
+
- **Fix:** Increase cache reads by reusing sessions. Reduce input by trimming unnecessary context.
|
|
40
|
+
|
|
41
|
+
## SEEKER (0 ≤ 10xDEV < 1.0)
|
|
42
|
+
- **Profile:** Learning operator. Compounding loop not yet formed.
|
|
43
|
+
- **Behavior:** Exploring — input-heavy, building toward reuse.
|
|
44
|
+
- **Fix:** Commit context to cache (longer sessions, --continue) to start compounding.
|
|
45
|
+
|
|
46
|
+
## REFINER (-0.3 ≤ 10xDEV < 0)
|
|
6
47
|
- **Profile:** Raw volume operators. High input tokens, low cache reuse.
|
|
7
48
|
- **Behavior:** Brute-force — lots of context fed in, relatively little output back.
|
|
8
49
|
- **Typical:** New AI users, verbose prompters, no session continuity.
|
|
9
50
|
- **Fix:** Build cache across sessions. Stop re-explaining context. Use --continue.
|
|
10
51
|
|
|
11
|
-
##
|
|
12
|
-
- **Profile:**
|
|
13
|
-
- **Behavior:**
|
|
14
|
-
- **
|
|
15
|
-
- **Fix:** Increase cache reads by reusing sessions. Reduce input by trimming unnecessary context.
|
|
52
|
+
## IGNITER (10xDEV < -0.3)
|
|
53
|
+
- **Profile:** Sub-baseline. Negative compounding — context is being lost faster than it's built.
|
|
54
|
+
- **Behavior:** Each turn re-ingests more than it reuses; the loop runs in reverse.
|
|
55
|
+
- **Fix:** Stop starting fresh sessions. Commit context once and reuse it.
|
|
16
56
|
|
|
17
|
-
##
|
|
18
|
-
- **Profile:**
|
|
19
|
-
- **Behavior:**
|
|
20
|
-
- **
|
|
21
|
-
- **Maintain:** Keep cache hit rate high. Avoid context bloat. Monitor with watch_tokenpull.
|
|
57
|
+
## UNCLASSED (no data)
|
|
58
|
+
- **Profile:** Empty session — all four pillars are zero.
|
|
59
|
+
- **Behavior:** No tokens recorded for this window.
|
|
60
|
+
- **Fix:** Run some sessions; the tier will resolve once there is data.
|
|
22
61
|
|
|
23
62
|
Tiers are recalculated on every submission. Your tier can change between windows (7d, 30d, 90d, all-time).
|
|
@@ -2,20 +2,23 @@
|
|
|
2
2
|
|
|
3
3
|
## What leaves your machine
|
|
4
4
|
|
|
5
|
-
**
|
|
5
|
+
**Every submission path sends only four numbers:**
|
|
6
6
|
- Input tokens
|
|
7
7
|
- Output tokens
|
|
8
8
|
- Cache create tokens
|
|
9
9
|
- Cache read tokens
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
That's the entire payload — no prompts, no code, no file contents, no conversation text. The MCP parses your input locally and sends only the four token counts to the server, which re-parses and re-scores them authoritatively.
|
|
12
|
+
|
|
13
|
+
- **Signed path** (`submit_verified`, `watch_tokenpull` with `submit:true`): the four numbers travel with the device's public key, codename, window, and an ed25519 signature. The board verifies the signature without seeing your data.
|
|
14
|
+
- **Paste path** (`submit_paste`, `tokenpull_submit`): the MCP parses your pasted token counts locally, then sends only the four canonical numbers to the server's web-paste endpoint. Even if you paste mixed text (prose + numbers), only the extracted token counts are transmitted — the raw text never leaves your machine.
|
|
12
15
|
|
|
13
16
|
## How it works
|
|
14
17
|
|
|
15
18
|
1. **Local-first:** All token pulling happens on your machine. SigRank reads session logs from ~/.claude, ~/.codex, ~/.local/share/amp, etc.
|
|
16
|
-
2. **Token counts only:** The MCP tools extract integer counts from log metadata. The actual content of your conversations is never read, parsed, or transmitted.
|
|
17
|
-
3. **Signed submission:**
|
|
18
|
-
4. **
|
|
19
|
+
2. **Token counts only:** The MCP tools extract integer counts from log metadata. The actual content of your conversations is never read, parsed, or transmitted. On the paste path, the MCP parses the paste locally and sends only the four extracted numbers — the raw paste text stays on your machine.
|
|
20
|
+
3. **Signed submission:** Ranked submissions (`submit_verified`, `watch_tokenpull` with `submit:true`) are ed25519-signed with a device-bound key generated locally at enrollment. The board verifies the signature without seeing your data. Paste submissions (`submit_paste`, `tokenpull_submit`) are unsigned and go through the web-paste endpoint with a codename only — but still send only the four token counts, not the raw paste.
|
|
21
|
+
4. **Read tools need no auth:** No API keys, no OAuth, no account needed to read the leaderboard or operator profiles. **Enrollment requires a connect code** from signalaf.com → Settings → New key (the code binds your device's public key to your operator server-side); a codename alone is not enough for the signed/ranked path.
|
|
19
22
|
|
|
20
23
|
## What SigRank can NOT see
|
|
21
24
|
|
package/submit/index.mjs
CHANGED
|
@@ -11,22 +11,15 @@
|
|
|
11
11
|
|
|
12
12
|
import { snapshotHash, signPayload } from "../identity/sign.mjs";
|
|
13
13
|
import { preflight } from "./preflight.mjs";
|
|
14
|
-
import {
|
|
14
|
+
import { pkgVersion } from "../lib/pkg-version.mjs";
|
|
15
|
+
import {
|
|
16
|
+
TERMS_VERSION,
|
|
17
|
+
PRIVACY_VERSION,
|
|
18
|
+
isRankedAck,
|
|
19
|
+
} from "../lib/constants.mjs";
|
|
15
20
|
|
|
16
21
|
const WINDOW_TYPE = { "7d": "7d", "30d": "30d", "90d": "90d", all: "all_time" };
|
|
17
22
|
const WINDOW_SPAN_DAYS = { "7d": 7, "30d": 30, "90d": 90, all_time: 3650 };
|
|
18
|
-
|
|
19
|
-
/** Resolve this package's version for the User-Agent stamp (best-effort). */
|
|
20
|
-
function _pkgVersion() {
|
|
21
|
-
try {
|
|
22
|
-
const pkg = JSON.parse(
|
|
23
|
-
readFileSync(new URL("./package.json", import.meta.url), "utf-8"),
|
|
24
|
-
);
|
|
25
|
-
return pkg.version;
|
|
26
|
-
} catch {
|
|
27
|
-
return "unknown";
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
23
|
const PLATFORM_ENUM = new Set([
|
|
31
24
|
"claude",
|
|
32
25
|
"chatgpt",
|
|
@@ -38,8 +31,6 @@ const PLATFORM_ENUM = new Set([
|
|
|
38
31
|
]);
|
|
39
32
|
// Provenance tag only — the board ranks by Υ (no RS.xx weights, §0.1).
|
|
40
33
|
const RULESET_VERSION = "sigrank-token-1";
|
|
41
|
-
const TERMS_VERSION = "2026-07-21";
|
|
42
|
-
const PRIVACY_VERSION = "2026-07-21";
|
|
43
34
|
const DAY_MS = 86_400_000;
|
|
44
35
|
|
|
45
36
|
const clamp = (x, lo, hi) => Math.max(lo, Math.min(hi, x));
|
|
@@ -200,7 +191,7 @@ export async function submitSignedWindow(
|
|
|
200
191
|
const ctrl = new AbortController();
|
|
201
192
|
const timer = setTimeout(() => ctrl.abort(), 15_000);
|
|
202
193
|
const headers = {
|
|
203
|
-
"user-agent": `node/${process.version} sigrank-mcp/${
|
|
194
|
+
"user-agent": `node/${process.version} sigrank-mcp/${pkgVersion()}`,
|
|
204
195
|
...(init.headers || {}),
|
|
205
196
|
};
|
|
206
197
|
return fetch(url, {
|
|
@@ -296,11 +287,7 @@ export async function submitSignedWindow(
|
|
|
296
287
|
persisted: ack.persisted ?? null,
|
|
297
288
|
// ranked = actually on the board (verified + written), not just "received".
|
|
298
289
|
// An unenrolled/revoked device gets HTTP 202 received but is NEVER ranked.
|
|
299
|
-
ranked:
|
|
300
|
-
res.ok &&
|
|
301
|
-
ack.verification_tier === "verified" &&
|
|
302
|
-
ack.persisted === true
|
|
303
|
-
),
|
|
290
|
+
ranked: isRankedAck(res, ack),
|
|
304
291
|
snapshot_hash: payload.agent.snapshot_hash,
|
|
305
292
|
reason: res.ok ? null : ack.reason || ack.status || `http_${res.status}`,
|
|
306
293
|
detail: ack.detail ?? null,
|
package/tokscale_analytics.mjs
CHANGED
|
@@ -33,6 +33,7 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
33
33
|
import os from "node:os";
|
|
34
34
|
import path from "node:path";
|
|
35
35
|
import { fileURLToPath } from "node:url";
|
|
36
|
+
import { TOKSCALE_CLIENT_MAP } from "./lib/constants.mjs";
|
|
36
37
|
|
|
37
38
|
// Resolve local node_modules/.bin for the bundled tokscale binary.
|
|
38
39
|
const _pkgRoot = path.dirname(fileURLToPath(import.meta.url));
|
|
@@ -85,23 +86,11 @@ export function num(x) {
|
|
|
85
86
|
return Number.isFinite(n) ? n : 0;
|
|
86
87
|
}
|
|
87
88
|
|
|
88
|
-
// Map tokscale's client slugs to canonical platform names
|
|
89
|
+
// Map tokscale's client slugs to canonical platform names.
|
|
90
|
+
// TOKSCALE_CLIENT_MAP is imported from lib/constants.mjs (single source of
|
|
91
|
+
// truth — was duplicated here and in tools/index.mjs +
|
|
92
|
+
// tools/tokscale-breakdown.mjs, and the copies had started to drift).
|
|
89
93
|
// `synthetic` → null (filtered). unmapped slugs → "other".
|
|
90
|
-
const TOKSCALE_CLIENT_MAP = {
|
|
91
|
-
claude: "claude", codex: "codex", "devin-cli": "devin",
|
|
92
|
-
"devin-desktop": "devin", gemini: "gemini", amp: "amp",
|
|
93
|
-
kimi: "kimi", qwen: "qwen", goose: "goose", kilo: "kilo",
|
|
94
|
-
kilocode: "kilo", hermes: "hermes", droid: "droid",
|
|
95
|
-
codebuff: "codebuff", copilot: "copilot", opencode: "opencode",
|
|
96
|
-
openclaw: "openclaw", pi: "pi",
|
|
97
|
-
cursor: "other", roocode: "other", mux: "other", crush: "other",
|
|
98
|
-
antigravity: "other", "antigravity-cli": "other", zed: "other",
|
|
99
|
-
kiro: "other", trae: "other", warp: "other", cline: "other",
|
|
100
|
-
gjc: "other", grok: "other", jcode: "other", commandcode: "other",
|
|
101
|
-
micode: "other", junie: "other", zcode: "other",
|
|
102
|
-
opencodereview: "other", codebuddy: "other", workbuddy: "other",
|
|
103
|
-
synthetic: null,
|
|
104
|
-
};
|
|
105
94
|
|
|
106
95
|
/** Human-readable label for a tokscale client slug. */
|
|
107
96
|
const CLIENT_LABELS = {
|