create-quorum-router 0.1.12 → 0.1.15

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
@@ -13,11 +13,12 @@ cd my-quorum-router-demo
13
13
  deno --version
14
14
  deno task smoke
15
15
  deno task calibration:demo
16
+ deno task calibration:hierarchy-demo
16
17
  deno task intake
17
18
  deno task supabase:status
18
19
  ```
19
20
 
20
- Current package version: `create-quorum-router@0.1.12`. Releases are published
21
+ Current package version: `create-quorum-router@0.1.15`. Releases are published
21
22
  from an immutable Git tag through GitHub Actions OIDC Trusted Publishing.
22
23
 
23
24
  ## What the generated project supports
@@ -25,11 +26,18 @@ from an immutable Git tag through GitHub Actions OIDC Trusted Publishing.
25
26
  `deno task smoke` is deterministic fixture-only and does not call a provider
26
27
  API.
27
28
 
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. The command
31
- does not call provider APIs; on a new Deno installation, its first run resolves
32
- the pinned Zod dependency before execution.
29
+ `deno task calibration:demo` runs the bundled flat calibration-by-task API
30
+ against deterministic local observations. `deno task calibration:hierarchy-demo`
31
+ runs three deterministic scenarios that select at the prompt-pattern level, then
32
+ fall back to task subtype, then task type as narrower sample buckets become
33
+ insufficient. Generated projects also export
34
+ `aggregateHierarchicalTaskCalibration()` and
35
+ `resolveHierarchicalTaskCalibration()` with
36
+ `prompt_pattern → task_subtype → task_type` sample-count fallback. Reports are
37
+ advisory-only and are not connected to routing weights, provider eligibility, or
38
+ execution. Both demos are local-only; the hierarchy demo does not call provider
39
+ APIs. On a new Deno installation, the first run resolves the pinned Zod
40
+ dependency before execution.
33
41
 
34
42
  `deno task intake` is the first real setup command. It detects local provider
35
43
  wrappers, checks OAuth/session status, runs safe list-only model inventory where
@@ -40,6 +48,7 @@ the next command.
40
48
  deno task check
41
49
  deno task smoke
42
50
  deno task calibration:demo
51
+ deno task calibration:hierarchy-demo
43
52
  deno task intake
44
53
  deno task auth:status
45
54
  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.12";
6
+ const VERSION = "0.1.15";
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.12",
3
+ "version": "0.1.15",
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.12.
4
+ npm latest targets v0.1.15.
5
5
 
6
6
  QuorumRouter is **MIT**. It is **open source**. Commercial and production use
7
7
  are permitted under the MIT License.
@@ -38,6 +38,7 @@ deno --version
38
38
  ```bash
39
39
  deno task smoke
40
40
  deno task calibration:demo
41
+ deno task calibration:hierarchy-demo
41
42
  deno task intake
42
43
  deno task auth:status
43
44
  deno task models:list
@@ -48,11 +49,23 @@ deno task supabase:status
48
49
  `smoke` proves the local scaffold runs with deterministic fixtures only. It does
49
50
  **not** call a real provider API.
50
51
 
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. The command does not call provider APIs; on a new Deno installation,
55
- its first run resolves the pinned Zod dependency before execution.
52
+ `calibration:demo` exercises the bundled flat calibration-by-task API with local
53
+ fixture observations. `calibration:hierarchy-demo` runs three deterministic
54
+ queries that select a sufficient prompt-pattern group, fall back to task
55
+ subtype, and fall back again to task type. Calibration reports are
56
+ advisory-only: the scaffold does not use them to change routing weights, ranks,
57
+ provider eligibility, quorum, or execution. Both demos are local-only; the
58
+ hierarchy demo does not call provider APIs. On a new Deno installation, the
59
+ first run resolves the pinned Zod dependency before execution.
60
+
61
+ The hierarchical demo uses `aggregateHierarchicalTaskCalibration()` and
62
+ `resolveHierarchicalTaskCalibration()` from `src/calibration.ts`. Its
63
+ observations use caller-defined categories such as `task_type: "code-review"`,
64
+ `task_subtype: "typescript"`, and `prompt_pattern: "schema-boundary-review"`.
65
+ Resolution checks `prompt_pattern → task_subtype → task_type`, using the first
66
+ group that reaches the configured sample threshold. Groups never cross exact
67
+ provider/model source boundaries, labels must not contain raw prompts, and the
68
+ result remains advisory-only.
56
69
 
