sigrank 1.0.4 → 1.0.6

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 CHANGED
@@ -1,17 +1,16 @@
1
- # SigRank SignalAF MCP
1
+ # Upsilon Measurement Engine — `sigrank` CLI/MCP
2
2
 
3
- > **🏆 SigRank SignalAF is live: [signalaf.com](https://signalaf.com)** — the AI operator
4
- > evaluation leaderboard. Ranks how efficiently you use AI by token cascade
5
- > efficiency (Υ = cache_read × output / input²), not how much. Run `npx sigrank`
6
- > to see your cascade now. _Token counts only. Never your prompts._
3
+ > **Upsilon** is SignalAF's measurement engine. **SigRank** is the public leaderboard
4
+ > and proof surface. The existing `sigrank` package name and `sigrank/0.1-draft`
5
+ > record remain stable for compatibility. _Four token counts only. Never prompts or code._
7
6
 
8
7
  <div align="center">
9
8
 
10
9
  <p><img src="./.github/assets/og-card.png" alt="SigRank SignalAF — AI operator evaluation and token cascade efficiency leaderboard" width="800"></p>
11
10
 
12
- **The yield cascade + live leaderboard as MCP tools any agent can call.**
11
+ **Upsilon measures. SigRank proves. MO§ES™ governs. SignalAF is the brand.**
13
12
 
14
- SigRank evaluates the human using the AI — not the AI model itself.
13
+ Upsilon describes observable AI operator token-processing patterns—not AI model quality, cognition, work quality, employee productivity, or business value.
15
14
 
16
15
  [![npm version](https://img.shields.io/npm/v/sigrank.svg?style=flat-square&color=gold)](https://www.npmjs.com/package/sigrank)
17
16
  [![CI](https://github.com/SunrisesIllNeverSee/sigrank-mcp/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/SunrisesIllNeverSee/sigrank-mcp/actions/workflows/ci.yml)
@@ -32,7 +31,7 @@ SigRank evaluates the human using the AI — not the AI model itself.
32
31
 
33
32
  ## Table of Contents
34
33
 
35
- - [The SigRank SignalAF ecosystem](#the-sigrank-signalaf-ecosystem)
34
+ - [SignalAF product architecture](#signalaf-product-architecture)
36
35
  - [Quickstart](#quickstart--3-steps-to-the-board)
37
36
  - [Install from GitHub](#install-from-github)
38
37
  - [Install via Smithery](#install-via-smithery)
@@ -57,12 +56,16 @@ SigRank evaluates the human using the AI — not the AI model itself.
57
56
 
58
57
  ---
59
58
 
60
- ## The SigRank SignalAF ecosystem
59
+ ## SignalAF product architecture
60
+
61
+ ```text
62
+ MO§ES™ governance → Upsilon measurement engine → SigRank public proof
63
+ ```
61
64
 
62
65
  | Repo | What it is | Install |
63
66
  |------|-----------|---------|
64
- | **[sigrank-mcp](https://github.com/SunrisesIllNeverSee/sigrank-mcp)** (this repo) | The instrument — extracts 4 token pillars, computes the cascade, submits to the leaderboard. MCP server + TUI dashboard. | `npx sigrank` |
65
- | **[sigrank-app](https://github.com/SunrisesIllNeverSee/sigrank-app)** | The leaderboard — signalaf.com. Privacy-preserving operator profiles, class tiers, board rankings. | [signalaf.com](https://signalaf.com) |
67
+ | **[sigrank-mcp](https://github.com/SunrisesIllNeverSee/sigrank-mcp)** (this repo) | Upsilon's compatibility instrument—extracts four token pillars, computes locally, and optionally submits to SigRank. | `npx sigrank` |
68
+ | **[sigrank-app](https://github.com/SunrisesIllNeverSee/sigrank-app)** | Upsilon web implementation plus the SigRank leaderboard/proof surface. | [signalaf.com](https://signalaf.com) |
66
69
  | **[bestuser-router-mcp](https://github.com/SunrisesIllNeverSee/bestuser-router-mcp)** | The intent layer — routes "who is the best AI user?" queries to SigRank SignalAF's leaderboard. MCP server for AI assistants. | `npx bestuser-router-mcp` |
67
70
  | **[sigarena](https://github.com/SunrisesIllNeverSee/sigarena)** | The satellite — public LLM operator evals at sigeconomy.com. Read-only leaderboard, SEO/AEO surface. | [sigeconomy.com](https://sigeconomy.com) |
68
71
  | **[signaf](https://github.com/SunrisesIllNeverSee/signa)** | The coach — reads your session logs, builds a taste profile, measures ASI, coaches you on token efficiency. | `npx @burnmydays/signaf` |
@@ -74,7 +77,7 @@ SigRank evaluates the human using the AI — not the AI model itself.
74
77
  | Site | What it is |
75
78
  | ---- | ---------- |
76
79
  | **[SIGNOMY](https://signomy.xyz)** | Governed AI agent marketplace where ranked agents form teams, fill slots, run missions, and earn revenue under constitutional protocol. Agents are free. Operators pay. |
77
- | **[MO§ES](https://mos2es.com)** | The governance framework that underpins SigRank SignalAF, SIGNOMY, and all governed agent operations. Structural accountability for agentic systems. |
80
+ | **[MO§ES](https://mos2es.com)** | Constitutional governance and methodology—the law governing Upsilon and other governed systems. |
78
81
 
79
82
  ## Quickstart — 3 steps to the board
80
83
 
package/bin/sigrank.mjs CHANGED
@@ -30,6 +30,12 @@ function printStandard() {
30
30
  const payload = {
31
31
  spec: SPEC,
32
32
  status: "proposed_open_standard",
33
+ architecture: {
34
+ brand: "SignalAF",
35
+ governance: "MO§ES™",
36
+ product: "Upsilon",
37
+ leaderboard: "SigRank",
38
+ },
33
39
  scope: "human operation of generative AI systems",
34
40
  telemetry: ["input", "output", "cache_write", "cache_read"],
35
41
  metrics: {
@@ -42,7 +48,7 @@ function printStandard() {
42
48
  privacy:
43
49
  "Core metrics do not require prompt text, response text, source code, or repository contents.",
44
50
  reference_math: "@sigrank/cascade",
45
- reference_platform: "SignalAF",
51
+ reference_platform: "Upsilon on SignalAF",
46
52
  standard_url: STANDARD_URL,
47
53
  schema_url: SCHEMA_URL,
48
54
  };
@@ -52,13 +58,13 @@ function printStandard() {
52
58
  return;
53
59
  }
54
60
 
55
- process.stdout.write(`SigRank Standard ${SPEC}\n`);
56
- process.stdout.write(`The new standard in operator metrics.\n\n`);
61
+ process.stdout.write(`Upsilon measurement engine · compatibility spec ${SPEC}\n`);
62
+ process.stdout.write(`Upsilon measures. SigRank is the public proof surface.\n\n`);
57
63
  process.stdout.write(`Scope: ${payload.scope}\n`);
58
64
  process.stdout.write(`Primitives: input / output / cache_write / cache_read\n`);
59
65
  process.stdout.write(`Core metrics: Yield / Leverage / Velocity / SNR / 10xDEV\n`);
60
66
  process.stdout.write(`Reference math: @sigrank/cascade\n`);
61
- process.stdout.write(`Reference platform: SignalAF\n`);
67
+ process.stdout.write(`Reference platform: Upsilon on SignalAF\n`);
62
68
  process.stdout.write(`Spec: ${STANDARD_URL}\n`);
63
69
  process.stdout.write(`Schema: ${SCHEMA_URL}\n`);
64
70
  }
@@ -69,9 +75,13 @@ async function exportStandardRecord() {
69
75
  const platform = flag("platform", "claude");
70
76
  const window = flag("window", "30d");
71
77
 
72
- const explicit = ["input", "output", "cache-write", "cache-read"].every(
73
- (name) => flag(name) !== undefined,
74
- );
78
+ const explicitFlags = ["input", "output", "cache-write", "cache-read"];
79
+ const hasExplicit = explicitFlags.some((name) => flag(name) !== undefined);
80
+ const explicit = ["input", "output"].every((name) => flag(name) !== undefined);
81
+
82
+ if (hasExplicit && !explicit) {
83
+ throw new Error("Explicit Standard export requires both --input and --output.");
84
+ }
75
85
 
76
86
  let pillars;
77
87
  let sourcePlatform = platform;
@@ -80,8 +90,8 @@ async function exportStandardRecord() {
80
90
  pillars = {
81
91
  input: Number(flag("input")),
82
92
  output: Number(flag("output")),
83
- cache_write: Number(flag("cache-write")),
84
- cache_read: Number(flag("cache-read")),
93
+ cache_write: flag("cache-write") === undefined ? null : Number(flag("cache-write")),
94
+ cache_read: flag("cache-read") === undefined ? null : Number(flag("cache-read")),
85
95
  };
86
96
  } else {
87
97
  const pulled = await callTool("tokenpull", { platform });
@@ -97,14 +107,14 @@ async function exportStandardRecord() {
97
107
  pillars = {
98
108
  input: selected.pillars.input,
99
109
  output: selected.pillars.output,
100
- cache_write: selected.pillars.cacheCreate,
101
- cache_read: selected.pillars.cacheRead,
110
+ cache_write: selected.pillars.cacheCreate ?? null,
111
+ cache_read: selected.pillars.cacheRead ?? null,
102
112
  };
103
113
  }
104
114
 
105
115
  for (const [key, value] of Object.entries(pillars)) {
106
- if (!Number.isFinite(value) || value < 0) {
107
- throw new Error(`${key} must be a non-negative finite number.`);
116
+ if (value !== null && (!Number.isFinite(value) || !Number.isInteger(value) || value < 0)) {
117
+ throw new Error(`${key} must be a non-negative integer token count or null.`);
108
118
  }
109
119
  }
110
120
 
package/index.mjs CHANGED
@@ -102,7 +102,7 @@ async function startMcpServer() {
102
102
  {
103
103
  name: "check-my-efficiency",
104
104
  description:
105
- "Pull your local token usage, compute your SigRank yield, and compare your efficiency against the leaderboard. Shows your class tier (ARCH+ to IGNITER) and where you rank.",
105
+ "Use Upsilon to pull local token telemetry and compute Yield, then compare the result against the SigRank public leaderboard. Shows your class tier (ARCH+ to IGNITER) and where you rank.",
106
106
  arguments: [],
107
107
  },
108
108
  {
@@ -189,7 +189,7 @@ async function startMcpServer() {
189
189
  const RESOURCES = [
190
190
  {
191
191
  uri: "sigrank://scoring-formula",
192
- name: "SigRank Scoring Formula",
192
+ name: "Upsilon Measurement Formula",
193
193
  description:
194
194
  "The yield cascade formula and all derived metrics: Υ (Yield = Cache Reads × Output / Input²), SNR (signal-to-noise), Leverage (Cr/I), Velocity (O/I), 10xDEV score, and class tier thresholds (8-tier experience ladder, see class-tiers.md).",
195
195
  mimeType: "text/markdown",
@@ -210,16 +210,16 @@ async function startMcpServer() {
210
210
  },
211
211
  {
212
212
  uri: "sigrank://privacy-model",
213
- name: "Privacy Model",
213
+ name: "Upsilon Privacy Model",
214
214
  description:
215
- "How SigRank protects user privacy: runs locally, only four token counts (input, output, cacheCreate, cacheRead) leave the machine, ed25519-signed submissions, no prompt content ever transmitted.",
215
+ "How Upsilon protects user privacy: runs locally, only four token counts (input, output, cacheCreate, cacheRead) leave the machine, ed25519-signed submissions, no prompt content ever transmitted.",
216
216
  mimeType: "text/markdown",
217
217
  },
218
218
  {
219
219
  uri: "sigrank://data-policy",
220
220
  name: "Data Policy",
221
221
  description:
222
- "Summary of SigRank data governance: what we collect, what we do not collect, consent requirements, and how to pause, delete, or export your data.",
222
+ "Summary of Upsilon measurement-data governance: what is collected, what is not collected, consent requirements, and how to pause, delete, or export data.",
223
223
  mimeType: "text/markdown",
224
224
  },
225
225
  ];
package/manifest.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "$schema": "https://raw.githubusercontent.com/anthropics/mcpb/main/schemas/mcpb-manifest-v0.4.schema.json",
3
3
  "manifest_version": "0.4",
4
4
  "name": "sigrank",
5
- "version": "1.0.3",
5
+ "version": "1.0.5",
6
6
  "description": "Ranks AI coding operators by token cascade efficiency (Yield = cache_read \u00d7 output / input\u00b2). 25 tools including SigRank Standard record, yield calculation, local token usage pulls, signed submission, and tokscale-powered analytics (market share, developer profiles, model trends, cost analysis, device profiles, MCP usage, competitive intelligence). Privacy-first: runs locally, only four token counts leave the machine.",
7
7
  "author": {
8
8
  "name": "SunrisesIllNeverSee",
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "sigrank",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "author": "SunrisesIllNeverSee (https://github.com/SunrisesIllNeverSee)",
5
- "description": "SigRank MCP server — AI operator evaluation and token cascade efficiency tools. The yield cascade + live leaderboard as MCP tools any agent can call.",
5
+ "description": "Upsilon measurement engine distributed through the sigrank CLI/MCP compatibility package, with SigRank public leaderboard tools.",
6
6
  "mcpName": "io.github.SunrisesIllNeverSee/sigrank-mcp",
7
7
  "type": "module",
8
8
  "license": "MIT",
@@ -24,7 +24,7 @@
24
24
  },
25
25
  "scripts": {
26
26
  "start": "node index.mjs",
27
- "test": "node test.mjs && node __tests__/sign.test.mjs && node --test __tests__/cascade.test.mjs __tests__/badges.test.mjs __tests__/tui-input.test.mjs __tests__/standard-record.test.mjs __tests__/standard-cli.test.mjs",
27
+ "test": "node test.mjs && node __tests__/sign.test.mjs && node --test __tests__/cascade.test.mjs __tests__/badges.test.mjs __tests__/tui-input.test.mjs __tests__/standard-record.test.mjs __tests__/standard-cli.test.mjs __tests__/product-architecture.test.mjs",
28
28
  "test:packaged": "node scripts/packed-artifact-smoke.mjs",
29
29
  "sync:platforms": "node scripts/sync-platforms.mjs",
30
30
  "sync:spine": "node scripts/sync-spine.mjs",
@@ -1,17 +1,17 @@
1
- # SigRank Data Policy
1
+ # Upsilon Measurement and SigRank Publication Data Policy
2
2
 
3
- SigRank is a public AI-operator leaderboard. This policy summarizes what data we collect, how we use it, and how you can delete it.
3
+ Upsilon is the measurement engine; SigRank is the public AI-operator leaderboard and proof surface. This policy summarizes what data is collected, how it is used, and how you can delete it.
4
4
 
5
5
  ## What we collect
6
6
 
7
- - **Token telemetry** from your local agent: token counts, content lengths, model identifiers, and platform/source tags. We use these to compute your score on the leaderboard.
7
+ - **Token telemetry** from your local instrument: token counts, model identifiers, and platform/source tags. Upsilon uses these to compute measurements; publication to SigRank is a separate action.
8
8
  - **Account identifiers** when you sign in (email from your OAuth provider) so we can link your operator profile to your login.
9
9
  - **Optional profile fields** you choose to add: display name, handle, bio, location, links, avatar.
10
10
 
11
11
  ## What we never collect
12
12
 
13
13
  We do **not** collect or receive the content of your prompts or AI conversations
14
- at the SigRank service. Local-log adapters extract only numeric usage metadata.
14
+ at the Upsilon service. Local-log adapters extract only numeric usage metadata.
15
15
  If you explicitly run the optional local API proxy, it forwards provider-bound
16
16
  requests and responses in memory but does not persist their content; only usage
17
17
  metadata is written locally or submitted to SigRank.
@@ -1,4 +1,4 @@
1
- # SigRank Privacy Model
1
+ # Upsilon Privacy Model
2
2
 
3
3
  ## What leaves your machine
4
4
 
@@ -10,8 +10,8 @@
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
12
 
13
- The optional `sigrank proxy` is a local transport path to Anthropic/OpenAI, not a
14
- submission path to SigRank. When explicitly enabled, it necessarily receives and
13
+ The optional `sigrank proxy` is a local Upsilon transport path to Anthropic/OpenAI, not a
14
+ submission path to the SigRank leaderboard. When explicitly enabled, it necessarily receives and
15
15
  forwards provider-bound API keys, prompts, tool calls, and responses in memory.
16
16
  It does not persist that content or send it to the SigRank service; it appends
17
17
  only provider-reported token counts, model/backend metadata, and timestamps to
@@ -22,12 +22,12 @@ only provider-reported token counts, model/backend metadata, and timestamps to
22
22
 
23
23
  ## How it works
24
24
 
25
- 1. **Local-first:** All token pulling happens on your machine. SigRank reads session logs from ~/.claude, ~/.codex, ~/.local/share/amp, etc. The optional proxy also runs only on loopback and only when manually started.
25
+ 1. **Local-first:** All token pulling happens on your machine. Upsilon reads usage metadata from session logs in ~/.claude, ~/.codex, ~/.local/share/amp, etc. The optional proxy also runs only on loopback and only when manually started.
26
26
  2. **Token-only persistence:** Local-log adapters extract integer counts from log metadata without reading conversation content. The optional proxy forwards request/response bytes transiently but persists only usage metadata. 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.
27
27
  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.
28
28
  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.
29
29
 
30
- ## What the SigRank service can NOT see
30
+ ## What the Upsilon service can NOT see
31
31
 
32
32
  - Your prompts or messages
33
33
  - Your code or file contents
@@ -35,7 +35,7 @@ only provider-reported token counts, model/backend metadata, and timestamps to
35
35
  - Which AI platform you use (beyond token counts)
36
36
  - Your identity (only your chosen codename)
37
37
 
38
- The local proxy process is different from the SigRank service: if you opt in, it
38
+ The local proxy process is different from the Upsilon service: if you opt in, it
39
39
  handles your provider traffic in memory solely to forward it to Anthropic or
40
40
  OpenAI. That content is never stored in the proxy JSONL file.
41
41
 
@@ -1,4 +1,6 @@
1
- # SigRank Scoring Formula
1
+ # Upsilon Measurement Formulas
2
+
3
+ Upsilon is the measurement engine. SigRank is the public leaderboard and proof surface. The installed package and resource URI retain the `sigrank` name for compatibility.
2
4
 
3
5
  ## Yield (Υ) — the headline metric
4
6
 
@@ -6,19 +8,19 @@
6
8
  Υ = Cache Reads × Output / Input²
7
9
  ```
8
10
 
9
- Yield rewards operators who maximize output while minimizing input — and who build cache to amortize that input across sessions. A high yield means you're getting more done with less, efficiently.
11
+ Yield describes the compound relationship between context reuse and output relative to fresh input. It does not, by itself, prove work quality or productivity.
10
12
 
11
13
  ## Derived Metrics
12
14
 
13
15
  | Metric | Formula | Meaning |
14
16
  |--------|---------|---------|
15
- | **SNR** | Output / (Input + CacheCreate) | Signal-to-noise: how much of your token spend is productive output vs. overhead |
16
- | **Leverage** | Cache Reads / Input | How well you reuse cached context — higher = better cache utilization |
17
- | **Velocity** | Output / Input | Raw output efficiency — how much output you generate per token of input |
18
- | **10xDEV** | Composite score | Weighted blend of yield, leverage, and velocity for cross-platform comparison |
17
+ | **SNR** | Output / (Input + Output) | Output share of the direct input/output exchange |
18
+ | **Leverage** | Cache Reads / Input | Cache-read context relative to fresh input |
19
+ | **Velocity** | Output / Input | Output tokens relative to fresh input |
20
+ | **10xDEV** | log₁₀(Cache Reads / Input) | Log-scale Leverage under the reference null policy |
19
21
 
20
22
  ## Class Tiers
21
23
 
22
- Class tiers are based on **total tokens accumulated**, not yield ranges. The 8-tier experience ladder (descending: ARCH+, ARCH, POWER, BASE, SEEKER, REFINER, BEARER, IGNITER) measures operator progression. TRANSMITTER is a separate peak badge (K.00), not a ladder tier. See `class-tiers.md` for the full ladder and thresholds.
24
+ Class is a SignalAF/SigRank reference qualification layer, not a portable Upsilon metric and not the same thing as archetype or rank. TRANSMITTER is a separate peak badge (K.00), not a ladder tier. See `class-tiers.md` for the current product policy.
23
25
 
24
26
  The formula is deterministic and computed locally. No network calls needed for scoring.
@@ -2,13 +2,20 @@ import { cascade } from "../analytics/cascade.mjs";
2
2
  import { ANNOTATIONS } from "./_schemas.mjs";
3
3
 
4
4
  export const SIGRANK_STANDARD_VERSION = "sigrank/0.1-draft";
5
+ export const PRODUCT_ARCHITECTURE = Object.freeze({
6
+ brand: "SignalAF",
7
+ governance: "MO§ES™",
8
+ product: "Upsilon",
9
+ leaderboard: "SigRank",
10
+ wire_spec: SIGRANK_STANDARD_VERSION,
11
+ });
5
12
 
6
13
  export const TOOL_DEF = {
7
14
  name: "get_sigrank_standard_record",
8
15
  description:
9
- "Build a SigRank Standard v0.1-draft portable operator record from four token pillars. Computes the canonical cascade locally through @sigrank/cascade and returns versioned telemetry plus the normative core metrics: Yield, Leverage, Velocity, SNR, and 10xDEV. No data is submitted or persisted. The core record contains token telemetry only and does not require prompt text, code, or response content.",
16
+ "Build Upsilon's portable sigrank/0.1-draft compatibility record from available token telemetry. Input and output are required; unavailable cache telemetry remains null. Computes the canonical cascade locally through @sigrank/cascade and returns Yield, Leverage, Velocity, SNR, and 10xDEV. Upsilon is the measurement product; SigRank is the public leaderboard. No data is submitted or persisted.",
10
17
  annotations: {
11
- title: "Export SigRank standard record",
18
+ title: "Export Upsilon measurement record",
12
19
  ...ANNOTATIONS.readOnlyHint,
13
20
  ...ANNOTATIONS.destructiveHint,
14
21
  ...ANNOTATIONS.idempotentHint,
@@ -17,26 +24,29 @@ export const TOOL_DEF = {
17
24
  inputSchema: {
18
25
  type: "object",
19
26
  additionalProperties: false,
20
- required: ["input", "output", "cache_write", "cache_read"],
27
+ required: ["input", "output"],
21
28
  properties: {
22
- input: { type: "number", minimum: 0, description: "Fresh input tokens." },
23
- output: { type: "number", minimum: 0, description: "Output tokens." },
24
- cache_write: { type: "number", minimum: 0, description: "Cache-write / cache-creation tokens." },
25
- cache_read: { type: "number", minimum: 0, description: "Cache-read tokens." },
29
+ input: { type: "integer", minimum: 0, description: "Fresh input tokens." },
30
+ output: { type: "integer", minimum: 0, description: "Output tokens." },
31
+ cache_write: { type: ["integer", "null"], minimum: 0, description: "Cache-write / cache-creation tokens, or null when unavailable." },
32
+ cache_read: { type: ["integer", "null"], minimum: 0, description: "Cache-read tokens, or null when unavailable." },
26
33
  provider: { type: "string", description: "Optional provider identifier." },
27
34
  model: { type: "string", description: "Optional model identifier." },
28
35
  tool: { type: "string", description: "Optional tool/client identifier." },
29
- timestamp: { type: "string", description: "Optional ISO-8601 timestamp. Defaults to the current time." }
36
+ timestamp: { type: "string", format: "date-time", description: "Optional ISO-8601 timestamp. Defaults to the current time." }
30
37
  }
31
38
  },
32
39
  outputSchema: {
33
40
  type: "object",
34
- required: ["spec", "timestamp", "source", "telemetry", "metrics"],
41
+ additionalProperties: false,
42
+ required: ["spec", "timestamp", "source", "telemetry", "metrics", "warnings"],
35
43
  properties: {
36
- spec: { type: "string", description: "SigRank specification version." },
37
- timestamp: { type: "string", description: "ISO-8601 record timestamp." },
44
+ spec: { const: SIGRANK_STANDARD_VERSION, description: "SigRank specification version." },
45
+ timestamp: { type: "string", format: "date-time", description: "ISO-8601 record timestamp." },
38
46
  source: {
39
47
  type: "object",
48
+ additionalProperties: false,
49
+ required: ["provider", "model", "tool"],
40
50
  properties: {
41
51
  provider: { type: "string" },
42
52
  model: { type: "string" },
@@ -45,15 +55,19 @@ export const TOOL_DEF = {
45
55
  },
46
56
  telemetry: {
47
57
  type: "object",
58
+ additionalProperties: false,
59
+ required: ["input", "output", "cache_write", "cache_read"],
48
60
  properties: {
49
- input: { type: "number" },
50
- output: { type: "number" },
51
- cache_write: { type: "number" },
52
- cache_read: { type: "number" }
61
+ input: { type: "integer", minimum: 0 },
62
+ output: { type: "integer", minimum: 0 },
63
+ cache_write: { type: ["integer", "null"], minimum: 0 },
64
+ cache_read: { type: ["integer", "null"], minimum: 0 }
53
65
  }
54
66
  },
55
67
  metrics: {
56
68
  type: "object",
69
+ additionalProperties: false,
70
+ required: ["yield", "leverage", "velocity", "snr", "dev10x"],
57
71
  properties: {
58
72
  yield: { type: ["number", "null"] },
59
73
  leverage: { type: ["number", "null"] },
@@ -67,24 +81,48 @@ export const TOOL_DEF = {
67
81
  }
68
82
  };
69
83
 
70
- export async function handleGetSigRankStandardRecord(args) {
71
- const input = Number(args?.input);
72
- const output = Number(args?.output);
73
- const cacheWrite = Number(args?.cache_write);
74
- const cacheRead = Number(args?.cache_read);
84
+ const ISO_DATE_TIME =
85
+ /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}(?:\.\d+)?(?:Z|[+-]\d{2}:\d{2})$/;
75
86
 
76
- if (![input, output, cacheWrite, cacheRead].every(Number.isFinite)) {
77
- throw new Error("get_sigrank_standard_record requires four finite numeric token pillars.");
87
+ function tokenCount(value, name, required) {
88
+ if (value === null || value === undefined) {
89
+ if (required) throw new Error(`${name} is required.`);
90
+ return null;
78
91
  }
79
- if ([input, output, cacheWrite, cacheRead].some((v) => v < 0)) {
80
- throw new Error("get_sigrank_standard_record requires non-negative token pillars.");
92
+ if (typeof value !== "number" || !Number.isFinite(value) || !Number.isInteger(value)) {
93
+ throw new Error(`${name} must be a non-negative integer token count.`);
94
+ }
95
+ if (value < 0) throw new Error(`${name} must be a non-negative integer token count.`);
96
+ return value;
97
+ }
98
+
99
+ function recordTimestamp(value) {
100
+ if (value === undefined) return new Date().toISOString();
101
+ if (typeof value !== "string" || !ISO_DATE_TIME.test(value) || !Number.isFinite(Date.parse(value))) {
102
+ throw new Error("timestamp must be a valid ISO-8601 date-time.");
81
103
  }
104
+ return value;
105
+ }
106
+
107
+ export async function handleGetSigRankStandardRecord(args) {
108
+ const input = tokenCount(args?.input, "input", true);
109
+ const output = tokenCount(args?.output, "output", true);
110
+ const cacheWrite = tokenCount(args?.cache_write, "cache_write", false);
111
+ const cacheRead = tokenCount(args?.cache_read, "cache_read", false);
82
112
 
83
- const c = cascade({ input, output, cacheCreate: cacheWrite, cacheRead });
113
+ const c = cascade({
114
+ input,
115
+ output,
116
+ cacheCreate: cacheWrite ?? 0,
117
+ cacheRead: cacheRead ?? 0,
118
+ });
119
+ const warnings = [...(c.warnings || [])];
120
+ if (cacheWrite === null) warnings.push("cache_write is unavailable; 10xDEV is undefined.");
121
+ if (cacheRead === null) warnings.push("cache_read is unavailable; Yield, Leverage, and 10xDEV are undefined.");
84
122
 
85
123
  return {
86
124
  spec: SIGRANK_STANDARD_VERSION,
87
- timestamp: args?.timestamp || new Date().toISOString(),
125
+ timestamp: recordTimestamp(args?.timestamp),
88
126
  source: {
89
127
  provider: args?.provider || "unknown",
90
128
  model: args?.model || "unknown",
@@ -97,12 +135,12 @@ export async function handleGetSigRankStandardRecord(args) {
97
135
  cache_read: cacheRead,
98
136
  },
99
137
  metrics: {
100
- yield: c.yield,
101
- leverage: c.leverage,
138
+ yield: cacheRead === null ? null : c.yield,
139
+ leverage: cacheRead === null ? null : c.leverage,
102
140
  velocity: c.velocity,
103
141
  snr: c.snr,
104
- dev10x: c.dev10x,
142
+ dev10x: cacheWrite === null || cacheRead === null ? null : c.dev10x,
105
143
  },
106
- warnings: c.warnings || [],
144
+ warnings,
107
145
  };
108
146
  }