create-quorum-router 0.1.7 → 0.1.8

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
@@ -12,11 +12,12 @@ npx --yes create-quorum-router@latest my-quorum-router-demo
12
12
  cd my-quorum-router-demo
13
13
  deno --version
14
14
  deno task smoke
15
+ deno task calibration:demo
15
16
  deno task intake
16
17
  deno task supabase:status
17
18
  ```
18
19
 
19
- Current package version: `create-quorum-router@0.1.7`. Releases are published
20
+ Current package version: `create-quorum-router@0.1.8`. Releases are published
20
21
  from an immutable Git tag through GitHub Actions OIDC Trusted Publishing.
21
22
 
22
23
  ## What the generated project supports
@@ -24,6 +25,10 @@ from an immutable Git tag through GitHub Actions OIDC Trusted Publishing.
24
25
  `deno task smoke` is deterministic fixture-only and does not call a provider
25
26
  API.
26
27
 
28
+ `deno task calibration:demo` runs the bundled calibration-by-task API against
29
+ deterministic local observations. The report is advisory-only and is not
30
+ connected to routing weights, provider eligibility, or execution.
31
+
27
32
  `deno task intake` is the first real setup command. It detects local provider
28
33
  wrappers, checks OAuth/session status, runs safe list-only model inventory where
29
34
  possible, writes redacted local health artifacts under `out/`, and recommends
@@ -32,6 +37,7 @@ the next command.
32
37
  ```bash
33
38
  deno task check
34
39
  deno task smoke
40
+ deno task calibration:demo
35
41
  deno task intake
36
42
  deno task auth:status
37
43
  deno task auth:login
@@ -3,7 +3,7 @@ const fs = require("fs");
3
3
  const path = require("path");
4
4
  const { spawnSync } = require("child_process");
5
5
 
6
- const VERSION = "0.1.7";
6
+ const VERSION = "0.1.8";
7
7
  const SUPPORTED_TEMPLATES = new Set(["basic"]);
8
8
 
9
9
  function usage() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-quorum-router",
3
- "version": "0.1.7",
3
+ "version": "0.1.8",
4
4
  "description": "Create a local QuorumRouter project.",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -1,7 +1,7 @@
1
1
  # QuorumRouter generated workspace
2
2
 
3
3
  This generated workspace contains the MIT-licensed QuorumRouter current release.
4
- npm latest targets v0.1.7.
4
+ npm latest targets v0.1.8.
5
5
 
6
6
  QuorumRouter is **MIT**. It is **open source**. Commercial and production use
7
7
  are permitted under the MIT License.
@@ -37,6 +37,7 @@ deno --version
37
37
 