57
70
  `intake` detects local provider wrappers, checks OAuth/session status, runs safe
58
71
  model inventory/list-only probes where possible, writes local health traces
@@ -228,8 +241,9 @@ not paste them into chat/logs and do not commit `.env`.
228
241
  `src/provider_client.ts` — provider discovery and safe invocation.
229
242
  - `src/best_route.ts`, `src/agent_chat.ts` — gated dogfood commands.
230
243
  - `src/cost_aware.ts` — estimated-cost budget selection for Best Route.
231
- - `src/calibration.ts`, `src/calibration_demo.ts` — strict advisory calibration
232
- aggregation and an offline runnable example.
244
+ - `src/calibration.ts`, `src/calibration_demo.ts` — strict flat and hierarchical
245
+ advisory aggregation, parent fallback resolution, and an offline runnable
246
+ example.
233
247
  - `src/trace.ts`, `src/redact.ts`, `src/schema.ts`, `src/fixture_smoke.ts` —
234
248
  trace/redaction/schema/fixture support.
235
249
  - `out/.gitkeep` — local output directory placeholder.
@@ -7,6 +7,7 @@
7
7
  "check": "deno check main.ts src/*.ts",
8
8
  "smoke": "deno run main.ts",
9
9
  "calibration:demo": "deno run src/calibration_demo.ts",
10
+ "calibration:hierarchy-demo": "deno run src/hierarchical_calibration_demo.ts",
10
11
  "intake": "deno run --allow-read --allow-write --allow-env --allow-net --allow-run=grok,codex,claude,gemini,devin src/cli.ts intake",
11
12
  "auth:status": "deno run --allow-read --allow-env --allow-run=grok,codex,claude,gemini,devin src/cli.ts auth:status",
12
13
  "auth:login": "deno run src/cli.ts auth:login",
@@ -28,6 +28,10 @@ const ObservationIdSchema = boundedCanonicalText(
28
28
  MAX_CALIBRATION_IDENTIFIER_LENGTH,
29
29
  );
30
30
  const TaskTypeSchema = boundedCanonicalText(MAX_CALIBRATION_IDENTIFIER_LENGTH);
31
+ const HierarchicalTaxonomyLabelSchema = boundedCanonicalText(128).refine(
32
+ (value) => /^[a-z0-9](?:[a-z0-9._:/-]{0,126}[a-z0-9])?$/.test(value),
33
+ "hierarchical taxonomy labels must be lowercase canonical ASCII identifiers",
34
+ );
31
35
  const SourceLabelSchema = boundedCanonicalText(
32
36
  MAX_CALIBRATION_IDENTIFIER_LENGTH,
33
37
  );
@@ -247,3 +251,497 @@ export function aggregateTaskCalibration(
247
251
  groups,
248
252
  });
249
253
  }
