tickmarkr 2.6.8 → 2.6.9
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/dist/adapters/catalog-remote.js +5 -2
- package/dist/adapters/codex.d.ts +1 -0
- package/dist/adapters/codex.js +18 -10
- package/dist/adapters/model-lints.js +21 -11
- package/dist/adapters/model-windows.js +0 -1
- package/dist/adapters/registry.js +7 -3
- package/dist/adapters/types.d.ts +2 -0
- package/dist/cli/commands/doctor.js +3 -0
- package/dist/config/config.js +6 -5
- package/dist/tui/ink/fleet-app.js +25 -9
- package/package.json +4 -1
|
@@ -505,9 +505,12 @@ export async function refreshCatalogCommand(opts) {
|
|
|
505
505
|
let modelsDev = current.catalog.modelsDev;
|
|
506
506
|
let modelsDevUpdated = false;
|
|
507
507
|
try {
|
|
508
|
-
|
|
509
|
-
|
|
508
|
+
// v2.6.9 (queue row 97): validate before the payload replaces the spine — an invalid payload left in
|
|
509
|
+
// modelsDev would be written beside an updated sibling leg and make the whole cache unreadable.
|
|
510
|
+
const fetched = await fetchCatalog(fetcher, MODELS_DEV_CATALOG_URL, {}, timeoutMs, (r) => r.json());
|
|
511
|
+
if (!validModelsDevCatalog(fetched))
|
|
510
512
|
throw new Error("models.dev catalog schema is invalid");
|
|
513
|
+
modelsDev = fetched;
|
|
511
514
|
modelsDevUpdated = true;
|
|
512
515
|
legAt.modelsDev = stamp;
|
|
513
516
|
legs.push({ leg: "models.dev", status: "updated", detail: "fetched", retry: "next retry after this leg is stale" });
|
package/dist/adapters/codex.d.ts
CHANGED
|
@@ -3,6 +3,7 @@ export declare function readCodexModelsCache(path?: string): {
|
|
|
3
3
|
models: string[];
|
|
4
4
|
fetchedAt?: string;
|
|
5
5
|
retirements?: Record<string, ModelRetirement | null>;
|
|
6
|
+
hidden?: string[];
|
|
6
7
|
};
|
|
7
8
|
export declare const CODEX_GIT_GRANT = "-c \"sandbox_workspace_write.writable_roots=[$(tkr_common=$(git rev-parse --path-format=absolute --git-common-dir); tkr_gitdir=$(git rev-parse --absolute-git-dir); if [ \"$tkr_gitdir\" != \"$tkr_common\" ]; then printf '\"%s\",' \"$tkr_gitdir\"; fi; printf '\"%s/objects\",\"%s/refs\",\"%s/logs\"' \"$tkr_common\" \"$tkr_common\" \"$tkr_common\")]\"";
|
|
8
9
|
export declare const CODEX_TRUST_DIALOG: TrustDialog;
|
package/dist/adapters/codex.js
CHANGED
|
@@ -61,23 +61,29 @@ function codexSessionDayDirs(sessions, sinceMs) {
|
|
|
61
61
|
// Fails OPEN to { models: [] } on missing/corrupt cache. Version drift observed (installed CLI
|
|
62
62
|
// 0.143.0 vs cache client_version 0.144.0, 2026-07-10) — hence the defensive try/catch and no shape
|
|
63
63
|
// assumptions. CODEX_HOME is codex's own relocation env. fetchedAt is the honest codex knowledge age.
|
|
64
|
-
// v2.6.4 T2 (B1a): each
|
|
65
|
-
//
|
|
66
|
-
//
|
|
67
|
-
//
|
|
64
|
+
// v2.6.4 T2 (B1a): each entry's `upgrade` is the CLI's own retirement notice; its successor is whatever
|
|
65
|
+
// that notice names — never a fixed id in this file. A listed row with upgrade: null records known clean;
|
|
66
|
+
// a malformed or absent `upgrade` records nothing, so that id stays unknown. v2.6.9 (queue row 64): the CLI
|
|
67
|
+
// HIDES a model before it retires it (0.162.0, 2026-10-08: gpt-5.5 visibility "hide" with a dated
|
|
68
|
+
// upgrade), so a valid dated notice is read from hidden rows too; a hidden row still never lists, and a
|
|
69
|
+
// hidden row without a dated notice records nothing (delisted is not known clean). `hidden` names every
|
|
70
|
+
// visibility "hide" row (D-1526: routing evidence). An unreadable cache returns neither (unknown, never clean).
|
|
68
71
|
export function readCodexModelsCache(path) {
|
|
69
72
|
const p = path ?? join(process.env.CODEX_HOME || join(homedir(), ".codex"), "models_cache.json");
|
|
70
73
|
try {
|
|
71
74
|
const d = JSON.parse(readFileSync(p, "utf8"));
|
|
72
|
-
const
|
|
73
|
-
.filter((m) =>
|
|
75
|
+
const rows = (d.models ?? [])
|
|
76
|
+
.filter((m) => typeof m?.slug === "string" && m.slug.length > 0 && MODEL_ID_RE.test(m.slug));
|
|
77
|
+
const listed = rows.filter((m) => m.visibility === "list");
|
|
74
78
|
const retirements = {};
|
|
75
|
-
for (const m of
|
|
79
|
+
for (const m of rows) {
|
|
76
80
|
const notice = codexRetirement(m.upgrade);
|
|
77
|
-
if (notice !==
|
|
78
|
-
|
|
81
|
+
if (notice === undefined || (notice === null && m.visibility !== "list"))
|
|
82
|
+
continue;
|
|
83
|
+
retirements[m.slug] = notice;
|
|
79
84
|
}
|
|
80
|
-
|
|
85
|
+
const hidden = rows.filter((m) => m.visibility === "hide").map((m) => m.slug);
|
|
86
|
+
return { models: listed.map((m) => m.slug), fetchedAt: typeof d.fetched_at === "string" ? d.fetched_at : undefined, retirements, hidden };
|
|
81
87
|
}
|
|
82
88
|
catch {
|
|
83
89
|
return { models: [] };
|
|
@@ -305,6 +311,8 @@ export const codex = {
|
|
|
305
311
|
listModelsFetchedAt: () => readCodexModelsCache().fetchedAt,
|
|
306
312
|
// v2.6.4 T2 (B1a): the cache's per-model retirement notices, persisted beside models in doctor.json.
|
|
307
313
|
listModelsRetirements: () => readCodexModelsCache().retirements,
|
|
314
|
+
// v2.6.9 (D-1526): the ids the cache marks hidden, persisted beside models as routing evidence.
|
|
315
|
+
listModelsHidden: () => readCodexModelsCache().hidden,
|
|
308
316
|
collectUsage(cwd, sinceMs) {
|
|
309
317
|
try {
|
|
310
318
|
const real = realpathSync(cwd);
|
|
@@ -497,13 +497,15 @@ function storedRetirement(record, model) {
|
|
|
497
497
|
return { retiresAt: n.retiresAt };
|
|
498
498
|
return typeof n.successor === "string" && MODEL_ID_RE.test(n.successor) ? { retiresAt: n.retiresAt, successor: n.successor } : undefined;
|
|
499
499
|
}
|
|
500
|
-
// Configured
|
|
501
|
-
//
|
|
502
|
-
//
|
|
503
|
-
|
|
500
|
+
// Configured models the CLI lists, plus configured models it stopped listing that still carry a dated
|
|
501
|
+
// notice (the CLI hides a model before retiring it); any other unlisted id carries the bare tombstone
|
|
502
|
+
// advisory instead. The CLI's notice is quoted as-is: its successor is the CLI's upgrade target, never
|
|
503
|
+
// a tier or benchmark claim, and no model name or visibility is read as one. The notice never changes routing;
|
|
504
|
+
// the CLI's hide does (D-1526), so a hidden id's lint says it is not routed instead of "routing unchanged".
|
|
505
|
+
function retirementLints(id, configured, listed, hiddenIds, record, nowMs) {
|
|
504
506
|
const lints = [];
|
|
505
507
|
const unknown = [];
|
|
506
|
-
for (const model of
|
|
508
|
+
for (const model of configured) {
|
|
507
509
|
const notice = storedRetirement(record, model);
|
|
508
510
|
if (notice === undefined) {
|
|
509
511
|
unknown.push(model);
|
|
@@ -511,12 +513,14 @@ function retirementLints(id, listedConfigured, record, nowMs) {
|
|
|
511
513
|
}
|
|
512
514
|
if (!notice)
|
|
513
515
|
continue; // listed with no notice: known clean
|
|
516
|
+
const hidden = listed.includes(model) ? "" : " (the CLI no longer lists it)";
|
|
517
|
+
const routing = hiddenIds.includes(model) && !listed.includes(model) ? "not routed while the CLI hides it" : "routing unchanged";
|
|
514
518
|
const successor = notice.successor
|
|
515
519
|
? `successor ${notice.successor} (named by the CLI — not a tier claim; classify it per benchmark policy)`
|
|
516
520
|
: "no successor named";
|
|
517
521
|
lints.push(nowMs >= Date.parse(notice.retiresAt)
|
|
518
|
-
? `${id}: ${model} retired ${notice.retiresAt} per the CLI's notice; ${successor} — tombstone it (${model}: null overlay) or verify the id (advisory — routing
|
|
519
|
-
: `${id}: ${model} retires ${notice.retiresAt} per the CLI's notice; ${successor} — tombstone it (${model}: null overlay) before then (advisory — routing
|
|
522
|
+
? `${id}: ${model} retired ${notice.retiresAt} per the CLI's notice${hidden}; ${successor} — tombstone it (${model}: null overlay) or verify the id (advisory — ${routing})`
|
|
523
|
+
: `${id}: ${model} retires ${notice.retiresAt} per the CLI's notice${hidden}; ${successor} — tombstone it (${model}: null overlay) before then (advisory — ${routing})`);
|
|
520
524
|
}
|
|
521
525
|
if (unknown.length) {
|
|
522
526
|
const why = record && typeof record === "object"
|
|
@@ -555,19 +559,25 @@ export function modelLints(cfg, health, adapters, opts) {
|
|
|
555
559
|
}
|
|
556
560
|
const h = health[id];
|
|
557
561
|
const detected = h?.models ?? []; // MANDATORY default: pre-v1.5 files lack a populated models array
|
|
562
|
+
const configured = Object.keys(cfg.tiers[id]?.models ?? {});
|
|
563
|
+
// a configured id the CLI stopped listing but still carries a dated notice for gets the dated lint, not the bare one
|
|
564
|
+
const noticed = (model) => !!adapter.listModelsRetirements && !!storedRetirement(h?.modelRetirements, model);
|
|
558
565
|
if (detected.length === 0) {
|
|
559
566
|
if (h?.installed)
|
|
560
567
|
lints.push(`${id}: no detection data — run tickmarkr doctor`);
|
|
568
|
+
// D-1526: a cache holding only hidden rows lists nothing, yet its dated notices are still the CLI's word
|
|
569
|
+
if (adapter.listModelsRetirements)
|
|
570
|
+
lints.push(...retirementLints(id, configured.filter(noticed), detected, h?.modelsHidden ?? [], h?.modelRetirements, nowMs));
|
|
561
571
|
continue; // no data to diff or age
|
|
562
572
|
}
|
|
563
|
-
const configured = Object.keys(cfg.tiers[id]?.models ?? {});
|
|
564
573
|
for (const model of configured) {
|
|
565
|
-
if (!detected.includes(model)) {
|
|
574
|
+
if (!detected.includes(model) && !noticed(model)) {
|
|
566
575
|
lints.push(`${id}: tiers lists ${model} — CLI no longer reports it; tombstone it (${model}: null overlay) or verify the id`);
|
|
567
576
|
}
|
|
568
577
|
}
|
|
569
|
-
if (adapter.listModelsRetirements)
|
|
570
|
-
lints.push(...retirementLints(id, configured.filter((m) => detected.includes(m)), h?.modelRetirements, nowMs));
|
|
578
|
+
if (adapter.listModelsRetirements) {
|
|
579
|
+
lints.push(...retirementLints(id, configured.filter((m) => detected.includes(m) || noticed(m)), detected, h?.modelsHidden ?? [], h?.modelRetirements, nowMs));
|
|
580
|
+
}
|
|
571
581
|
const extra = collapseUnclassified(detected, new Set(configured));
|
|
572
582
|
if (extra.length) {
|
|
573
583
|
const shown = extra.slice(0, cap).map((row) => row.model).join(", ");
|
|
@@ -49,7 +49,6 @@ const VENDORED_MODEL_WINDOW_CLAIMS = [
|
|
|
49
49
|
{ modelId: "gpt-6-sol", window: 272_000, source: CODEX_CLI_CACHE_SOURCE, readDate: CODEX_CLI_CACHE_READ_DATE },
|
|
50
50
|
{ modelId: "gpt-6-luna", window: 272_000, source: CODEX_CLI_CACHE_SOURCE, readDate: CODEX_CLI_CACHE_READ_DATE },
|
|
51
51
|
{ modelId: "gpt-5.6-sol", window: 272_000, source: CODEX_CLI_CACHE_SOURCE, readDate: CODEX_CLI_CACHE_READ_DATE },
|
|
52
|
-
{ modelId: "gpt-5.5", window: 272_000, source: CODEX_CLI_CACHE_SOURCE, readDate: CODEX_CLI_CACHE_READ_DATE },
|
|
53
52
|
{ modelId: "gpt-5.6-terra", window: 272_000, source: CODEX_CLI_CACHE_SOURCE, readDate: CODEX_CLI_CACHE_READ_DATE },
|
|
54
53
|
{ modelId: "gpt-5.6-luna", window: 272_000, source: CODEX_CLI_CACHE_SOURCE, readDate: CODEX_CLI_CACHE_READ_DATE },
|
|
55
54
|
{ modelId: "composer-2.5", window: 200_000, source: CURSOR_SOURCE, readDate: READ_DATE },
|
|
@@ -107,14 +107,18 @@ function modelListValidated(h) {
|
|
|
107
107
|
return !!h?.modelsDetectedAt && Array.isArray(h.models);
|
|
108
108
|
}
|
|
109
109
|
export function invalidConfiguredModels(cfg, adapterId, h) {
|
|
110
|
+
const configured = Object.keys(cfg.tiers[adapterId]?.models ?? {});
|
|
111
|
+
// v2.6.9 (queue row 64, D-1526): an id the CLI's own list marks hidden is positive evidence it stopped offering
|
|
112
|
+
// it — out whatever the overlap below. The fail-open that follows covers only ids with no such evidence.
|
|
113
|
+
const hidden = new Set(h?.modelsHidden ?? []);
|
|
114
|
+
const hiddenConfigured = configured.filter((m) => hidden.has(m) && !(h?.models ?? []).includes(m));
|
|
110
115
|
if (!modelListValidated(h))
|
|
111
|
-
return
|
|
116
|
+
return hiddenConfigured;
|
|
112
117
|
const listed = new Set(h.models);
|
|
113
|
-
const configured = Object.keys(cfg.tiers[adapterId]?.models ?? {});
|
|
114
118
|
// Fail open until the CLI list intersects at least one configured model — proves the list is authoritative
|
|
115
119
|
// for this adapter's tier namespace (MODEL-04: unrelated detected ids must not invalidate routing).
|
|
116
120
|
if (!configured.some((m) => listed.has(m)))
|
|
117
|
-
return
|
|
121
|
+
return hiddenConfigured;
|
|
118
122
|
return configured.filter((m) => !listed.has(m));
|
|
119
123
|
}
|
|
120
124
|
export function modelAliasExclusions(cfg, adapters, health) {
|
package/dist/adapters/types.d.ts
CHANGED
|
@@ -51,6 +51,7 @@ export interface AuthHealth {
|
|
|
51
51
|
modelAuth?: Record<string, ModelAuth>;
|
|
52
52
|
modelIdentities?: Record<string, string>;
|
|
53
53
|
modelRetirements?: Record<string, ModelRetirement | null>;
|
|
54
|
+
modelsHidden?: string[];
|
|
54
55
|
codexCommit?: CodexCommitStatus;
|
|
55
56
|
launchServices?: LaunchServicesRecord;
|
|
56
57
|
}
|
|
@@ -206,6 +207,7 @@ export interface WorkerAdapter {
|
|
|
206
207
|
listModels?(): Promise<string[]>;
|
|
207
208
|
listModelsFetchedAt?(): string | undefined;
|
|
208
209
|
listModelsRetirements?(): Record<string, ModelRetirement | null> | undefined;
|
|
210
|
+
listModelsHidden?(): string[] | undefined;
|
|
209
211
|
collectUsage?(cwd: string, sinceMs: number): TokenUsage | undefined;
|
|
210
212
|
contextUsage?(session: SessionRef): ContextUsage | null;
|
|
211
213
|
readSessionTranscript?(session: SessionRef): {
|
|
@@ -600,6 +600,9 @@ export async function doctor(_argv, cwd = process.cwd(), adapters = allAdapters(
|
|
|
600
600
|
const retirements = a.listModelsRetirements?.();
|
|
601
601
|
if (retirements)
|
|
602
602
|
health[a.id].modelRetirements = retirements;
|
|
603
|
+
const hidden = a.listModelsHidden?.(); // v2.6.9 (D-1526): the CLI's hidden flag, read by routing
|
|
604
|
+
if (hidden)
|
|
605
|
+
health[a.id].modelsHidden = hidden;
|
|
603
606
|
}
|
|
604
607
|
catch { /* fail open: leave models as-is, doctor stays healthy */ }
|
|
605
608
|
}
|
package/dist/config/config.js
CHANGED
|
@@ -551,20 +551,21 @@ export const DEFAULT_CONFIG = {
|
|
|
551
551
|
// astra=frontier and luna=cheap follow the provider's own descriptions in the local cache (read
|
|
552
552
|
// 2026-09-29: "Frontier intelligence for the most demanding work" / "Fast and affordable model for
|
|
553
553
|
// easier tasks"); sol=frontier keeps the shipped workhorse-family mapping (gpt-5.6-sol above) by
|
|
554
|
-
// continuity. Learned routing or an overlay may retier on evidence. gpt-5.5
|
|
555
|
-
// CLI
|
|
556
|
-
//
|
|
554
|
+
// continuity. Learned routing or an overlay may retier on evidence. v2.6.9 (queue row 64): gpt-5.5 left
|
|
555
|
+
// the seed — the CLI hides it and announces its retirement (2026-10-14T19:00:00Z); its successor is
|
|
556
|
+
// whatever the CLI's notice names (doctor quotes it), never a fixed id here.
|
|
557
|
+
// Windows: the same snapshot lists context_window=272000 for the six still seeded — the budget the CLI launches
|
|
557
558
|
// with, and the authority for these seeds. The API's published 1,050,000 is a different surface and is
|
|
558
559
|
// not contradicted. A later cache listing more ids (gpt-6.1-sol) does not widen this sealed set.
|
|
559
560
|
codex: {
|
|
560
561
|
vendor: "openai", channel: "sub",
|
|
561
562
|
models: {
|
|
562
563
|
"gpt-6-astra": "frontier", "gpt-6-sol": "frontier", "gpt-6-luna": "cheap",
|
|
563
|
-
"gpt-5.6-sol": "frontier", "gpt-5.
|
|
564
|
+
"gpt-5.6-sol": "frontier", "gpt-5.6-terra": "mid", "gpt-5.6-luna": "cheap",
|
|
564
565
|
},
|
|
565
566
|
windows: {
|
|
566
567
|
"gpt-6-astra": 272_000, "gpt-6-sol": 272_000, "gpt-6-luna": 272_000,
|
|
567
|
-
"gpt-5.6-sol": 272_000, "gpt-5.
|
|
568
|
+
"gpt-5.6-sol": 272_000, "gpt-5.6-terra": 272_000, "gpt-5.6-luna": 272_000,
|
|
568
569
|
},
|
|
569
570
|
},
|
|
570
571
|
// grok-4.5 (xAI, released 2026-07-08) → mid: AA Intelligence 54 (#4), Terminal-Bench 2.1 83.3%
|
|
@@ -44,6 +44,18 @@ function ledgerChannelId(line) {
|
|
|
44
44
|
const slash = head.indexOf("/");
|
|
45
45
|
return slash === -1 ? undefined : `${head.slice(0, slash)}:${head.slice(slash + 1)}`;
|
|
46
46
|
}
|
|
47
|
+
/**
|
|
48
|
+
* Space picks exactly as Enter does in every single-select overlay (operator field report 2026-10-08,
|
|
49
|
+
* "can't choose here space key doesn't select" — the reach picker even OPENS on Space). Space keeps its
|
|
50
|
+
* own meaning only where it has one: the multi-select chains (candidates, prefer) toggle on it, the text
|
|
51
|
+
* stages (add-model, the classify note) type it, and the review diff confirms only on `y`. The lift
|
|
52
|
+
* listing is a confirm, not a pick: it clears a deny entry for every channel that entry covers, so only
|
|
53
|
+
* Enter lifts — a stray Space there does nothing.
|
|
54
|
+
*/
|
|
55
|
+
const spacePicks = (overlay) => overlay.kind === "classify"
|
|
56
|
+
? overlay.stage !== "note"
|
|
57
|
+
: overlay.kind === "presets" || overlay.kind === "assign" || overlay.kind === "poolmode"
|
|
58
|
+
|| overlay.kind === "judge" || overlay.kind === "reach";
|
|
47
59
|
const REACH_CHOICES = [
|
|
48
60
|
{ id: "in", label: "in", gloss: "every seat may route here" },
|
|
49
61
|
{ id: "out-workers", label: "out · workers", gloss: "workers skip it; judge/review/consult unaffected" },
|
|
@@ -991,6 +1003,7 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
991
1003
|
return true;
|
|
992
1004
|
};
|
|
993
1005
|
if (overlay) {
|
|
1006
|
+
const pick = key.return || (input === " " && spacePicks(overlay));
|
|
994
1007
|
if (overlay.kind === "presets") {
|
|
995
1008
|
const rowCount = modeOptions.length + (overlay.home ? 1 : 0);
|
|
996
1009
|
if (key.escape) {
|
|
@@ -1015,7 +1028,7 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
1015
1028
|
bump();
|
|
1016
1029
|
return;
|
|
1017
1030
|
}
|
|
1018
|
-
if (
|
|
1031
|
+
if (pick) {
|
|
1019
1032
|
if (overlay.home && overlay.at === modeOptions.length) {
|
|
1020
1033
|
setOverlay(null); // custom → the browser IS the full editor
|
|
1021
1034
|
return;
|
|
@@ -1065,8 +1078,10 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
1065
1078
|
finish({ kind: "discard" });
|
|
1066
1079
|
return;
|
|
1067
1080
|
}
|
|
1081
|
+
// the review screen holds every staged edit by definition — `q` takes the same two presses
|
|
1082
|
+
// as every other screen, never one keystroke to lose the session (`n` is the explicit discard)
|
|
1068
1083
|
if (input === "q") {
|
|
1069
|
-
|
|
1084
|
+
guardedQuit(armed);
|
|
1070
1085
|
return;
|
|
1071
1086
|
}
|
|
1072
1087
|
if (input === "y") {
|
|
@@ -1097,7 +1112,7 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
1097
1112
|
bump();
|
|
1098
1113
|
return;
|
|
1099
1114
|
}
|
|
1100
|
-
if (
|
|
1115
|
+
if (pick) {
|
|
1101
1116
|
const channel = CHANNELS[overlay.channelAt];
|
|
1102
1117
|
ui.channelByAdapter[overlay.adapter] = channel;
|
|
1103
1118
|
// OBS-508: a bulk stage waiting on the one first-touch channel answer resumes here.
|
|
@@ -1125,7 +1140,7 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
1125
1140
|
bump();
|
|
1126
1141
|
return;
|
|
1127
1142
|
}
|
|
1128
|
-
if (
|
|
1143
|
+
if (pick) {
|
|
1129
1144
|
// OBS-508: keep the suggested band → the evidence note arrives pre-typed; override the
|
|
1130
1145
|
// band → the note starts empty (the suggestion argues for a DIFFERENT tier).
|
|
1131
1146
|
const chosen = TIERS[overlay.tierAt];
|
|
@@ -1208,7 +1223,7 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
1208
1223
|
bump();
|
|
1209
1224
|
return;
|
|
1210
1225
|
}
|
|
1211
|
-
if (
|
|
1226
|
+
if (pick && rows[overlay.at]) {
|
|
1212
1227
|
const shape = rows[overlay.at].id;
|
|
1213
1228
|
ui.map = { ...ui.map, [shape]: { pin: { via: overlay.adapter, model: overlay.model } } };
|
|
1214
1229
|
setOverlay(null);
|
|
@@ -1305,7 +1320,7 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
1305
1320
|
bump();
|
|
1306
1321
|
return;
|
|
1307
1322
|
}
|
|
1308
|
-
if (
|
|
1323
|
+
if (pick) {
|
|
1309
1324
|
const { shape, chain } = overlay.picker;
|
|
1310
1325
|
// a pool is the shape's WHOLE declaration: pin and prefer leave with it (schema
|
|
1311
1326
|
// exclusivity — pin+pool and pool+prefer both fail config load)
|
|
@@ -1383,7 +1398,7 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
1383
1398
|
bump();
|
|
1384
1399
|
return;
|
|
1385
1400
|
}
|
|
1386
|
-
if (
|
|
1401
|
+
if (pick) {
|
|
1387
1402
|
const choice = REACH_CHOICES[overlay.at].id;
|
|
1388
1403
|
setOverlay(null);
|
|
1389
1404
|
applyReach(overlay.target, choice);
|
|
@@ -1420,7 +1435,8 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
1420
1435
|
bump();
|
|
1421
1436
|
return;
|
|
1422
1437
|
}
|
|
1423
|
-
|
|
1438
|
+
// Space picks too (spacePicks): a judge picker that swallowed it read as "can't choose"
|
|
1439
|
+
if (pick && rows[overlay.at]) {
|
|
1424
1440
|
// single-select: one seat or back to the config default — never a chain
|
|
1425
1441
|
const picked = rows[overlay.at];
|
|
1426
1442
|
ui.judgeSeat = picked === judgeKeepRow ? null : picked;
|
|
@@ -2045,7 +2061,7 @@ export function FleetApp({ ageMs, agents, initialAllowOut = [], initialEfforts =
|
|
|
2045
2061
|
const rows = [judgeKeepRow, ...judgeSeats].filter((row) => matches(row, ui.filter));
|
|
2046
2062
|
const { visible, start, above, below } = windowRows(rows, overlay.at, capacity);
|
|
2047
2063
|
const selectedSeat = ui.judgeSeat ?? judgeKeepRow;
|
|
2048
|
-
return (_jsxs(OverlayPanel, { title: "pick \u00B7 judge", width: bodyW, children: [_jsx(Text, { dimColor: true, children: "one seat judges acceptance criteria
|
|
2064
|
+
return (_jsxs(OverlayPanel, { title: "pick \u00B7 judge", width: bodyW, children: [_jsx(Text, { dimColor: true, wrap: "truncate", children: clip("Space or Enter picks · Esc keeps · one seat judges acceptance criteria — failover stays runtime (GATE-09)", bodyW - 4) }), _jsx(SearchRow, { filter: ui.filter, active: true }), above > 0 && _jsx(ElisionMark, { count: above, side: "above" }), visible.map((label, index) => (_jsxs(Text, { children: [_jsx(Pointer, { on: start + index === overlay.at }), label === selectedSeat ? _jsx(Glyph, { kind: "on" }) : _jsx(Text, { children: " " }), _jsx(Text, { bold: start + index === overlay.at, children: ` ${clip(label, bodyW - 10)}` })] }, label))), below > 0 && _jsx(ElisionMark, { count: below, side: "below" })] }));
|
|
2049
2065
|
})();
|
|
2050
2066
|
const modelVisibilityLine = () => {
|
|
2051
2067
|
const hidden = hiddenModelCount();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tickmarkr",
|
|
3
|
-
"version": "2.6.
|
|
3
|
+
"version": "2.6.9",
|
|
4
4
|
"description": "Spec in, verified work out.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -114,6 +114,9 @@
|
|
|
114
114
|
"scripts/export-public.sh",
|
|
115
115
|
"scripts/verify-export.sh",
|
|
116
116
|
"tests/scripts/verify-export.test.ts",
|
|
117
|
+
"scripts/check-analysis.mjs",
|
|
118
|
+
"scripts/atlas.mjs",
|
|
119
|
+
"tests/docs/analysis-library.test.ts",
|
|
117
120
|
".github/workflows/ci.yml",
|
|
118
121
|
"**/*.local.*"
|
|
119
122
|
]
|