38
38
  ```bash
39
39
  deno task smoke
40
+ deno task calibration:demo
40
41
  deno task intake
41
42
  deno task auth:status
42
43
  deno task models:list
@@ -47,6 +48,11 @@ deno task supabase:status
47
48
  `smoke` proves the local scaffold runs with deterministic fixtures only. It does
48
49
  **not** call a real provider API.
49
50
 
51
+ `calibration:demo` exercises the bundled calibration-by-task API with local
52
+ fixture observations. Calibration reports are advisory-only: the scaffold does
53
+ not use them to change routing weights, ranks, provider eligibility, quorum, or
54
+ execution.
55
+
50
56
  `intake` detects local provider wrappers, checks OAuth/session status, runs safe
51
57
  model inventory/list-only probes where possible, writes local health traces
52
58
  under `out/`, and recommends the next command.
@@ -218,6 +224,8 @@ not paste them into chat/logs and do not commit `.env`.
218
224
  `src/provider_client.ts` — provider discovery and safe invocation.
219
225
  - `src/best_route.ts`, `src/agent_chat.ts` — gated dogfood commands.
220
226
  - `src/cost_aware.ts` — estimated-cost budget selection for Best Route.
227
+ - `src/calibration.ts`, `src/calibration_demo.ts` — strict advisory calibration
228
+ aggregation and an offline runnable example.
221
229
  - `src/trace.ts`, `src/redact.ts`, `src/schema.ts`, `src/fixture_smoke.ts` —
222
230
  trace/redaction/schema/fixture support.
223
231
  - `out/.gitkeep` — local output directory placeholder.
@@ -6,6 +6,7 @@
6
6
  "tasks": {
7
7
  "check": "deno check main.ts src/*.ts",
8
8
  "smoke": "deno run main.ts",
9
+ "calibration:demo": "deno run src/calibration_demo.ts",
9
10
  "intake": "deno run --allow-read --allow-write --allow-env --allow-net --allow-run=grok,codex,claude,gemini,qwen,devin src/cli.ts intake",
10
11
  "auth:status": "deno run --allow-read --allow-env --allow-run=grok,codex,claude,gemini,qwen,devin src/cli.ts auth:status",
11
12
  "auth:login": "deno run src/cli.ts auth:login",
@@ -0,0 +1,249 @@
1
+ import { z } from "zod";
2
+
3
+ export const MAX_CALIBRATION_OBSERVATIONS = 10_000;
4
+ export const MAX_CALIBRATION_IDENTIFIER_LENGTH = 256;
5
+
6
+ function stableMetric(value: number): number {
7
+ const rounded = Number(value.toPrecision(15));
8
+ return rounded === 0 ? 0 : rounded;
9
+ }
10
+
11
+ function boundedCanonicalText(maxLength: number) {
12
+ return z.string().trim().transform((value) => value.normalize("NFC")).pipe(
13
+ z.string().min(1).max(maxLength).refine(
14
+ (value) => !/[\p{Cc}\p{Cf}\p{Default_Ignorable_Code_Point}]/u.test(value),
15
+ "control and default-ignorable characters are not allowed",
16
+ ),
17
+ );
18
+ }
19
+
20
+ function hasValidRfc3339Offset(value: string): boolean {
21
+ if (value.endsWith("Z")) return true;
22
+ const match = /[+-](\d{2}):(\d{2})$/.exec(value);
23
+ if (!match) return false;
24
+ return Number(match[1]) <= 23 && Number(match[2]) <= 59;
25
+ }
26
+
27
+ const ObservationIdSchema = boundedCanonicalText(
28
+ MAX_CALIBRATION_IDENTIFIER_LENGTH,
29
+ );
30
+ const TaskTypeSchema = boundedCanonicalText(MAX_CALIBRATION_IDENTIFIER_LENGTH);
31
+ const SourceLabelSchema = boundedCanonicalText(
32
+ MAX_CALIBRATION_IDENTIFIER_LENGTH,
33
+ );
34
+ const EvaluationTimestampSchema = z.string().datetime({ offset: true }).refine(
35
+ hasValidRfc3339Offset,
36
+ "evaluated_at must use a valid RFC 3339 UTC offset",
37
+ );
38
+
39
+ export const TaskCalibrationSourceSchema = z.object({
40
+ provider: SourceLabelSchema,
41
+ model: SourceLabelSchema,
42
+ }).strict();
43
+
44
+ /**
45
+ * Structurally validated calibration input supplied by the caller.
46
+ *
47
+ * `evaluation_basis` records an unverified provenance assertion; this schema
48
+ * cannot establish label independence, label quality, example matching, or
49
+ * freedom from leakage. `confidence` must be the model's probability, recorded
50
+ * before the correctness label is observed, that this specific answer is
51
+ * correct. The schema can enforce only the numeric range, not that provenance.
52
+ */
53
+ export const TaskCalibrationObservationSchema = z.object({
54
+ observation_id: ObservationIdSchema,
55
+ task_type: TaskTypeSchema,
56
+ source: TaskCalibrationSourceSchema,
57
+ evaluation_basis: z.literal("caller_attested_external_ground_truth"),
58
+ correct: z.boolean(),
59
+ confidence: z.number().finite().min(0).max(1),
60
+ evaluated_at: EvaluationTimestampSchema,
61
+ }).strict();
62
+
63
+ export const TaskCalibrationObservationListSchema = z.array(
64
+ TaskCalibrationObservationSchema,
65
+ ).max(MAX_CALIBRATION_OBSERVATIONS).superRefine((observations, context) => {
66
+ const ids = new Set<string>();
67
+ for (const [index, observation] of observations.entries()) {
68
+ if (ids.has(observation.observation_id)) {
69
+ context.addIssue({
70
+ code: "custom",
71
+ message: "observation_id must be unique",
72
+ path: [index, "observation_id"],
73
+ });
74
+ }
75
+ ids.add(observation.observation_id);
76
+ }
77
+ });
78
+
79
+ export const TaskCalibrationOptionsSchema = z.object({
80
+ minimum_sample_count: z.number().finite().int().positive().max(
81
+ MAX_CALIBRATION_OBSERVATIONS,
82
+ ).default(20),
83
+ }).strict();
84
+
85
+ export const TaskCalibrationGroupSchema = z.object({
86
+ task_type: TaskTypeSchema,
87
+ source: TaskCalibrationSourceSchema,
88
+ sample_count: z.number().int().positive().max(MAX_CALIBRATION_OBSERVATIONS),
89
+ accuracy: z.number().finite().min(0).max(1),
90
+ mean_confidence: z.number().finite().min(0).max(1),
91
+ brier_score: z.number().finite().min(0).max(1),
92
+ mean_calibration_bias: z.number().finite().min(-1).max(1),
93
+ // "sufficient" means only that the configured sample-count threshold is met.
94
+ sample_status: z.enum(["insufficient", "sufficient"]),
95
+ }).strict();
96
+
97
+ export const TaskCalibrationReportSchema = z.object({
98
+ schema_version: z.literal("quorum-router.calibration-by-task.v1"),
99
+ advisory_only: z.literal(true),
100
+ minimum_sample_count: z.number().int().positive().max(
101
+ MAX_CALIBRATION_OBSERVATIONS,
102
+ ),
103
+ groups: z.array(TaskCalibrationGroupSchema).max(MAX_CALIBRATION_OBSERVATIONS),
104
+ }).strict().superRefine((report, context) => {
105
+ const groupKeys = new Set<string>();
106
+ for (const [index, group] of report.groups.entries()) {
107
+ const groupKey = JSON.stringify([
108
+ group.task_type,
109
+ group.source.provider,
110
+ group.source.model,
111
+ ]);
112
+ if (groupKeys.has(groupKey)) {
113
+ context.addIssue({
114
+ code: "custom",
115
+ message: "calibration report groups must be unique",
116
+ path: ["groups", index],
117
+ });
118
+ }
119
+ groupKeys.add(groupKey);
120
+
121
+ const expectedStatus = group.sample_count < report.minimum_sample_count
122
+ ? "insufficient"
123
+ : "sufficient";
124
+ if (group.sample_status !== expectedStatus) {
125
+ context.addIssue({
126
+ code: "custom",
127
+ message: "sample_status must match the configured sample threshold",
128
+ path: ["groups", index, "sample_status"],
129
+ });
130
+ }
131
+
132
+ const expectedBias = stableMetric(
133
+ group.mean_confidence - group.accuracy,
134
+ );
135
+ if (group.mean_calibration_bias !== expectedBias) {
136
+ context.addIssue({
137
+ code: "custom",
138
+ message: "mean_calibration_bias must equal mean_confidence - accuracy",
139
+ path: ["groups", index, "mean_calibration_bias"],
140
+ });
141
+ }
142
+ }
143
+ });
144
+
145
+ export type TaskCalibrationSource = z.infer<
146
+ typeof TaskCalibrationSourceSchema
147
+ >;
148
+ export type TaskCalibrationObservation = z.infer<
149
+ typeof TaskCalibrationObservationSchema
150
+ >;
151
+ export type TaskCalibrationOptions = z.input<
152
+ typeof TaskCalibrationOptionsSchema
153
+ >;
154
+ export type TaskCalibrationGroup = z.infer<
155
+ typeof TaskCalibrationGroupSchema
156
+ >;
157
+ export type TaskCalibrationReport = z.infer<
158
+ typeof TaskCalibrationReportSchema
159
+ >;
160
+
161
+ function stableSum(values: readonly number[]): number {
162
+ const sorted = [...values].sort((left, right) => left - right);
163
+ let total = 0;
164
+ let compensation = 0;
165
+
166
+ for (const value of sorted) {
167
+ const next = total + value;
168
+ compensation += Math.abs(total) >= Math.abs(value)
169
+ ? (total - next) + value
170
+ : (value - next) + total;
171
+ total = next;
172
+ }
173
+
174
+ return total + compensation;
175
+ }
176
+
177
+ function compareCodeUnits(left: string, right: string): number {
178
+ if (left < right) return -1;
179
+ if (left > right) return 1;
180
+ return 0;
181
+ }
182
+
183
+ export function aggregateTaskCalibration(
184
+ observations: readonly unknown[],
185
+ options: TaskCalibrationOptions = {},
186
+ ): TaskCalibrationReport {
187
+ const parsedObservations = TaskCalibrationObservationListSchema.parse(
188
+ observations,
189
+ );
190
+ const { minimum_sample_count } = TaskCalibrationOptionsSchema.parse(options);
191
+ const grouped = new Map<string, TaskCalibrationObservation[]>();
192
+
193
+ for (const observation of parsedObservations) {
194
+ const key = JSON.stringify([
195
+ observation.task_type,
196
+ observation.source.provider,
197
+ observation.source.model,
198
+ ]);
199
+ const group = grouped.get(key);
200
+ if (group) {
201
+ group.push(observation);
202
+ } else {
203
+ grouped.set(key, [observation]);
204
+ }
205
+ }
206
+
207
+ const groups = [...grouped.values()].map((group) => {
208
+ const sample_count = group.length;
209
+ const correctCount = group.reduce(
210
+ (total, observation) => total + Number(observation.correct),
211
+ 0,
212
+ );
213
+ const confidenceTotal = stableSum(
214
+ group.map((observation) => observation.confidence),
215
+ );
216
+ const brierTotal = stableSum(
217
+ group.map((observation) => {
218
+ const outcome = Number(observation.correct);
219
+ return (observation.confidence - outcome) ** 2;
220
+ }),
221
+ );
222
+ const accuracy = stableMetric(correctCount / sample_count);
223
+ const mean_confidence = stableMetric(confidenceTotal / sample_count);
224
+
225
+ return {
226
+ task_type: group[0].task_type,
227
+ source: group[0].source,
228
+ sample_count,
229
+ accuracy,
230
+ mean_confidence,
231
+ brier_score: stableMetric(brierTotal / sample_count),
232
+ mean_calibration_bias: stableMetric(mean_confidence - accuracy),
233
+ sample_status: sample_count < minimum_sample_count
234
+ ? "insufficient" as const
235
+ : "sufficient" as const,
236
+ };
237
+ }).sort((left, right) =>
238
+ compareCodeUnits(left.task_type, right.task_type) ||
239
+ compareCodeUnits(left.source.provider, right.source.provider) ||
240
+ compareCodeUnits(left.source.model, right.source.model)
241
+ );
242
+
243
+ return TaskCalibrationReportSchema.parse({
244
+ schema_version: "quorum-router.calibration-by-task.v1",
245
+ advisory_only: true,
246
+ minimum_sample_count,
247
+ groups,
248
+ });
249
+ }
@@ -0,0 +1,25 @@
1
+ import { aggregateTaskCalibration } from "./calibration.ts";
2
+
3
+ const evaluated_at = "2026-07-13T00:00:00Z";
4
+ const report = aggregateTaskCalibration([
5
+ {
6
+ observation_id: "demo-1",
7
+ task_type: "code-review",
8
+ source: { provider: "OpenAI", model: "example-model" },
9
+ evaluation_basis: "caller_attested_external_ground_truth",
10
+ correct: true,
11
+ confidence: 0.8,
12
+ evaluated_at,
13
+ },
14
+ {
15
+ observation_id: "demo-2",
16
+ task_type: "code-review",
17
+ source: { provider: "OpenAI", model: "example-model" },
18
+ evaluation_basis: "caller_attested_external_ground_truth",
19
+ correct: false,
20
+ confidence: 0.6,
21
+ evaluated_at,
22
+ },
23
+ ], { minimum_sample_count: 2 });
24
+
25
+ console.log(JSON.stringify(report, null, 2));