254
+
255
+ export const MAX_HIERARCHICAL_CALIBRATION_GROUPS =
256
+ MAX_CALIBRATION_OBSERVATIONS * 3;
257
+
258
+ export const HierarchicalCalibrationScopeSchema = z.enum([
259
+ "task_type",
260
+ "task_subtype",
261
+ "prompt_pattern",
262
+ ]);
263
+
264
+ export const HierarchicalTaskCalibrationObservationSchema = z.object({
265
+ observation_id: ObservationIdSchema,
266
+ task_type: TaskTypeSchema,
267
+ task_subtype: HierarchicalTaxonomyLabelSchema.optional(),
268
+ prompt_pattern: HierarchicalTaxonomyLabelSchema.optional(),
269
+ source: TaskCalibrationSourceSchema,
270
+ evaluation_basis: z.literal("caller_attested_external_ground_truth"),
271
+ correct: z.boolean(),
272
+ confidence: z.number().finite().min(0).max(1),
273
+ evaluated_at: EvaluationTimestampSchema,
274
+ }).strict().superRefine((observation, context) => {
275
+ if (
276
+ observation.prompt_pattern !== undefined &&
277
+ observation.task_subtype === undefined
278
+ ) {
279
+ context.addIssue({
280
+ code: "custom",
281
+ message: "prompt_pattern requires task_subtype",
282
+ path: ["prompt_pattern"],
283
+ });
284
+ }
285
+ });
286
+
287
+ export const HierarchicalTaskCalibrationObservationListSchema = z.array(
288
+ HierarchicalTaskCalibrationObservationSchema,
289
+ ).max(MAX_CALIBRATION_OBSERVATIONS).superRefine((observations, context) => {
290
+ const ids = new Set<string>();
291
+ for (const [index, observation] of observations.entries()) {
292
+ if (ids.has(observation.observation_id)) {
293
+ context.addIssue({
294
+ code: "custom",
295
+ message: "observation_id must be unique",
296
+ path: [index, "observation_id"],
297
+ });
298
+ }
299
+ ids.add(observation.observation_id);
300
+ }
301
+ });
302
+
303
+ export const HierarchicalTaskCalibrationGroupSchema = z.object({
304
+ scope: HierarchicalCalibrationScopeSchema,
305
+ task_type: TaskTypeSchema,
306
+ task_subtype: HierarchicalTaxonomyLabelSchema.optional(),
307
+ prompt_pattern: HierarchicalTaxonomyLabelSchema.optional(),
308
+ source: TaskCalibrationSourceSchema,
309
+ sample_count: z.number().int().positive().max(MAX_CALIBRATION_OBSERVATIONS),
310
+ accuracy: z.number().finite().min(0).max(1),
311
+ mean_confidence: z.number().finite().min(0).max(1),
312
+ brier_score: z.number().finite().min(0).max(1),
313
+ mean_calibration_bias: z.number().finite().min(-1).max(1),
314
+ sample_status: z.enum(["insufficient", "sufficient"]),
315
+ }).strict().superRefine((group, context) => {
316
+ const validLabels =
317
+ (group.scope === "task_type" && group.task_subtype === undefined &&
318
+ group.prompt_pattern === undefined) ||
319
+ (group.scope === "task_subtype" && group.task_subtype !== undefined &&
320
+ group.prompt_pattern === undefined) ||
321
+ (group.scope === "prompt_pattern" && group.task_subtype !== undefined &&
322
+ group.prompt_pattern !== undefined);
323
+ if (!validLabels) {
324
+ context.addIssue({
325
+ code: "custom",
326
+ message: "scope must match task_subtype and prompt_pattern labels",
327
+ });
328
+ }
329
+ });
330
+
331
+ export const HierarchicalTaskCalibrationReportSchema = z.object({
332
+ schema_version: z.literal("quorum-router.hierarchical-calibration.v1"),
333
+ advisory_only: z.literal(true),
334
+ minimum_sample_count: z.number().int().positive().max(
335
+ MAX_CALIBRATION_OBSERVATIONS,
336
+ ),
337
+ groups: z.array(HierarchicalTaskCalibrationGroupSchema).max(
338
+ MAX_HIERARCHICAL_CALIBRATION_GROUPS,
339
+ ),
340
+ }).strict().superRefine((report, context) => {
341
+ const groupKeys = new Set<string>();
342
+ for (const [index, group] of report.groups.entries()) {
343
+ const groupKey = JSON.stringify([
344
+ group.scope,
345
+ group.task_type,
346
+ group.task_subtype ?? null,
347
+ group.prompt_pattern ?? null,
348
+ group.source.provider,
349
+ group.source.model,
350
+ ]);
351
+ if (groupKeys.has(groupKey)) {
352
+ context.addIssue({
353
+ code: "custom",
354
+ message: "hierarchical calibration report groups must be unique",
355
+ path: ["groups", index],
356
+ });
357
+ }
358
+ groupKeys.add(groupKey);
359
+
360
+ const expectedStatus = group.sample_count < report.minimum_sample_count
361
+ ? "insufficient"
362
+ : "sufficient";
363
+ if (group.sample_status !== expectedStatus) {
364
+ context.addIssue({
365
+ code: "custom",
366
+ message: "sample_status must match the configured sample threshold",
367
+ path: ["groups", index, "sample_status"],
368
+ });
369
+ }
370
+ if (
371
+ group.mean_calibration_bias !==
372
+ stableMetric(group.mean_confidence - group.accuracy)
373
+ ) {
374
+ context.addIssue({
375
+ code: "custom",
376
+ message: "mean_calibration_bias must equal mean_confidence - accuracy",
377
+ path: ["groups", index, "mean_calibration_bias"],
378
+ });
379
+ }
380
+ }
381
+ });
382
+
383
+ export const HierarchicalTaskCalibrationQuerySchema = z.object({
384
+ task_type: TaskTypeSchema,
385
+ task_subtype: HierarchicalTaxonomyLabelSchema.optional(),
386
+ prompt_pattern: HierarchicalTaxonomyLabelSchema.optional(),
387
+ source: TaskCalibrationSourceSchema,
388
+ }).strict().superRefine((query, context) => {
389
+ if (query.prompt_pattern !== undefined && query.task_subtype === undefined) {
390
+ context.addIssue({
391
+ code: "custom",
392
+ message: "prompt_pattern requires task_subtype",
393
+ path: ["prompt_pattern"],
394
+ });
395
+ }
396
+ });
397
+
398
+ const HierarchicalCalibrationCandidateSchema = z.object({
399
+ scope: HierarchicalCalibrationScopeSchema,
400
+ sample_count: z.number().int().positive().max(MAX_CALIBRATION_OBSERVATIONS)
401
+ .nullable(),
402
+ sample_status: z.enum(["missing", "insufficient", "sufficient"]),
403
+ }).strict().superRefine((candidate, context) => {
404
+ if (
405
+ (candidate.sample_status === "missing") !==
406
+ (candidate.sample_count === null)
407
+ ) {
408
+ context.addIssue({
409
+ code: "custom",
410
+ message: "missing candidates must have a null sample_count",
411
+ });
412
+ }
413
+ });
414
+
415
+ export const HierarchicalTaskCalibrationSelectionSchema = z.object({
416
+ schema_version: z.literal("quorum-router.hierarchical-selection.v1"),
417
+ advisory_only: z.literal(true),
418
+ query: HierarchicalTaskCalibrationQuerySchema,
419
+ requested_scope: HierarchicalCalibrationScopeSchema,
420
+ resolution_status: z.enum(["selected", "no_sufficient_group"]),
421
+ selected_scope: HierarchicalCalibrationScopeSchema.nullable(),
422
+ candidates: z.array(HierarchicalCalibrationCandidateSchema).min(1).max(3),
423
+ selected_group: HierarchicalTaskCalibrationGroupSchema.optional(),
424
+ }).strict().superRefine((selection, context) => {
425
+ const queryScope = selection.query.prompt_pattern !== undefined
426
+ ? "prompt_pattern"
427
+ : selection.query.task_subtype !== undefined
428
+ ? "task_subtype"
429
+ : "task_type";
430
+ if (selection.requested_scope !== queryScope) {
431
+ context.addIssue({
432
+ code: "custom",
433
+ message: "requested_scope must match the query labels",
434
+ path: ["requested_scope"],
435
+ });
436
+ }
437
+
438
+ const expectedScopes = selection.requested_scope === "prompt_pattern"
439
+ ? ["prompt_pattern", "task_subtype", "task_type"]
440
+ : selection.requested_scope === "task_subtype"
441
+ ? ["task_subtype", "task_type"]
442
+ : ["task_type"];
443
+ if (
444
+ JSON.stringify(selection.candidates.map((candidate) => candidate.scope)) !==
445
+ JSON.stringify(expectedScopes)
446
+ ) {
447
+ context.addIssue({
448
+ code: "custom",
449
+ message: "candidates must follow the requested scope fallback order",
450
+ path: ["candidates"],
451
+ });
452
+ }
453
+
454
+ const firstSufficient = selection.candidates.find((candidate) =>
455
+ candidate.sample_status === "sufficient"
456
+ );
457
+ if (firstSufficient === undefined) {
458
+ if (
459
+ selection.resolution_status !== "no_sufficient_group" ||
460
+ selection.selected_scope !== null ||
461
+ selection.selected_group !== undefined
462
+ ) {
463
+ context.addIssue({
464
+ code: "custom",
465
+ message: "no sufficient candidate must produce no selection",
466
+ });
467
+ }
468
+ } else if (
469
+ selection.resolution_status !== "selected" ||
470
+ selection.selected_scope !== firstSufficient.scope ||
471
+ selection.selected_group?.scope !== firstSufficient.scope ||
472
+ selection.selected_group.sample_count !== firstSufficient.sample_count ||
473
+ selection.selected_group.sample_status !== "sufficient" ||
474
+ selection.selected_group.task_type !== selection.query.task_type ||
475
+ selection.selected_group.source.provider !==
476
+ selection.query.source.provider ||
477
+ selection.selected_group.source.model !== selection.query.source.model ||
478
+ (firstSufficient.scope !== "task_type" &&
479
+ selection.selected_group.task_subtype !== selection.query.task_subtype) ||
480
+ (firstSufficient.scope === "prompt_pattern" &&
481
+ selection.selected_group.prompt_pattern !==
482
+ selection.query.prompt_pattern)
483
+ ) {
484
+ context.addIssue({
485
+ code: "custom",
486
+ message: "selection must use the first sufficient candidate",
487
+ });
488
+ }
489
+ });
490
+
491
+ export const HierarchicalTaskCalibrationDecisionSchema = z.object({
492
+ report: HierarchicalTaskCalibrationReportSchema,
493
+ selection: HierarchicalTaskCalibrationSelectionSchema,
494
+ }).strict().superRefine((decision, context) => {
495
+ const { query } = decision.selection;
496
+ const expectedGroups = decision.selection.candidates.map((candidate) =>
497
+ decision.report.groups.find((group) =>
498
+ group.scope === candidate.scope && group.task_type === query.task_type &&
499
+ group.source.provider === query.source.provider &&
500
+ group.source.model === query.source.model &&
501
+ (candidate.scope === "task_type" ||
502
+ group.task_subtype === query.task_subtype) &&
503
+ (candidate.scope !== "prompt_pattern" ||
504
+ group.prompt_pattern === query.prompt_pattern)
505
+ )
506
+ );
507
+
508
+ decision.selection.candidates.forEach((candidate, index) => {
509
+ const group = expectedGroups[index];
510
+ const expected = group === undefined
511
+ ? { sample_count: null, sample_status: "missing" }
512
+ : {
513
+ sample_count: group.sample_count,
514
+ sample_status: group.sample_status,
515
+ };
516
+ if (
517
+ candidate.sample_count !== expected.sample_count ||
518
+ candidate.sample_status !== expected.sample_status
519
+ ) {
520
+ context.addIssue({
521
+ code: "custom",
522
+ message: "selection candidate must match the aggregate report",
523
+ path: ["selection", "candidates", index],
524
+ });
525
+ }
526
+ });
527
+
528
+ const expectedSelected = expectedGroups.find((group) =>
529
+ group?.sample_status === "sufficient"
530
+ );
531
+ if (
532
+ JSON.stringify(decision.selection.selected_group) !==
533
+ JSON.stringify(expectedSelected)
534
+ ) {
535
+ context.addIssue({
536
+ code: "custom",
537
+ message: "selected_group must match the first sufficient report group",
538
+ path: ["selection", "selected_group"],
539
+ });
540
+ }
541
+ });
542
+
543
+ export type HierarchicalCalibrationScope = z.infer<
544
+ typeof HierarchicalCalibrationScopeSchema
545
+ >;
546
+ export type HierarchicalTaskCalibrationObservation = z.infer<
547
+ typeof HierarchicalTaskCalibrationObservationSchema
548
+ >;
549
+ export type HierarchicalTaskCalibrationGroup = z.infer<
550
+ typeof HierarchicalTaskCalibrationGroupSchema
551
+ >;
552
+ export type HierarchicalTaskCalibrationReport = z.infer<
553
+ typeof HierarchicalTaskCalibrationReportSchema
554
+ >;
555
+ export type HierarchicalTaskCalibrationQuery = z.infer<
556
+ typeof HierarchicalTaskCalibrationQuerySchema
557
+ >;
558
+ export type HierarchicalTaskCalibrationSelection = z.infer<
559
+ typeof HierarchicalTaskCalibrationSelectionSchema
560
+ >;
561
+ export type HierarchicalTaskCalibrationDecision = z.infer<
562
+ typeof HierarchicalTaskCalibrationDecisionSchema
563
+ >;
564
+
565
+ type HierarchicalGroupSeed = {
566
+ scope: HierarchicalCalibrationScope;
567
+ task_type: string;
568
+ task_subtype?: string;
569
+ prompt_pattern?: string;
570
+ source: TaskCalibrationSource;
571
+ observations: HierarchicalTaskCalibrationObservation[];
572
+ };
573
+
574
+ function summarizeHierarchicalGroup(
575
+ seed: HierarchicalGroupSeed,
576
+ minimumSampleCount: number,
577
+ ): HierarchicalTaskCalibrationGroup {
578
+ const sample_count = seed.observations.length;
579
+ const correctCount = seed.observations.reduce(
580
+ (total, observation) => total + Number(observation.correct),
581
+ 0,
582
+ );
583
+ const confidenceTotal = stableSum(
584
+ seed.observations.map((observation) => observation.confidence),
585
+ );
586
+ const brierTotal = stableSum(seed.observations.map((observation) => {
587
+ const outcome = Number(observation.correct);
588
+ return (observation.confidence - outcome) ** 2;
589
+ }));
590
+ const accuracy = stableMetric(correctCount / sample_count);
591
+ const mean_confidence = stableMetric(confidenceTotal / sample_count);
592
+
593
+ return HierarchicalTaskCalibrationGroupSchema.parse({
594
+ scope: seed.scope,
595
+ task_type: seed.task_type,
596
+ ...(seed.task_subtype === undefined
597
+ ? {}
598
+ : { task_subtype: seed.task_subtype }),
599
+ ...(seed.prompt_pattern === undefined
600
+ ? {}
601
+ : { prompt_pattern: seed.prompt_pattern }),
602
+ source: seed.source,
603
+ sample_count,
604
+ accuracy,
605
+ mean_confidence,
606
+ brier_score: stableMetric(brierTotal / sample_count),
607
+ mean_calibration_bias: stableMetric(mean_confidence - accuracy),
608
+ sample_status: sample_count < minimumSampleCount
609
+ ? "insufficient"
610
+ : "sufficient",
611
+ });
612
+ }
613
+
614
+ export function aggregateHierarchicalTaskCalibration(
615
+ observations: readonly unknown[],
616
+ options: TaskCalibrationOptions = {},
617
+ ): HierarchicalTaskCalibrationReport {
618
+ const parsed = HierarchicalTaskCalibrationObservationListSchema.parse(
619
+ observations,
620
+ );
621
+ const { minimum_sample_count } = TaskCalibrationOptionsSchema.parse(options);
622
+ const grouped = new Map<string, HierarchicalGroupSeed>();
623
+
624
+ function add(
625
+ observation: HierarchicalTaskCalibrationObservation,
626
+ scope: HierarchicalCalibrationScope,
627
+ ) {
628
+ const task_subtype = scope === "task_type"
629
+ ? undefined
630
+ : observation.task_subtype;
631
+ const prompt_pattern = scope === "prompt_pattern"
632
+ ? observation.prompt_pattern
633
+ : undefined;
634
+ const key = JSON.stringify([
635
+ scope,
636
+ observation.task_type,
637
+ task_subtype ?? null,
638
+ prompt_pattern ?? null,
639
+ observation.source.provider,
640
+ observation.source.model,
641
+ ]);
642
+ const existing = grouped.get(key);
643
+ if (existing) {
644
+ existing.observations.push(observation);
645
+ return;
646
+ }
647
+ grouped.set(key, {
648
+ scope,
649
+ task_type: observation.task_type,
650
+ ...(task_subtype === undefined ? {} : { task_subtype }),
651
+ ...(prompt_pattern === undefined ? {} : { prompt_pattern }),
652
+ source: observation.source,
653
+ observations: [observation],
654
+ });
655
+ }
656
+
657
+ for (const observation of parsed) {
658
+ add(observation, "task_type");
659
+ if (observation.task_subtype !== undefined) {
660
+ add(observation, "task_subtype");
661
+ }
662
+ if (observation.prompt_pattern !== undefined) {
663
+ add(observation, "prompt_pattern");
664
+ }
665
+ }
666
+
667
+ const scopeOrder: Record<HierarchicalCalibrationScope, number> = {
668
+ task_type: 0,
669
+ task_subtype: 1,
670
+ prompt_pattern: 2,
671
+ };
672
+ const groups = [...grouped.values()].map((seed) =>
673
+ summarizeHierarchicalGroup(seed, minimum_sample_count)
674
+ ).sort((left, right) =>
675
+ compareCodeUnits(left.task_type, right.task_type) ||
676
+ compareCodeUnits(left.source.provider, right.source.provider) ||
677
+ compareCodeUnits(left.source.model, right.source.model) ||
678
+ scopeOrder[left.scope] - scopeOrder[right.scope] ||
679
+ compareCodeUnits(left.task_subtype ?? "", right.task_subtype ?? "") ||
680
+ compareCodeUnits(left.prompt_pattern ?? "", right.prompt_pattern ?? "")
681
+ );
682
+
683
+ return HierarchicalTaskCalibrationReportSchema.parse({
684
+ schema_version: "quorum-router.hierarchical-calibration.v1",
685
+ advisory_only: true,
686
+ minimum_sample_count,
687
+ groups,
688
+ });
689
+ }
690
+
691
+ export function resolveHierarchicalTaskCalibration(
692
+ report: HierarchicalTaskCalibrationReport,
693
+ query: HierarchicalTaskCalibrationQuery,
694
+ ): HierarchicalTaskCalibrationSelection {
695
+ const parsedReport = HierarchicalTaskCalibrationReportSchema.parse(report);
696
+ const parsedQuery = HierarchicalTaskCalibrationQuerySchema.parse(query);
697
+ const requested_scope: HierarchicalCalibrationScope =
698
+ parsedQuery.prompt_pattern !== undefined
699
+ ? "prompt_pattern"
700
+ : parsedQuery.task_subtype !== undefined
701
+ ? "task_subtype"
702
+ : "task_type";
703
+ const scopes: HierarchicalCalibrationScope[] = requested_scope ===
704
+ "prompt_pattern"
705
+ ? ["prompt_pattern", "task_subtype", "task_type"]
706
+ : requested_scope === "task_subtype"
707
+ ? ["task_subtype", "task_type"]
708
+ : ["task_type"];
709
+
710
+ const matchingGroups = scopes.map((scope) =>
711
+ parsedReport.groups.find((group) =>
712
+ group.scope === scope && group.task_type === parsedQuery.task_type &&
713
+ group.source.provider === parsedQuery.source.provider &&
714
+ group.source.model === parsedQuery.source.model &&
715
+ (scope === "task_type" ||
716
+ group.task_subtype === parsedQuery.task_subtype) &&
717
+ (scope !== "prompt_pattern" ||
718
+ group.prompt_pattern === parsedQuery.prompt_pattern)
719
+ )
720
+ );
721
+ const candidates = scopes.map((scope, index) => {
722
+ const group = matchingGroups[index];
723
+ return group === undefined
724
+ ? { scope, sample_count: null, sample_status: "missing" as const }
725
+ : {
726
+ scope,
727
+ sample_count: group.sample_count,
728
+ sample_status: group.sample_status,
729
+ };
730
+ });
731
+ const selected_group = matchingGroups.find((group) =>
732
+ group?.sample_status === "sufficient"
733
+ );
734
+
735
+ return HierarchicalTaskCalibrationSelectionSchema.parse({
736
+ schema_version: "quorum-router.hierarchical-selection.v1",
737
+ advisory_only: true,
738
+ query: parsedQuery,
739
+ requested_scope,
740
+ resolution_status: selected_group === undefined
741
+ ? "no_sufficient_group"
742
+ : "selected",
743
+ selected_scope: selected_group?.scope ?? null,
744
+ candidates,
745
+ ...(selected_group === undefined ? {} : { selected_group }),
746
+ });
747
+ }
@@ -0,0 +1,132 @@
1
+ import {
2
+ aggregateHierarchicalTaskCalibration,
3
+ resolveHierarchicalTaskCalibration,
4
+ } from "./calibration.ts";
5
+
6
+ const evaluated_at = "2026-07-14T00:00:00Z";
7
+ const source = { provider: "OpenAI", model: "example-model" } as const;
8
+ const observations = [
9
+ {
10
+ observation_id: "hierarchy-demo-pattern-1",
11
+ task_type: "code-review",
12
+ task_subtype: "typescript",
13
+ prompt_pattern: "schema-boundary-review",
14
+ source,
15
+ evaluation_basis: "caller_attested_external_ground_truth",
16
+ correct: true,
17
+ confidence: 0.9,
18
+ evaluated_at,
19
+ },
20
+ {
21
+ observation_id: "hierarchy-demo-pattern-2",
22
+ task_type: "code-review",
23
+ task_subtype: "typescript",
24
+ prompt_pattern: "schema-boundary-review",
25
+ source,
26
+ evaluation_basis: "caller_attested_external_ground_truth",
27
+ correct: true,
28
+ confidence: 0.8,
29
+ evaluated_at,
30
+ },
31
+ {
32
+ observation_id: "hierarchy-demo-pattern-3",
33
+ task_type: "code-review",
34
+ task_subtype: "typescript",
35
+ prompt_pattern: "schema-boundary-review",
36
+ source,
37
+ evaluation_basis: "caller_attested_external_ground_truth",
38
+ correct: false,
39
+ confidence: 0.7,
40
+ evaluated_at,
41
+ },
42
+ {
43
+ observation_id: "hierarchy-demo-subtype-1",
44
+ task_type: "code-review",
45
+ task_subtype: "typescript",
46
+ prompt_pattern: "concurrency-review",
47
+ source,
48
+ evaluation_basis: "caller_attested_external_ground_truth",
49
+ correct: true,
50
+ confidence: 0.8,
51
+ evaluated_at,
52
+ },
53
+ {
54
+ observation_id: "hierarchy-demo-parent-1",
55
+ task_type: "code-review",
56
+ task_subtype: "rust",
57
+ prompt_pattern: "unsafe-boundary-review",
58
+ source,
59
+ evaluation_basis: "caller_attested_external_ground_truth",
60
+ correct: true,
61
+ confidence: 0.7,
62
+ evaluated_at,
63
+ },
64
+ {
65
+ observation_id: "hierarchy-demo-foreign-1",
66
+ task_type: "code-review",
67
+ task_subtype: "typescript",
68
+ prompt_pattern: "schema-boundary-review",
69
+ source: { provider: "Anthropic", model: "example-model" },
70
+ evaluation_basis: "caller_attested_external_ground_truth",
71
+ correct: true,
72
+ confidence: 0.75,
73
+ evaluated_at,
74
+ },
75
+ ];
76
+
77
+ const report = aggregateHierarchicalTaskCalibration(observations, {
78
+ minimum_sample_count: 3,
79
+ });
80
+ const scenarios = [
81
+ {
82
+ name: "pattern-sufficient",
83
+ query: {
84
+ task_type: "code-review",
85
+ task_subtype: "typescript",
86
+ prompt_pattern: "schema-boundary-review",
87
+ source,
88
+ },
89
+ },
90
+ {
91
+ name: "fallback-to-subtype",
92
+ query: {
93
+ task_type: "code-review",
94
+ task_subtype: "typescript",
95
+ prompt_pattern: "concurrency-review",
96
+ source,
97
+ },
98
+ },
99
+ {
100
+ name: "fallback-to-task",
101
+ query: {
102
+ task_type: "code-review",
103
+ task_subtype: "rust",
104
+ prompt_pattern: "unsafe-boundary-review",
105
+ source,
106
+ },
107
+ },
108
+ ].map(({ name, query }) => {
109
+ const selection = resolveHierarchicalTaskCalibration(report, query);
110
+ return {
111
+ name,
112
+ query,
113
+ candidates: selection.candidates,
114
+ resolution_status: selection.resolution_status,
115
+ selected_scope: selection.selected_scope,
116
+ selected_sample_count: selection.selected_group?.sample_count ?? null,
117
+ };
118
+ });
119
+
120
+ console.log(JSON.stringify(
121
+ {
122
+ schema_version: "quorum-router.hierarchical-demo.v1",
123
+ advisory_only: true,
124
+ provider_request_sent: false,
125
+ fallback_order: ["prompt_pattern", "task_subtype", "task_type"],
126
+ minimum_sample_count: report.minimum_sample_count,
127
+ observation_count: observations.length,
128
+ scenarios,
129
+ },
130
+ null,
131
+ 2,
132
+ ));