tickmarkr 2.6.3 → 2.6.5
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 +10 -2
- package/dist/adapters/catalog-remote.d.ts +23 -0
- package/dist/adapters/catalog-remote.js +38 -0
- package/dist/adapters/claude-code.d.ts +1 -1
- package/dist/adapters/claude-code.js +4 -1
- package/dist/adapters/codex-commit-check.d.ts +25 -0
- package/dist/adapters/codex-commit-check.js +108 -0
- package/dist/adapters/codex.d.ts +2 -1
- package/dist/adapters/codex.js +26 -5
- package/dist/adapters/model-discovery-check.d.ts +33 -0
- package/dist/adapters/model-discovery-check.js +123 -0
- package/dist/adapters/model-lints.d.ts +2 -1
- package/dist/adapters/model-lints.js +52 -1
- package/dist/adapters/model-windows.d.ts +2 -1
- package/dist/adapters/model-windows.js +18 -7
- package/dist/adapters/prompt.js +3 -2
- package/dist/adapters/types.d.ts +13 -0
- package/dist/cli/commands/approve.js +37 -6
- package/dist/cli/commands/doctor.d.ts +4 -0
- package/dist/cli/commands/doctor.js +20 -3
- package/dist/cli/commands/fleet.d.ts +10 -1
- package/dist/cli/commands/fleet.js +279 -95
- package/dist/cli/commands/init.js +10 -4
- package/dist/cli/commands/plan.js +65 -4
- package/dist/cli/commands/report.d.ts +39 -2
- package/dist/cli/commands/report.js +227 -10
- package/dist/cli/commands/resume.js +2 -4
- package/dist/cli/commands/run.d.ts +11 -0
- package/dist/cli/commands/run.js +33 -5
- package/dist/cli/commands/status.js +30 -3
- package/dist/cli/commands/verify.d.ts +1 -1
- package/dist/cli/commands/verify.js +356 -123
- package/dist/compile/native.js +130 -0
- package/dist/config/config.d.ts +25 -7
- package/dist/config/config.js +92 -11
- package/dist/config/fleet-overlay.d.ts +2 -1
- package/dist/config/fleet-overlay.js +77 -4
- package/dist/drivers/orca.d.ts +19 -1
- package/dist/drivers/orca.js +161 -31
- package/dist/gates/cache.d.ts +4 -0
- package/dist/gates/cache.js +10 -2
- package/dist/gates/llm.d.ts +4 -1
- package/dist/gates/llm.js +6 -1
- package/dist/gates/review.js +8 -3
- package/dist/gates/run-gates.d.ts +3 -2
- package/dist/gates/run-gates.js +91 -50
- package/dist/route/router.d.ts +11 -0
- package/dist/route/router.js +29 -8
- package/dist/run/daemon.js +158 -22
- package/dist/run/git.js +9 -0
- package/dist/run/journal.d.ts +101 -1
- package/dist/run/journal.js +352 -2
- package/dist/run/launchservices-check.d.ts +56 -0
- package/dist/run/launchservices-check.js +117 -0
- package/dist/run/operator-state.d.ts +22 -2
- package/dist/run/operator-state.js +20 -3
- package/dist/run/title-environment.d.ts +45 -0
- package/dist/run/title-environment.js +127 -0
- package/dist/run/title-preload.cjs +38 -0
- package/dist/run/title-preload.d.cts +1 -0
- package/dist/tui/cockpit/board.js +12 -6
- package/dist/tui/cockpit/keys.d.ts +8 -0
- package/dist/tui/cockpit/keys.js +4 -1
- package/dist/tui/cockpit/layout.d.ts +2 -2
- package/dist/tui/cockpit/layout.js +2 -2
- package/dist/tui/cockpit/live-runtime.js +46 -20
- package/dist/tui/cockpit/live-store.d.ts +30 -1
- package/dist/tui/cockpit/live-store.js +134 -18
- package/dist/tui/cockpit/log-view.d.ts +28 -0
- package/dist/tui/cockpit/log-view.js +60 -0
- package/dist/tui/cockpit/run-view.js +4 -0
- package/dist/tui/cockpit/shell.d.ts +2 -1
- package/dist/tui/cockpit/shell.js +6 -4
- package/dist/tui/ink/fleet-app.d.ts +12 -6
- package/dist/tui/ink/fleet-app.js +17 -11
- package/package.json +1 -1
- package/skills/tickmarkr-auto/SKILL.md +33 -3
- package/skills/tickmarkr-loop/SKILL.md +55 -5
- package/skills/tickmarkr-overseer/SKILL.md +83 -8
|
@@ -9,7 +9,8 @@ const ModelWindowClaimSchema = z
|
|
|
9
9
|
.strict();
|
|
10
10
|
/**
|
|
11
11
|
* Validate cited model-window claims before production code can consume them.
|
|
12
|
-
* These values are vendor-published claims,
|
|
12
|
+
* These values are vendor-published claims (a vendor page, or a vendor CLI's own model cache), not
|
|
13
|
+
* measurements made by tickmarkr.
|
|
13
14
|
*/
|
|
14
15
|
export function loadModelWindowClaims(input) {
|
|
15
16
|
const claims = z.array(ModelWindowClaimSchema).parse(input);
|
|
@@ -22,8 +23,15 @@ export function loadModelWindowClaims(input) {
|
|
|
22
23
|
return Object.freeze(claims.map((claim) => Object.freeze(claim)));
|
|
23
24
|
}
|
|
24
25
|
const ANTHROPIC_SOURCE = "https://platform.claude.com/docs/en/about-claude/models/overview";
|
|
25
|
-
|
|
26
|
-
|
|
26
|
+
// v2.6.4 T3 (D-717): the Codex rows cite the installed CLI's own models_cache.json snapshot, not the API
|
|
27
|
+
// docs. That file has no public URL, so the source names it in its own scheme, snapshot instant included.
|
|
28
|
+
// Every listed entry there carried context_window=272000 — the budget the codex CLI launches with. The
|
|
29
|
+
// API docs' 1,050,000 (formerly cited here) describes the API surface; it is superseded for these CLI
|
|
30
|
+
// seeds, not called false. These windows are the CLI's self-report, not a tickmarkr measurement. Only the
|
|
31
|
+
// window comes from this snapshot: the codex tiers seeded beside it (config.ts) rest on the provider's
|
|
32
|
+
// descriptions and the retained workhorse tier policy, not on any measured benchmark.
|
|
33
|
+
const CODEX_CLI_CACHE_SOURCE = "codex-cli:models_cache.json#fetched_at=2026-09-29T16:09Z";
|
|
34
|
+
const CODEX_CLI_CACHE_READ_DATE = "2026-09-29";
|
|
27
35
|
const CURSOR_SOURCE = "https://cursor.com/help/ai-features/max-mode";
|
|
28
36
|
const ZAI_SOURCE = "https://z.ai/blog/glm-5.2";
|
|
29
37
|
const XAI_SOURCE = "https://docs.x.ai/developers/models/grok-4.5";
|
|
@@ -37,10 +45,13 @@ const VENDORED_MODEL_WINDOW_CLAIMS = [
|
|
|
37
45
|
{ modelId: "opus", window: 1_000_000, source: ANTHROPIC_SOURCE, readDate: READ_DATE },
|
|
38
46
|
{ modelId: "sonnet", window: 1_000_000, source: ANTHROPIC_SOURCE, readDate: READ_DATE },
|
|
39
47
|
{ modelId: "haiku", window: 200_000, source: ANTHROPIC_SOURCE, readDate: READ_DATE },
|
|
40
|
-
{ modelId: "gpt-
|
|
41
|
-
{ modelId: "gpt-
|
|
42
|
-
{ modelId: "gpt-
|
|
43
|
-
{ modelId: "gpt-5.6-
|
|
48
|
+
{ modelId: "gpt-6-astra", window: 272_000, source: CODEX_CLI_CACHE_SOURCE, readDate: CODEX_CLI_CACHE_READ_DATE },
|
|
49
|
+
{ modelId: "gpt-6-sol", window: 272_000, source: CODEX_CLI_CACHE_SOURCE, readDate: CODEX_CLI_CACHE_READ_DATE },
|
|
50
|
+
{ modelId: "gpt-6-luna", window: 272_000, source: CODEX_CLI_CACHE_SOURCE, readDate: CODEX_CLI_CACHE_READ_DATE },
|
|
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
|
+
{ modelId: "gpt-5.6-terra", window: 272_000, source: CODEX_CLI_CACHE_SOURCE, readDate: CODEX_CLI_CACHE_READ_DATE },
|
|
54
|
+
{ modelId: "gpt-5.6-luna", window: 272_000, source: CODEX_CLI_CACHE_SOURCE, readDate: CODEX_CLI_CACHE_READ_DATE },
|
|
44
55
|
{ modelId: "composer-2.5", window: 200_000, source: CURSOR_SOURCE, readDate: READ_DATE },
|
|
45
56
|
{ modelId: "composer-2.5-fast", window: 200_000, source: CURSOR_SOURCE, readDate: READ_DATE },
|
|
46
57
|
{ modelId: "claude-fable-5-1", window: 1_000_000, source: ANTHROPIC_SOURCE, readDate: OBS_871_READ_DATE },
|
package/dist/adapters/prompt.js
CHANGED
|
@@ -130,8 +130,9 @@ export function parseWorkerResult(raw, nonce) {
|
|
|
130
130
|
}
|
|
131
131
|
// Signatures anchor distinctive CLI-error phrasing — never bare fragments or bare status-code
|
|
132
132
|
// numbers (the QUOTA_RE Pitfall-3 lesson): these run only over no-trailer output, but a stalled
|
|
133
|
-
// worker's harvested pane can still contain ordinary work text.
|
|
134
|
-
|
|
133
|
+
// worker's harvested pane can still contain ordinary work text. E1: the recorded refresh failure
|
|
134
|
+
// ("access token could not be refreshed") is auth-required — classified, never re-probed or relaunched.
|
|
135
|
+
const AUTH_RE = /not logged in|please (?:log ?in|sign in)|please run [^\n]{0,30}log ?in|authentication[ _](?:required|failed|error)|invalid (?:api key|credentials)|api key (?:is )?(?:not set|missing|invalid|required)|credentials? (?:have )?expired|401 unauthorized|access token could not be refreshed/i;
|
|
135
136
|
const SETUP_RE = /command not found|not recognized as an internal or external command|spawn \S+ ENOENT|missing required config|workspace trust (?:required|not granted)/i;
|
|
136
137
|
const OUTAGE_RE = /unable to reach the model provider|cannot reach the model provider|model provider.{0,40}(?:unavailable|unreachable)|service (?:is )?temporarily unavailable|upstream connect error|overloaded_error/i;
|
|
137
138
|
const TIMEOUT_RE = /\bETIMEDOUT\b|request timed out|connection timed out|deadline exceeded|timed out waiting for/i;
|
package/dist/adapters/types.d.ts
CHANGED
|
@@ -36,6 +36,10 @@ export interface ModelAuth {
|
|
|
36
36
|
probedAt: string;
|
|
37
37
|
identity?: string;
|
|
38
38
|
}
|
|
39
|
+
export interface ModelRetirement {
|
|
40
|
+
retiresAt: string;
|
|
41
|
+
successor?: string;
|
|
42
|
+
}
|
|
39
43
|
export interface AuthHealth {
|
|
40
44
|
installed: boolean;
|
|
41
45
|
authed: boolean;
|
|
@@ -46,6 +50,14 @@ export interface AuthHealth {
|
|
|
46
50
|
servable?: string[];
|
|
47
51
|
modelAuth?: Record<string, ModelAuth>;
|
|
48
52
|
modelIdentities?: Record<string, string>;
|
|
53
|
+
modelRetirements?: Record<string, ModelRetirement | null>;
|
|
54
|
+
codexCommit?: "protected" | "allowed" | "unknown";
|
|
55
|
+
launchServices?: LaunchServicesRecord;
|
|
56
|
+
}
|
|
57
|
+
export interface LaunchServicesRecord {
|
|
58
|
+
pid: number;
|
|
59
|
+
start: string;
|
|
60
|
+
advisory?: string;
|
|
49
61
|
}
|
|
50
62
|
export declare function modelAuthed(health: AuthHealth | undefined, model: string, allowUnverifiedModels?: boolean): boolean;
|
|
51
63
|
export interface Invocation {
|
|
@@ -190,6 +202,7 @@ export interface WorkerAdapter {
|
|
|
190
202
|
parse(output: string, nonce: string): WorkerResult;
|
|
191
203
|
listModels?(): Promise<string[]>;
|
|
192
204
|
listModelsFetchedAt?(): string | undefined;
|
|
205
|
+
listModelsRetirements?(): Record<string, ModelRetirement | null> | undefined;
|
|
193
206
|
collectUsage?(cwd: string, sinceMs: number): TokenUsage | undefined;
|
|
194
207
|
contextUsage?(session: SessionRef): ContextUsage | null;
|
|
195
208
|
readSessionTranscript?(session: SessionRef): {
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { graphDefinitionHash, loadGraph, saveGraph, taskDefinitionFingerprint } from "../../graph/graph.js";
|
|
2
2
|
import { execFileSync } from "node:child_process";
|
|
3
3
|
import { userInfo } from "node:os";
|
|
4
|
+
import { allAdapters } from "../../adapters/registry.js";
|
|
5
|
+
import { channelKey } from "../../adapters/types.js";
|
|
4
6
|
import { loadConfig } from "../../config/config.js";
|
|
5
7
|
import { integrationBranch } from "../../run/merge.js";
|
|
6
8
|
import { separabilityErrors, surfaceErrors, taskBudgetErrors } from "../../compile/collateral.js";
|
|
7
9
|
import { GATE_NAMES } from "../../graph/schema.js";
|
|
8
|
-
import { applyScopeAmendments, bindingToken, engagementComparable, engagementReleased, failedGateBeforePark, parseBindingToken, physicalLine, ATTEMPT_CAP_RELEASE, GATE_SATISFIED_RELEASE, Journal, RECHECK_RELEASE, REVIEW_UPHELD_RELEASE } from "../../run/journal.js";
|
|
10
|
+
import { applyScopeAmendments, bindingToken, captureOwedCheck, engagementComparable, engagementReleased, failedGateBeforePark, parseBindingToken, physicalLine, ATTEMPT_CAP_RELEASE, GATE_SATISFIED_RELEASE, Journal, RECHECK_RELEASE, REVIEW_UPHELD_RELEASE } from "../../run/journal.js";
|
|
9
11
|
export const APPROVAL_DISPOSITIONS = ["dispatch", "waive-gate", "re-dispatch", "fund-fixed-attempt", "fresh-budget"];
|
|
10
12
|
/**
|
|
11
13
|
* What each disposition's release actually buys — the clause every operator-facing sentence about
|
|
@@ -208,11 +210,17 @@ export async function approve(argv, cwd = process.cwd()) {
|
|
|
208
210
|
if (namedGate !== undefined && namedGate !== park?.failedGate) {
|
|
209
211
|
throw new Error(`refusing mismatched gate for ${taskId}: --gate ${namedGate} but park ${bindingToken(parkBinding)} failed ${park?.failedGate ?? "no gate"} — nothing appended`);
|
|
210
212
|
}
|
|
211
|
-
|
|
213
|
+
// A scope-request park is released one of two ways: --files widens files[], or a nonempty --reason
|
|
214
|
+
// refuses the expansion and funds a plain worker within the current files[] — the reason rides into
|
|
215
|
+
// the next brief as a standing ruling (journaledFailureBrief). No reason, no release: the refusal is
|
|
216
|
+
// a ruling only when it says why. The cockpit menu still offers neither (it has no reason entry).
|
|
217
|
+
const scopeRefusal = park?.kind === "scope-request" && !decisions && files === undefined && Boolean(reason?.trim());
|
|
218
|
+
if ((park?.kind === "scope-request" && !decisions && !scopeRefusal) || files !== undefined) {
|
|
212
219
|
if (park?.kind !== "scope-request")
|
|
213
220
|
throw new Error("--files requires a scope-request park");
|
|
214
|
-
if (!files?.length)
|
|
215
|
-
throw new Error(`scope-request for ${taskId} requires --files <glob
|
|
221
|
+
if (!files?.length) {
|
|
222
|
+
throw new Error(`scope-request for ${taskId} requires --files <glob,…> to widen files[], or \`tickmarkr approve ${runId} ${taskId} --reason <text>\` to refuse the expansion and continue within the current files[]`);
|
|
223
|
+
}
|
|
216
224
|
if (decisions)
|
|
217
225
|
throw new Error("--files cannot be combined with --waive, --uphold or --recheck");
|
|
218
226
|
const graph = applyScopeAmendments(loadGraph(cwd), journal, false, engagementReleased(events));
|
|
@@ -296,6 +304,28 @@ export async function approve(argv, cwd = process.cwd()) {
|
|
|
296
304
|
if (!gateFailPark || !failedGate) {
|
|
297
305
|
throw new Error(`--waive applies to a gate-fail park; ${taskId}'s newest park is ${String(lastHuman?.data.kind ?? "none")} with failed gate ${failedGate ?? "none"} — refusing`);
|
|
298
306
|
}
|
|
307
|
+
// C1: the waiver's owed check rides the SAME row — one append, captured synchronously so a live
|
|
308
|
+
// daemon's boundary never observes the decision without its obligation (or a second write).
|
|
309
|
+
// The seats a review discharge may resolve to are this repository's declared channels, recorded on
|
|
310
|
+
// the row so every later fold reads the same table whatever process or checkout it runs in.
|
|
311
|
+
let integrationRef = "unknown";
|
|
312
|
+
let declared;
|
|
313
|
+
let owedTask;
|
|
314
|
+
try {
|
|
315
|
+
const cfg = loadConfig(cwd);
|
|
316
|
+
integrationRef = integrationBranch(cfg, runId);
|
|
317
|
+
declared = allAdapters().flatMap((a) => a.channels(cfg)).map((c) => ({ key: channelKey(c), vendor: c.vendor }));
|
|
318
|
+
}
|
|
319
|
+
catch { /* an unreadable config binds no range and declares no seat: unknown */ }
|
|
320
|
+
try {
|
|
321
|
+
owedTask = loadGraph(cwd).tasks.find((t) => t.id === taskId);
|
|
322
|
+
}
|
|
323
|
+
catch { /* recorded as unknown criteria and scope */ }
|
|
324
|
+
const obligation = captureOwedCheck({
|
|
325
|
+
cwd, runId, taskId, gate: failedGate, events, acceptance: owedTask?.acceptance, files: owedTask?.files, declared,
|
|
326
|
+
cause: reason ?? `operator waived failed ${failedGate} gate`, // the row's own park field binds the park
|
|
327
|
+
taskRef: `${integrationRef}--${taskId}`, integrationRef,
|
|
328
|
+
});
|
|
299
329
|
journal.append("task-approved", taskId, {
|
|
300
330
|
by,
|
|
301
331
|
...(reason ? { reason } : {}),
|
|
@@ -304,8 +334,9 @@ export async function approve(argv, cwd = process.cwd()) {
|
|
|
304
334
|
gate: failedGate,
|
|
305
335
|
park: parkBinding,
|
|
306
336
|
...(reviewRoundCeiling === undefined ? {} : { reviewRoundCeiling }),
|
|
337
|
+
obligation,
|
|
307
338
|
});
|
|
308
|
-
return disposition(cwd, runId, "waive-gate", `waived failed gate ${failedGate} for ${taskId} in ${runId} — by ${by}`, serialization.contended);
|
|
339
|
+
return disposition(cwd, runId, "waive-gate", `waived failed gate ${failedGate} for ${taskId} in ${runId} — by ${by}; owes ${failedGate} check ${obligation.id}${obligation.known ? "" : " (debt unknown)"}`, serialization.contended);
|
|
309
340
|
}
|
|
310
341
|
if (gateFailPark) {
|
|
311
342
|
const choices = [`--waive (disposition waive-gate)`, `--recheck (disposition re-dispatch)`];
|
|
@@ -333,7 +364,7 @@ export async function approve(argv, cwd = process.cwd()) {
|
|
|
333
364
|
park: parkBinding,
|
|
334
365
|
});
|
|
335
366
|
const token = capPark ? "fresh-budget" : "dispatch";
|
|
336
|
-
return disposition(cwd, runId, token, `approved ${taskId} in ${runId} — by ${by}`, serialization.contended);
|
|
367
|
+
return disposition(cwd, runId, token, `approved ${taskId} in ${runId} — by ${by}${scopeRefusal ? "; scope expansion refused, files[] unchanged" : ""}`, serialization.contended);
|
|
337
368
|
}
|
|
338
369
|
finally {
|
|
339
370
|
serialization.release();
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type ClaudeAlias } from "../../adapters/claude-code.js";
|
|
2
2
|
import type { WorkerAdapter } from "../../adapters/types.js";
|
|
3
3
|
import { type KimiDoctorTurnResult } from "../../adapters/kimi.js";
|
|
4
|
+
import { type CodexSandbox } from "../../adapters/codex-commit-check.js";
|
|
4
5
|
import { type CatalogFetcher, type CatalogReadResult } from "../../adapters/catalog-remote.js";
|
|
5
6
|
import { type ShResult } from "../../run/git.js";
|
|
6
7
|
import { type VitestListResult } from "../../gates/acceptance.js";
|
|
@@ -17,6 +18,7 @@ export declare function recentReviewDemotions(cwd: string, lastRuns?: number): R
|
|
|
17
18
|
export type DoctorOpts = {
|
|
18
19
|
banner?: boolean;
|
|
19
20
|
kimiTurnProbe?: (cwd: string) => Promise<KimiDoctorTurnResult>;
|
|
21
|
+
codexSandbox?: CodexSandbox;
|
|
20
22
|
resolveClaudeAliasIdentity?: (cwd: string, alias: ClaudeAlias) => string | undefined;
|
|
21
23
|
catalog?: CatalogReadResult;
|
|
22
24
|
catalogNow?: () => Date;
|
|
@@ -34,6 +36,8 @@ export type DoctorOpts = {
|
|
|
34
36
|
resolveOrcaBinary?: (cwd: string) => string | undefined;
|
|
35
37
|
/** Test seam for the runner-owned JSON listing used by the acceptance-oracle report row. */
|
|
36
38
|
listTests?: (cwd: string) => Promise<VitestListResult>;
|
|
39
|
+
/** Clock for model-knowledge lints (retirement notices, knowledge age); absent = the wall clock. */
|
|
40
|
+
now?: () => Date;
|
|
37
41
|
};
|
|
38
42
|
type OrcaCapability = {
|
|
39
43
|
verdict: "pass" | "fail" | "warn";
|
|
@@ -14,11 +14,14 @@ import { loadConfig, overlayPreferShapes } from "../../config/config.js";
|
|
|
14
14
|
import { HerdrDriver } from "../../drivers/herdr.js";
|
|
15
15
|
import { ORCA_FIXTURE_VERSION, parseEnvelope, resolveOrcaCliBinary } from "../../drivers/orca.js";
|
|
16
16
|
import { kimi, probeKimiDoctorTurn } from "../../adapters/kimi.js";
|
|
17
|
+
import { recordCodexCommit } from "../../adapters/codex-commit-check.js";
|
|
17
18
|
import { denyPreferCollisionLine, denyPreferCollisions, disallowedBy, excludedChannels, exclusionLine, observedSeat, preferRanks } from "../../route/preference.js";
|
|
18
19
|
import { CATALOG_REFRESH_TIMEOUT_MS, LIVEBENCH_TABLE_DATE, formatCatalogRefreshLegs, readCachedCatalog, refreshCatalogCommand } from "../../adapters/catalog-remote.js";
|
|
19
20
|
import { sh } from "../../run/git.js";
|
|
20
21
|
import { auditNamedTestOracles, listVitestTests } from "../../gates/acceptance.js";
|
|
21
22
|
import { readReviewNoVerdictHistory, reviewNoVerdictRows } from "../../run/journal.js";
|
|
23
|
+
import { checkModelDiscovery } from "../../adapters/model-discovery-check.js";
|
|
24
|
+
import { recordLaunchServices } from "../../run/launchservices-check.js";
|
|
22
25
|
/** Where a newer `table_<date>.csv` is discovered — the deployed site builds filenames by
|
|
23
26
|
* concatenation and publishes no index, so the release listing is the only enumerable surface. */
|
|
24
27
|
export const LIVEBENCH_RELEASES_URL = "https://api.github.com/repos/LiveBench/livebench.github.io/contents/public";
|
|
@@ -578,6 +581,7 @@ export async function doctor(_argv, cwd = process.cwd(), adapters = allAdapters(
|
|
|
578
581
|
};
|
|
579
582
|
}
|
|
580
583
|
}
|
|
584
|
+
await recordCodexCommit(adapters, health, cwd, opts.codexSandbox);
|
|
581
585
|
// MODEL-02: detect models where the adapter exposes a list surface, BEFORE writing doctor.json (write once, below).
|
|
582
586
|
// Fail OPEN — the inverse of gates' fail-closed: detection is advisory, so a broken list surface NEVER fails doctor.
|
|
583
587
|
for (const a of adapters) {
|
|
@@ -590,6 +594,11 @@ export async function doctor(_argv, cwd = process.cwd(), adapters = allAdapters(
|
|
|
590
594
|
// no fetch timestamp, so it still stamps now — its staleness lint is best-effort until it surfaces one.
|
|
591
595
|
if (health[a.id].models.length)
|
|
592
596
|
health[a.id].modelsDetectedAt = a.listModelsFetchedAt?.() ?? new Date().toISOString();
|
|
597
|
+
// v2.6.4 T2 (B1a): the CLI's retirement notices ride into doctor.json beside the list they describe;
|
|
598
|
+
// an unreadable source leaves the field absent, which every reader treats as unknown.
|
|
599
|
+
const retirements = a.listModelsRetirements?.();
|
|
600
|
+
if (retirements)
|
|
601
|
+
health[a.id].modelRetirements = retirements;
|
|
593
602
|
}
|
|
594
603
|
catch { /* fail open: leave models as-is, doctor stays healthy */ }
|
|
595
604
|
}
|
|
@@ -654,7 +663,11 @@ export async function doctor(_argv, cwd = process.cwd(), adapters = allAdapters(
|
|
|
654
663
|
}
|
|
655
664
|
}
|
|
656
665
|
}
|
|
657
|
-
|
|
666
|
+
const discovery = await checkModelDiscovery({ cwd, cfg, adapters, health, catalog, resolved: resolvedIdentitySnapshot, mayRefresh: !opts.catalog && !catalogRefreshLine && catalogRefreshAllowed, fetcher: opts.catalogFetcher, now: opts.catalogNow });
|
|
667
|
+
catalog = discovery.catalog;
|
|
668
|
+
aliasDriftFindings.push(...discovery.findings);
|
|
669
|
+
const launchServices = await recordLaunchServices(readDoctor(cwd), health);
|
|
670
|
+
writeDoctor(cwd, launchServices.health);
|
|
658
671
|
const rows = adapters.map((a) => {
|
|
659
672
|
const h = health[a.id];
|
|
660
673
|
// OBS-503: a crash-on-version verdict carries its reason in note — print it instead of the lie.
|
|
@@ -784,7 +797,7 @@ export async function doctor(_argv, cwd = process.cwd(), adapters = allAdapters(
|
|
|
784
797
|
const liveBenchStale = liveBenchStalenessFinding(opts.catalogNow?.() ?? new Date());
|
|
785
798
|
if (liveBenchStale)
|
|
786
799
|
lintRows.push(attentionRow(liveBenchStale));
|
|
787
|
-
lintRows.push(...modelLints(cfg, health, adapters, { tty: ttyVisual(), stateDir: stateDirName(cwd), overlayPreferShapes: overlayPreferShapes(cwd) }).map(attentionRow));
|
|
800
|
+
lintRows.push(...modelLints(cfg, health, adapters, { tty: ttyVisual(), stateDir: stateDirName(cwd), overlayPreferShapes: overlayPreferShapes(cwd), now: opts.now?.() }).map(attentionRow));
|
|
788
801
|
const excluded = excludedChannels(cfg, adapters, health);
|
|
789
802
|
if (excluded.length)
|
|
790
803
|
lintRows.push(attentionRow(exclusionLine(excluded)));
|
|
@@ -802,6 +815,8 @@ export async function doctor(_argv, cwd = process.cwd(), adapters = allAdapters(
|
|
|
802
815
|
lintRows.push(...flagDriftWarnings(adapters, health).map(attentionRow));
|
|
803
816
|
// OBS-145: resolved-identity drift is advisory display warning.
|
|
804
817
|
lintRows.push(...aliasDriftFindings.map(attentionRow));
|
|
818
|
+
if (launchServices.line)
|
|
819
|
+
lintRows.push(attentionRow(launchServices.line));
|
|
805
820
|
const resolvedCatalogModel = (adapter, model) => {
|
|
806
821
|
if (adapter !== "claude-code" || !(model in CLAUDE_ALIAS_IDENTITY_STAMPS))
|
|
807
822
|
return undefined;
|
|
@@ -890,9 +905,11 @@ export async function doctor(_argv, cwd = process.cwd(), adapters = allAdapters(
|
|
|
890
905
|
rows.push(` ${dim(`catalog · ${advisory.display}`)}`);
|
|
891
906
|
if (!listAllModels && bulk.length) {
|
|
892
907
|
const uncovered = bulk.filter((adv) => adv.coverage === "uncovered").length;
|
|
893
|
-
const
|
|
908
|
+
const unknown = bulk.filter((adv) => adv.coverage === "unknown").length;
|
|
909
|
+
const evidenceOnly = bulk.length - uncovered - unknown;
|
|
894
910
|
const parts = [
|
|
895
911
|
...(uncovered ? [`${uncovered} uncovered by ${catalog.source === "cache" ? "cached catalogs" : "vendored catalog"}`] : []),
|
|
912
|
+
...(unknown ? [`${unknown} coverage unknown after a failed refresh`] : []),
|
|
896
913
|
...(evidenceOnly ? [`${evidenceOnly} covered without a tier suggestion`] : []),
|
|
897
914
|
];
|
|
898
915
|
rows.push(` ${dim(`catalog · ${parts.join(" · ")} — tickmarkr doctor --models lists each`)}`);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { type CatalogFetcher, type CatalogReadResult } from "../../adapters/catalog-remote.js";
|
|
2
2
|
import type { WorkerAdapter } from "../../adapters/types.js";
|
|
3
|
-
import type
|
|
3
|
+
import { type MapEntry, type ModeResolution, type RoutingMode, type TickmarkrConfig } from "../../config/config.js";
|
|
4
|
+
import type { FleetEditorResult, FleetStagedDeny, FleetStagedMetadata } from "../../tui/ink/fleet-app.js";
|
|
4
5
|
type FleetEditorProps = Parameters<typeof import("../../tui/ink/fleet-app.js").runFleetInkEditor>[0];
|
|
5
6
|
export type FleetInput = NodeJS.ReadableStream & {
|
|
6
7
|
isTTY?: boolean;
|
|
@@ -44,6 +45,14 @@ export declare function assembleFleetEditor(cwd: string, adapters: WorkerAdapter
|
|
|
44
45
|
props: FleetEditorProps;
|
|
45
46
|
commit: (result: FleetEditorResult) => string;
|
|
46
47
|
renderWhy: () => string;
|
|
48
|
+
previewConfig: (mode: RoutingMode, map: Record<string, MapEntry>, deny: FleetStagedDeny, stage?: FleetStagedMetadata) => {
|
|
49
|
+
ok: true;
|
|
50
|
+
cfg: TickmarkrConfig;
|
|
51
|
+
mode: ModeResolution;
|
|
52
|
+
} | {
|
|
53
|
+
ok: false;
|
|
54
|
+
error: string;
|
|
55
|
+
};
|
|
47
56
|
} | {
|
|
48
57
|
unavailable: string;
|
|
49
58
|
}>;
|