tickmarkr 1.85.0 → 1.86.0
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 +4 -2
- package/dist/adapters/catalog-remote.d.ts +64 -0
- package/dist/adapters/catalog-remote.js +287 -0
- package/dist/adapters/catalog.d.ts +96 -0
- package/dist/adapters/catalog.js +176 -0
- package/dist/adapters/claude-code.js +5 -3
- package/dist/adapters/fake.js +33 -4
- package/dist/adapters/model-lints.d.ts +25 -5
- package/dist/adapters/model-lints.js +184 -50
- package/dist/adapters/model-windows.d.ts +31 -0
- package/dist/adapters/model-windows.js +69 -0
- package/dist/adapters/prompt.d.ts +5 -1
- package/dist/adapters/prompt.js +13 -4
- package/dist/adapters/registry.d.ts +25 -26
- package/dist/adapters/registry.js +173 -110
- package/dist/adapters/types.js +15 -3
- package/dist/brand.d.ts +5 -1
- package/dist/brand.js +18 -2
- package/dist/cli/commands/doctor.d.ts +3 -0
- package/dist/cli/commands/doctor.js +43 -21
- package/dist/cli/commands/fleet.d.ts +7 -0
- package/dist/cli/commands/fleet.js +94 -74
- package/dist/cli/commands/init.js +118 -5
- package/dist/cli/commands/status.js +44 -18
- package/dist/compile/gsd.d.ts +2 -1
- package/dist/compile/gsd.js +68 -2
- package/dist/compile/native.d.ts +14 -0
- package/dist/compile/native.js +161 -12
- package/dist/config/config.d.ts +20 -5
- package/dist/config/config.js +96 -64
- package/dist/config/fleet-overlay.d.ts +25 -20
- package/dist/config/fleet-overlay.js +195 -77
- package/dist/config/fleet-why.d.ts +23 -0
- package/dist/config/fleet-why.js +42 -0
- package/dist/drivers/herdr.d.ts +1 -0
- package/dist/drivers/herdr.js +70 -19
- package/dist/gates/acceptance.js +7 -2
- package/dist/gates/llm.d.ts +0 -1
- package/dist/gates/llm.js +5 -30
- package/dist/gates/review.d.ts +2 -1
- package/dist/gates/review.js +9 -7
- package/dist/gates/verdict-cause.d.ts +4 -0
- package/dist/gates/verdict-cause.js +63 -0
- package/dist/graph/schema.d.ts +6 -0
- package/dist/graph/schema.js +8 -5
- package/dist/route/router.d.ts +0 -5
- package/dist/route/router.js +16 -20
- package/dist/run/consult.d.ts +6 -0
- package/dist/run/consult.js +35 -25
- package/dist/run/daemon.js +71 -15
- package/dist/run/journal.js +87 -7
- package/dist/tui/cockpit/capture.d.ts +12 -0
- package/dist/tui/cockpit/capture.js +37 -1
- package/dist/tui/cockpit/components.js +8 -8
- package/dist/tui/cockpit/theme.d.ts +32 -26
- package/dist/tui/cockpit/theme.js +11 -5
- package/dist/tui/ink/components.d.ts +0 -15
- package/dist/tui/ink/components.js +0 -17
- package/dist/tui/ink/fleet-app.d.ts +4 -1
- package/dist/tui/ink/fleet-app.js +134 -13
- package/fixtures/sample.native.md +1 -1
- package/package.json +1 -1
- package/skills/tickmarkr-overseer/SKILL.md +354 -34
- package/skills/tickmarkr-overseer/scripts/watch-artifacts.sh +70 -0
- package/skills/tickmarkr-overseer/scripts/watch-panes.sh +1 -1
- package/dist/tui/ink/studio-app.d.ts +0 -59
- package/dist/tui/ink/studio-app.js +0 -320
- package/dist/tui/save.d.ts +0 -38
- package/dist/tui/save.js +0 -96
- package/dist/tui/staging.d.ts +0 -29
- package/dist/tui/staging.js +0 -78
package/dist/config/config.js
CHANGED
|
@@ -2,8 +2,9 @@ import { existsSync, readFileSync } from "node:fs";
|
|
|
2
2
|
import { homedir } from "node:os";
|
|
3
3
|
import { join } from "node:path";
|
|
4
4
|
import picomatch from "picomatch";
|
|
5
|
-
import {
|
|
5
|
+
import { Document, parse } from "yaml";
|
|
6
6
|
import { z } from "zod";
|
|
7
|
+
import { CITED_MODEL_WINDOWS } from "../adapters/model-windows.js";
|
|
7
8
|
import { stateDirName } from "../graph/graph.js";
|
|
8
9
|
import { SHAPES, TIERS } from "../graph/schema.js";
|
|
9
10
|
const TierEnum = z.enum(TIERS, {
|
|
@@ -11,6 +12,8 @@ const TierEnum = z.enum(TIERS, {
|
|
|
11
12
|
});
|
|
12
13
|
export const TIER_RANK = { cheap: 0, mid: 1, frontier: 2 };
|
|
13
14
|
export const DEFAULT_DIFF_CAP = 60_000;
|
|
15
|
+
// This is deliberately only a rejection guard. T20's cited table, not this floor, supplies each claim.
|
|
16
|
+
export const MODEL_WINDOW_PLAUSIBILITY_FLOOR = 1_024;
|
|
14
17
|
// v1.51 T1: routing.mode — a preset COMPILED INTO FLOORS at config load. The router never sees the
|
|
15
18
|
// mode; it receives resolved floors only (the structural defense against the quality-silently-loses
|
|
16
19
|
// class — no fourth runtime authority, no new precedence key).
|
|
@@ -39,12 +42,36 @@ export const MapEntrySchema = z.object({
|
|
|
39
42
|
prefer: z.array(z.string()).optional(),
|
|
40
43
|
escalate: z.boolean().optional(),
|
|
41
44
|
});
|
|
45
|
+
const DeclaredVendorSchema = z.string().trim().min(1, "vendor must be nonempty");
|
|
46
|
+
const ModelOverrideSchema = z
|
|
47
|
+
.object({
|
|
48
|
+
vendor: DeclaredVendorSchema.optional(),
|
|
49
|
+
channel: z.enum(["sub", "api"]).optional(),
|
|
50
|
+
})
|
|
51
|
+
.refine((override) => override.vendor !== undefined || override.channel !== undefined, {
|
|
52
|
+
message: "a model override must declare vendor, channel, or both",
|
|
53
|
+
});
|
|
42
54
|
export const TierEntrySchema = z.object({
|
|
43
|
-
vendor:
|
|
55
|
+
vendor: DeclaredVendorSchema.nullable(),
|
|
44
56
|
channel: z.enum(["sub", "api"]),
|
|
45
57
|
models: z.record(z.string(), TierEnum),
|
|
58
|
+
// T19: keep model values scalar for existing consumers. Optional sibling metadata resolves before
|
|
59
|
+
// parent metadata in channelsFromConfig; neither provider prefixes nor adapter ids manufacture it.
|
|
60
|
+
modelOverrides: z.record(z.string(), ModelOverrideSchema).optional(),
|
|
46
61
|
// v1.47 T3: optional per-model context-window sizes (tokens). Absent block ⇒ no doctor column, no plan lint.
|
|
47
62
|
windows: z.record(z.string(), z.number().int().positive()).optional(),
|
|
63
|
+
}).superRefine((entry, ctx) => {
|
|
64
|
+
if (entry.vendor !== null)
|
|
65
|
+
return;
|
|
66
|
+
for (const model of Object.keys(entry.models)) {
|
|
67
|
+
if (entry.modelOverrides?.[model]?.vendor)
|
|
68
|
+
continue;
|
|
69
|
+
ctx.addIssue({
|
|
70
|
+
code: "custom",
|
|
71
|
+
path: ["modelOverrides", model, "vendor"],
|
|
72
|
+
message: "vendor: null requires every configured model to declare a nonempty vendor override",
|
|
73
|
+
});
|
|
74
|
+
}
|
|
48
75
|
});
|
|
49
76
|
// v1.10 FLEET-06: optional routing.allow/deny fleet preference; absent blocks ⇒ byte-identical routing/discovery.
|
|
50
77
|
// deny wins on conflict; presence of allow (even {} / empty arrays) activates allowlist (fail-closed).
|
|
@@ -341,6 +368,24 @@ export class ConfigError extends Error {
|
|
|
341
368
|
this.name = "ConfigError";
|
|
342
369
|
}
|
|
343
370
|
}
|
|
371
|
+
function assertSeededModelWindows() {
|
|
372
|
+
const cited = new Map(CITED_MODEL_WINDOWS.map((claim) => [claim.modelId, claim.window]));
|
|
373
|
+
for (const [adapter, entry] of Object.entries(DEFAULT_CONFIG.tiers)) {
|
|
374
|
+
for (const [model, window] of Object.entries(entry.windows ?? {})) {
|
|
375
|
+
const path = `tiers.${adapter}.windows.${model}`;
|
|
376
|
+
if (window < MODEL_WINDOW_PLAUSIBILITY_FLOOR) {
|
|
377
|
+
throw new ConfigError(`${path} declares window ${window}, below plausibility floor ${MODEL_WINDOW_PLAUSIBILITY_FLOOR} (non-authoritative rejection guard)`);
|
|
378
|
+
}
|
|
379
|
+
const citedWindow = cited.get(model);
|
|
380
|
+
if (citedWindow === undefined) {
|
|
381
|
+
throw new ConfigError(`${path} has no cited model-window entry in T20's vendored table`);
|
|
382
|
+
}
|
|
383
|
+
if (window !== citedWindow) {
|
|
384
|
+
throw new ConfigError(`${path} declares ${window} but does not match cited window ${citedWindow} in T20's vendored table`);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
}
|
|
344
389
|
export const DEFAULT_CONFIG = {
|
|
345
390
|
concurrency: 3,
|
|
346
391
|
driver: "auto",
|
|
@@ -374,10 +419,13 @@ export const DEFAULT_CONFIG = {
|
|
|
374
419
|
allowUnverifiedModels: false,
|
|
375
420
|
},
|
|
376
421
|
// Seed table (spec §13). New models = edit this (or your config.yaml), never code.
|
|
422
|
+
// Windows are copied rather than derived from T20's table so config load can reject disagreement
|
|
423
|
+
// between two independently owned artifacts instead of letting one side certify itself.
|
|
377
424
|
tiers: {
|
|
378
425
|
"claude-code": {
|
|
379
426
|
vendor: "anthropic", channel: "sub",
|
|
380
427
|
models: { fable: "frontier", opus: "frontier", sonnet: "mid", haiku: "cheap" },
|
|
428
|
+
windows: { fable: 1_000_000, opus: 1_000_000, sonnet: 1_000_000, haiku: 200_000 },
|
|
381
429
|
},
|
|
382
430
|
// ids verified against installed CLI models_cache.json (codex 0.144.0, fetched 2026-07-09);
|
|
383
431
|
// gpt-5.4 / gpt-5.4-mini removed — OpenAI retirement 2026-07-23; sol=frontier / terra=mid / luna=cheap per OpenAI tier framing.
|
|
@@ -389,6 +437,7 @@ export const DEFAULT_CONFIG = {
|
|
|
389
437
|
codex: {
|
|
390
438
|
vendor: "openai", channel: "sub",
|
|
391
439
|
models: { "gpt-5.6-sol": "frontier", "gpt-5.5": "frontier", "gpt-5.6-terra": "mid", "gpt-5.6-luna": "cheap" },
|
|
440
|
+
windows: { "gpt-5.6-sol": 1_050_000, "gpt-5.5": 1_050_000, "gpt-5.6-terra": 1_050_000, "gpt-5.6-luna": 1_050_000 },
|
|
392
441
|
},
|
|
393
442
|
// grok-4.5 (xAI, released 2026-07-08) → mid: AA Intelligence 54 (#4), Terminal-Bench 2.1 83.3%
|
|
394
443
|
// (≈ GPT-5.5 83.4, Fable 5 84.3), SWE-bench Pro 64.7%, ~4.2× more token-efficient than Opus 4.8
|
|
@@ -409,6 +458,7 @@ export const DEFAULT_CONFIG = {
|
|
|
409
458
|
// stop burning its mid. Operator-approved 2026-07-16.
|
|
410
459
|
"composer-2.5-fast": "cheap",
|
|
411
460
|
},
|
|
461
|
+
windows: { "composer-2.5": 200_000, "composer-2.5-fast": 200_000 },
|
|
412
462
|
},
|
|
413
463
|
// GLM-5.2 → mid per benchmark policy (2026-07): SWE-bench Pro 62.1 (> GPT-5.5 58.6), FrontierSWE 74.4 ≈ Opus 4.8;
|
|
414
464
|
// no independent Terminal-Bench score → conservative mid, overlays may raise.
|
|
@@ -419,6 +469,7 @@ export const DEFAULT_CONFIG = {
|
|
|
419
469
|
opencode: {
|
|
420
470
|
vendor: "mixed", channel: "sub",
|
|
421
471
|
models: { "zai-coding-plan/glm-5.2": "mid" },
|
|
472
|
+
windows: { "zai-coding-plan/glm-5.2": 1_000_000 },
|
|
422
473
|
},
|
|
423
474
|
// GLM-5.2 via pi/ZAI Coding Plan (sub, flat-rate). mid per benchmark policy (2026-07):
|
|
424
475
|
// SWE-bench Pro 62.1, FrontierSWE 74.4 — same rationale as the opencode glm-5.2 seed above.
|
|
@@ -430,6 +481,7 @@ export const DEFAULT_CONFIG = {
|
|
|
430
481
|
pi: {
|
|
431
482
|
vendor: "zhipu", channel: "sub",
|
|
432
483
|
models: { "zai/glm-5.2": "mid" },
|
|
484
|
+
windows: { "zai/glm-5.2": 1_000_000 },
|
|
433
485
|
},
|
|
434
486
|
// Native grok CLI (Phase 40). grok-4.5 → mid: same benchmark provenance as the retired cursor-agent
|
|
435
487
|
// grok-4.5-xhigh seed above (AA 54, TB2.1 83.3%, SWE-b Pro 64.7% — researched 2026-07-10).
|
|
@@ -439,6 +491,7 @@ export const DEFAULT_CONFIG = {
|
|
|
439
491
|
grok: {
|
|
440
492
|
vendor: "xai", channel: "sub",
|
|
441
493
|
models: { "grok-4.5": "mid", "grok-composer-2.5-fast": "cheap" },
|
|
494
|
+
windows: { "grok-4.5": 500_000, "grok-composer-2.5-fast": 200_000 },
|
|
442
495
|
},
|
|
443
496
|
// Native kimi CLI (Phase 48). kimi-code/k3 → frontier: 81.2 FrontierSWE, 88.3 Terminal-Bench 2.1,
|
|
444
497
|
// top-3 across six coding benchmarks (research 2026-07-17, K3 released 2026-07-16).
|
|
@@ -451,6 +504,11 @@ export const DEFAULT_CONFIG = {
|
|
|
451
504
|
"kimi-code/kimi-for-coding": "mid",
|
|
452
505
|
"kimi-code/kimi-for-coding-highspeed": "cheap",
|
|
453
506
|
},
|
|
507
|
+
windows: {
|
|
508
|
+
"kimi-code/k3": 1_048_576,
|
|
509
|
+
"kimi-code/kimi-for-coding": 262_144,
|
|
510
|
+
"kimi-code/kimi-for-coding-highspeed": 262_144,
|
|
511
|
+
},
|
|
454
512
|
},
|
|
455
513
|
},
|
|
456
514
|
pricing: { cheap: 0.1, mid: 0.5, frontier: 2.5 },
|
|
@@ -464,7 +522,7 @@ export const DEFAULT_CONFIG = {
|
|
|
464
522
|
// v1.2: workers are the real agent TUI in the pane; "print" restores the -p-rendered-in-pane path.
|
|
465
523
|
visibility: { llm: "headless", keepPanes: "run", worker: "interactive", workersPerTab: 3 },
|
|
466
524
|
};
|
|
467
|
-
function deepMerge(base, over) {
|
|
525
|
+
function deepMerge(base, over, path = []) {
|
|
468
526
|
if (over === undefined || over === null)
|
|
469
527
|
return base;
|
|
470
528
|
if (Array.isArray(base) || Array.isArray(over) || typeof base !== "object" || typeof over !== "object" || base === null) {
|
|
@@ -473,12 +531,18 @@ function deepMerge(base, over) {
|
|
|
473
531
|
const out = { ...base };
|
|
474
532
|
for (const [k, v] of Object.entries(over)) {
|
|
475
533
|
if (v === null) {
|
|
476
|
-
|
|
534
|
+
// T19: null is a declared multi-vendor state only at tiers.<adapter>.vendor. Every other null
|
|
535
|
+
// remains the v1.1 overlay tombstone (for example a stale tiers model id).
|
|
536
|
+
if (path.length === 2 && path[0] === "tiers" && k === "vendor")
|
|
537
|
+
out[k] = null;
|
|
538
|
+
else
|
|
539
|
+
delete out[k];
|
|
477
540
|
continue;
|
|
478
541
|
}
|
|
479
542
|
// OBS-75 class: merge fresh-landing objects onto {} so nested tombstone nulls are pruned even when
|
|
480
543
|
// no lower layer set the key (a fleet-cleared deny writes {adapters: null} — the schema rejects raw null)
|
|
481
|
-
|
|
544
|
+
const childPath = [...path, k];
|
|
545
|
+
out[k] = k in out ? deepMerge(out[k], v, childPath) : deepMerge({}, v, childPath);
|
|
482
546
|
}
|
|
483
547
|
return out;
|
|
484
548
|
}
|
|
@@ -556,7 +620,7 @@ function resolveRoutingMode(cfg, layers) {
|
|
|
556
620
|
lints.push(`floors.${shape}: ${val} (config floors) overrides mode ${mode} — shadowed delta: ${shape} ${dflt}→${moded}`);
|
|
557
621
|
}
|
|
558
622
|
if (TIER_RANK[val] < TIER_RANK[integrityMin(shape)]) {
|
|
559
|
-
lints.push(`floors.${shape}: ${val} is below integrity minimum frontier — integrity class ${INTEGRITY_FLOOR_SHAPES.join("/")}
|
|
623
|
+
lints.push(`floors.${shape}: ${val} is below integrity minimum frontier — integrity class ${INTEGRITY_FLOOR_SHAPES.join("/")} is advisory to explicit floors and map pins; map pins are supreme`);
|
|
560
624
|
}
|
|
561
625
|
continue;
|
|
562
626
|
}
|
|
@@ -586,6 +650,9 @@ export function loadConfigWithMode(repoRoot, opts = {}) {
|
|
|
586
650
|
const r = TickmarkrConfigSchema.safeParse(merged);
|
|
587
651
|
if (!r.success)
|
|
588
652
|
throw new ConfigError(z.prettifyError(r.error));
|
|
653
|
+
// The vendored table attests Tickmarkr's built-in seed artifact. Operator overlays remain free to
|
|
654
|
+
// declare private/future models and deliberately smaller test windows without pretending T20 cited them.
|
|
655
|
+
assertSeededModelWindows();
|
|
589
656
|
return { cfg: r.data, mode: resolveRoutingMode(r.data, [globalCfg, repoCfg]) };
|
|
590
657
|
}
|
|
591
658
|
export function loadConfig(repoRoot, opts = {}) {
|
|
@@ -642,7 +709,7 @@ export function configTemplate(overlay) {
|
|
|
642
709
|
# llm: headless # headless (default): judge/review/consult run silently | pane: visible agents
|
|
643
710
|
# keepPanes: run # run (default): ephemeral judge/review/consult panes close when read; a merged task's worker pane closes on done; other worker panes persist until run end | attempt | forever (keep everything for debugging)
|
|
644
711
|
# worker: interactive # interactive (default): workers run the real agent TUI | print
|
|
645
|
-
# workersPerTab: 3 # cap
|
|
712
|
+
# workersPerTab: 3 # cap worker panes sharing one tab; gate panes ride with their task and never count. Default 3
|
|
646
713
|
# scope:
|
|
647
714
|
# allowDeviations: [] # globs an operator permits out-of-scope edits into, e.g. ["package-lock.json"]
|
|
648
715
|
# gates: # override auto-detected commands; per-shape participation may only skip LLM gates
|
|
@@ -696,9 +763,8 @@ export function configTemplate(overlay) {
|
|
|
696
763
|
const nl = base.indexOf("\n");
|
|
697
764
|
return `${base.slice(0, nl + 1)}${lines.join("\n")}\n${base.slice(nl + 1)}`;
|
|
698
765
|
}
|
|
699
|
-
//
|
|
700
|
-
|
|
701
|
-
export { FLEET_OVERLAY_KEYS, fleetEditableEquals, fleetRepoOverlayFromDelta, harvestFleetProvenance, repoOverlayYaml, serializeFleetOverlay, unifiedYamlDiff, } from "./fleet-overlay.js";
|
|
766
|
+
// Fleet overlay mutation, legacy serialization, and diff rendering live together in fleet-overlay.ts.
|
|
767
|
+
export { FLEET_OVERLAY_KEYS, fleetEditableEquals, fleetRepoOverlayFromDelta, renderFleetOverlayWrite, repoOverlayYaml, serializeFleetOverlay, unifiedYamlDiff, } from "./fleet-overlay.js";
|
|
702
768
|
export function repoOverlayPath(repoRoot) {
|
|
703
769
|
return join(repoRoot, stateDirName(repoRoot), "config.yaml");
|
|
704
770
|
}
|
|
@@ -710,15 +776,12 @@ export function readOverlayFile(path) {
|
|
|
710
776
|
throw new ConfigError(`invalid overlay at ${path}`);
|
|
711
777
|
return raw;
|
|
712
778
|
}
|
|
713
|
-
export function fleetEditableFromConfig(cfg
|
|
779
|
+
export function fleetEditableFromConfig(cfg) {
|
|
714
780
|
const tiers = {};
|
|
715
781
|
for (const [adapter, entry] of Object.entries(cfg.tiers)) {
|
|
716
782
|
tiers[adapter] = {};
|
|
717
783
|
for (const [model, tier] of Object.entries(entry.models)) {
|
|
718
|
-
|
|
719
|
-
// write knows about every existing note — not only the ones this session typed itself
|
|
720
|
-
const note = provenance[adapter]?.[model];
|
|
721
|
-
tiers[adapter][model] = note ? { tier, provenance: note } : { tier };
|
|
784
|
+
tiers[adapter][model] = { tier };
|
|
722
785
|
}
|
|
723
786
|
}
|
|
724
787
|
return {
|
|
@@ -754,55 +817,24 @@ export function fleetKeyLayer(repoRoot, dotted, opts = {}) {
|
|
|
754
817
|
export function formatFleetPrint(repoRoot, opts = {}) {
|
|
755
818
|
const gdir = opts.globalDir ?? globalConfigDir();
|
|
756
819
|
const effective = loadConfig(repoRoot, { globalDir: gdir });
|
|
757
|
-
const
|
|
758
|
-
|
|
759
|
-
const annotate = (
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
lines.push("routing:");
|
|
764
|
-
lines.push(" deny:");
|
|
765
|
-
if (denyAdapters.length)
|
|
766
|
-
lines.push(annotate("routing.deny.adapters", ` adapters: ${stringify(denyAdapters).trim()}`));
|
|
767
|
-
if (denyModels.length)
|
|
768
|
-
lines.push(annotate("routing.deny.models", ` models: ${stringify(denyModels).trim()}`));
|
|
769
|
-
}
|
|
770
|
-
const mapKeys = Object.keys(editable.map).sort();
|
|
771
|
-
if (mapKeys.length) {
|
|
772
|
-
if (!lines.some((l) => l === "routing:"))
|
|
773
|
-
lines.push("routing:");
|
|
774
|
-
lines.push(" map:");
|
|
775
|
-
for (const shape of mapKeys) {
|
|
776
|
-
const entry = editable.map[shape];
|
|
777
|
-
const body = stringify(entry, { indent: 4 }).trim().split("\n").map((l) => ` ${l}`).join("\n");
|
|
778
|
-
lines.push(annotate(`routing.map.${shape}`, ` ${shape}:`));
|
|
779
|
-
lines.push(body.split("\n").slice(1).join("\n"));
|
|
780
|
-
}
|
|
781
|
-
}
|
|
782
|
-
const floorKeys = Object.keys(editable.floors).sort();
|
|
783
|
-
if (floorKeys.length) {
|
|
784
|
-
if (!lines.some((l) => l === "routing:"))
|
|
785
|
-
lines.push("routing:");
|
|
786
|
-
lines.push(annotate("routing.floors", ` floors: ${stringify(Object.fromEntries(floorKeys.map((k) => [k, editable.floors[k]]))).trim().replace(/^/gm, " ")}`));
|
|
787
|
-
}
|
|
788
|
-
const tierAdapters = Object.keys(editable.tiers).sort();
|
|
789
|
-
if (tierAdapters.length) {
|
|
790
|
-
lines.push("tiers:");
|
|
791
|
-
for (const adapter of tierAdapters) {
|
|
792
|
-
const models = editable.tiers[adapter];
|
|
793
|
-
const modelIds = Object.keys(models).sort();
|
|
794
|
-
if (!modelIds.length)
|
|
795
|
-
continue;
|
|
796
|
-
lines.push(` ${adapter}:`);
|
|
797
|
-
lines.push(" models:");
|
|
798
|
-
for (const model of modelIds) {
|
|
799
|
-
const v = models[model];
|
|
800
|
-
if (v === null)
|
|
801
|
-
lines.push(` ${model}: null`);
|
|
802
|
-
else
|
|
803
|
-
lines.push(annotate(`tiers.${adapter}.models.${model}`, ` ${model}: ${v.tier}`));
|
|
804
|
-
}
|
|
820
|
+
const doc = new Document({ routing: effective.routing, tiers: effective.tiers });
|
|
821
|
+
doc.commentBefore = " tickmarkr fleet — effective state (repo > global > defaults)";
|
|
822
|
+
const annotate = (path, dotted = path.join(".")) => {
|
|
823
|
+
const node = doc.getIn(path, true);
|
|
824
|
+
if (node && typeof node === "object" && "comment" in node) {
|
|
825
|
+
node.comment = ` ${fleetKeyLayer(repoRoot, dotted, opts)}`;
|
|
805
826
|
}
|
|
827
|
+
};
|
|
828
|
+
if (effective.routing.deny?.adapters !== undefined)
|
|
829
|
+
annotate(["routing", "deny", "adapters"]);
|
|
830
|
+
if (effective.routing.deny?.models !== undefined)
|
|
831
|
+
annotate(["routing", "deny", "models"]);
|
|
832
|
+
for (const shape of Object.keys(effective.routing.map))
|
|
833
|
+
annotate(["routing", "map", shape]);
|
|
834
|
+
annotate(["routing", "floors"]);
|
|
835
|
+
for (const [adapter, entry] of Object.entries(effective.tiers)) {
|
|
836
|
+
for (const model of Object.keys(entry.models))
|
|
837
|
+
annotate(["tiers", adapter, "models", model]);
|
|
806
838
|
}
|
|
807
|
-
return
|
|
839
|
+
return String(doc);
|
|
808
840
|
}
|
|
@@ -1,27 +1,32 @@
|
|
|
1
|
-
import type { FleetEditable } from "./config.js";
|
|
1
|
+
import type { FleetEditable, RoutingMode } from "./config.js";
|
|
2
2
|
/** Fleet-owned overlay keys — the only config surface `tickmarkr fleet` may write. */
|
|
3
3
|
export declare const FLEET_OVERLAY_KEYS: readonly ["routing", "tiers"];
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
export type FleetOverlayWrite = {
|
|
5
|
+
initial: FleetEditable;
|
|
6
|
+
edited: FleetEditable;
|
|
7
|
+
mode?: RoutingMode;
|
|
8
|
+
steering?: {
|
|
9
|
+
initial: {
|
|
10
|
+
review?: string[];
|
|
11
|
+
consult?: string[];
|
|
12
|
+
};
|
|
13
|
+
edited: {
|
|
14
|
+
review?: string[];
|
|
15
|
+
consult?: string[];
|
|
16
|
+
};
|
|
17
|
+
};
|
|
10
18
|
};
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
* benchmark-provenance note survived exactly one write. Fail-open to empty: an unreadable
|
|
15
|
-
* overlay is the loader's problem to reject, never the harvester's. */
|
|
16
|
-
export declare function harvestFleetProvenance(overlayText: string): HarvestedProvenance;
|
|
17
|
-
/** Harvested deny-entry notes keyed by the exact entry string, re-attached at serialize time. */
|
|
18
|
-
export type FleetDenyNotes = {
|
|
19
|
-
adapters?: Record<string, string>;
|
|
20
|
-
models?: Record<string, string>;
|
|
19
|
+
export type FleetFirstTouch = {
|
|
20
|
+
vendor: string;
|
|
21
|
+
channel: "sub" | "api";
|
|
21
22
|
};
|
|
23
|
+
export declare function fleetFirstTouchProvenance(note: string, firstTouch: FleetFirstTouch): string;
|
|
24
|
+
/** Apply only fields fleet authored to the parsed YAML document. Untouched nodes retain their
|
|
25
|
+
* keys, ordering, scalar style, and comments; fresh provenance is written directly on its tier node. */
|
|
26
|
+
export declare function renderFleetOverlayWrite(priorBytes: string, write: FleetOverlayWrite): string;
|
|
22
27
|
/** Build the repo overlay fragment fleet would write for edits since session start. */
|
|
23
|
-
export declare function fleetRepoOverlayFromDelta(initial: FleetEditable, edited: FleetEditable, existingRepo?: Record<string, unknown
|
|
24
|
-
export declare function repoOverlayYaml(overlay: Record<string, unknown
|
|
25
|
-
export declare function serializeFleetOverlay(overlay: Record<string, unknown
|
|
28
|
+
export declare function fleetRepoOverlayFromDelta(initial: FleetEditable, edited: FleetEditable, existingRepo?: Record<string, unknown>, firstTouches?: Readonly<Record<string, FleetFirstTouch>>): Record<string, unknown>;
|
|
29
|
+
export declare function repoOverlayYaml(overlay: Record<string, unknown>): string;
|
|
30
|
+
export declare function serializeFleetOverlay(overlay: Record<string, unknown>): string;
|
|
26
31
|
export declare function unifiedYamlDiff(before: string, after: string, label?: string): string;
|
|
27
32
|
export declare function fleetEditableEquals(a: FleetEditable, b: FleetEditable): boolean;
|