tickmarkr 1.90.8 → 1.91.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/dist/adapters/catalog-remote.d.ts +11 -1
- package/dist/adapters/catalog-remote.js +169 -19
- package/dist/adapters/catalog.js +59 -0
- package/dist/adapters/model-lints.d.ts +36 -2
- package/dist/adapters/model-lints.js +151 -35
- package/dist/adapters/registry.js +32 -6
- package/dist/cli/commands/doctor.d.ts +12 -0
- package/dist/cli/commands/doctor.js +57 -15
- package/dist/cli/commands/fleet.js +31 -3
- package/dist/compile/native.js +5 -4
- package/dist/config/config.js +5 -0
- package/dist/config/fleet-overlay.js +20 -4
- package/dist/gates/acceptance.js +13 -4
- package/dist/run/daemon.js +1 -1
- package/dist/tui/ink/components.d.ts +16 -1
- package/dist/tui/ink/components.js +16 -2
- package/dist/tui/ink/fleet-app.d.ts +8 -1
- package/dist/tui/ink/fleet-app.js +184 -54
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Fleet-overlay mutation, serialization, and diff rendering for the `tickmarkr fleet` write path.
|
|
2
|
-
import { isMap, isScalar, isSeq, parseDocument, stringify } from "yaml";
|
|
2
|
+
import { isMap, isScalar, isSeq, parseDocument, stringify, visit } from "yaml";
|
|
3
3
|
/** Fleet-owned overlay keys — the only config surface `tickmarkr fleet` may write. */
|
|
4
4
|
export const FLEET_OVERLAY_KEYS = ["routing", "tiers"];
|
|
5
5
|
function fleetSubset(obj) {
|
|
@@ -18,6 +18,9 @@ function sortedUnique(xs) {
|
|
|
18
18
|
// private provenance envelope until this module writes the YAML. The envelope never reaches disk.
|
|
19
19
|
const FIRST_TOUCH_OPEN = "\uE000tickmarkr-fleet-first-touch:";
|
|
20
20
|
const FIRST_TOUCH_CLOSE = "\uE001";
|
|
21
|
+
// OBS-505: marks a scalar-trailing single-line comment for the two-space inline note style at
|
|
22
|
+
// stringify time; applied and consumed inside renderFleetOverlayWrite, never written to disk.
|
|
23
|
+
const INLINE_COMMENT_SENTINEL = "\uE002";
|
|
21
24
|
export function fleetFirstTouchProvenance(note, firstTouch) {
|
|
22
25
|
return `${FIRST_TOUCH_OPEN}${encodeURIComponent(firstTouch.vendor)}:${firstTouch.channel}${FIRST_TOUCH_CLOSE}${note}`;
|
|
23
26
|
}
|
|
@@ -189,10 +192,23 @@ export function renderFleetOverlayWrite(priorBytes, write) {
|
|
|
189
192
|
}
|
|
190
193
|
}
|
|
191
194
|
}
|
|
192
|
-
//
|
|
193
|
-
//
|
|
195
|
+
// OBS-505: fleet's two-space note style (`tier: mid # note`) applies to INLINE comments only.
|
|
196
|
+
// The previous commentString prefixed " #" onto EVERY comment line, so block comments — the
|
|
197
|
+
// whole init scaffold at column 0, and indented operator essays — each gained a stray leading
|
|
198
|
+
// space, turning a one-key write into a whole-file diff on the one confirmation surface an
|
|
199
|
+
// operator reviews. commentString has no position context, but this writer owns the document:
|
|
200
|
+
// scalar-trailing single-line comments (the only inline form fleet emits) are marked with a
|
|
201
|
+
// private-use sentinel (the FIRST_TOUCH envelope precedent above), everything else renders
|
|
202
|
+
// byte-identical to yaml's own stringifyComment.
|
|
203
|
+
visit(doc, (_key, node) => {
|
|
204
|
+
if (isScalar(node) && typeof node.comment === "string" && !node.comment.includes("\n")) {
|
|
205
|
+
node.comment = `${INLINE_COMMENT_SENTINEL}${node.comment}`;
|
|
206
|
+
}
|
|
207
|
+
});
|
|
194
208
|
return doc.toString({
|
|
195
|
-
commentString: (comment) => comment.
|
|
209
|
+
commentString: (comment) => comment.startsWith(INLINE_COMMENT_SENTINEL)
|
|
210
|
+
? ` #${comment.slice(1)}`
|
|
211
|
+
: comment.replace(/^(?!$)(?: $)?/gm, "#"),
|
|
196
212
|
});
|
|
197
213
|
}
|
|
198
214
|
/** Build the repo overlay fragment fleet would write for edits since session start. */
|
package/dist/gates/acceptance.js
CHANGED
|
@@ -75,10 +75,17 @@ function escapeRegExp(s) {
|
|
|
75
75
|
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
76
76
|
}
|
|
77
77
|
// v1.89 T6: Vitest applies -t to the runner-visible full name (enclosing describe titles and the test
|
|
78
|
-
// title, space-joined). Escaping preserves literal criteria;
|
|
79
|
-
// substring containment, the named-test contract.
|
|
78
|
+
// title, space-joined). Escaping preserves literal criteria; the leaf anchor makes trailing-title
|
|
79
|
+
// equality, rather than substring containment, the named-test contract.
|
|
80
|
+
// OBS-511 (runs 1013+1027, one full attempt lost in EACH): two independent workers nested the verbatim
|
|
81
|
+
// criterion under a describe, the old `^…$` full-name equality matched zero tests, and the gate parked a
|
|
82
|
+
// green suite. `(^| )` accepts any describe prefix while keeping the criterion as the complete trailing
|
|
83
|
+
// segment — equality still matches (empty prefix), and a title merely CONTAINING the criterion still
|
|
84
|
+
// selects zero. ponytail: a leaf title that itself ENDS with " <criterion>" also matches — vitest -t sees
|
|
85
|
+
// only the space-joined full name, so true leaf-boundary precision needs the runner's --list JSON
|
|
86
|
+
// (auditAcceptanceCorpus has it at doctor time); upgrade there if this window is ever exploited.
|
|
80
87
|
export function testFilterPattern(name) {
|
|
81
|
-
return
|
|
88
|
+
return `(^| )${escapeRegExp(name)}$`;
|
|
82
89
|
}
|
|
83
90
|
// OBS-55: when the base command already contains `--`, append -t after forwarded args — a second `--`
|
|
84
91
|
// makes vitest treat -t as a positional file filter and the name filter is dropped.
|
|
@@ -126,8 +133,10 @@ export function auditAcceptanceCorpus(corpusRoot, listedTests) {
|
|
|
126
133
|
? {
|
|
127
134
|
namedTest: {
|
|
128
135
|
criterion: item.test,
|
|
136
|
+
// OBS-511: mirror the gate's leaf-anchored suffix rule — the audit's denominator
|
|
137
|
+
// must count exactly the tests the -t filter would select, or doctor and gate disagree.
|
|
129
138
|
matches: runnerNames
|
|
130
|
-
.filter(({ fullName }) => fullName === item.test)
|
|
139
|
+
.filter(({ fullName }) => fullName === item.test || fullName.endsWith(` ${item.test}`))
|
|
131
140
|
.map(({ listed }) => listed),
|
|
132
141
|
},
|
|
133
142
|
}
|
package/dist/run/daemon.js
CHANGED
|
@@ -1944,7 +1944,7 @@ export async function runDaemon(repoRoot, opts = {}) {
|
|
|
1944
1944
|
const promptFile = writePrompt(journal.dir, t, attempt, feedback, nonce);
|
|
1945
1945
|
// OBS-56: state the non-interactive, one-pass finish contract and the OBS-54 stall budget in every
|
|
1946
1946
|
// worker prompt, not only consult retry guidance. Prepended so prompt.ts's completion trailer stays last.
|
|
1947
|
-
const workerContract = `## Harness contract\n- This harness is non-interactive: make one continuous pass; do not stop for questions or follow-up input.\n- You have a ${taskTimeoutMinutes} minute stall window. Budget the full suite once, then commit and emit the completion trailer before it expires.\n- Each test: acceptance criterion must exist as a vitest test whose title
|
|
1947
|
+
const workerContract = `## Harness contract\n- This harness is non-interactive: make one continuous pass; do not stop for questions or follow-up input.\n- You have a ${taskTimeoutMinutes} minute stall window. Budget the full suite once, then commit and emit the completion trailer before it expires.\n- Each test: acceptance criterion must exist as a vitest test whose OWN title (the leaf, not counting enclosing describe titles) is the criterion string verbatim — never shortened, never decorated. Nesting under describe() is allowed.`; // OBS-64; OBS-511: leaf-title rule stated where the worker reads it
|
|
1948
1948
|
// OBS-47: state the worktree layout contract in the worker prompt (cheap-tier workers were
|
|
1949
1949
|
// committing/deleting node_modules and tripping the scope gate). The harness re-asserts the link
|
|
1950
1950
|
// itself before gates regardless of what the worker does with it.
|
|
@@ -9,7 +9,20 @@ export declare function TextLines({ lines }: {
|
|
|
9
9
|
export declare function ToggleMark({ active }: {
|
|
10
10
|
active: boolean;
|
|
11
11
|
}): import("react").JSX.Element;
|
|
12
|
-
|
|
12
|
+
/**
|
|
13
|
+
* v1.90.9 (operator field report: "can't choose models for omp"): a list longer than the
|
|
14
|
+
* terminal rendered EVERY row, so the title, legend, and cursor scrolled off the top — omp's
|
|
15
|
+
* 218 unclassified models left the operator staring at a tail of unselectable rows. The window
|
|
16
|
+
* is cursor-centered and clamped; the markers say exactly what is elided and that the list is
|
|
17
|
+
* still fully reachable (arrows walk it, type-to-search narrows it).
|
|
18
|
+
*/
|
|
19
|
+
export declare function windowRows<T>(rows: T[], cursor: number, capacity: number): {
|
|
20
|
+
visible: T[];
|
|
21
|
+
start: number;
|
|
22
|
+
above: number;
|
|
23
|
+
below: number;
|
|
24
|
+
};
|
|
25
|
+
export declare function FleetListScreen({ title, legend, rows, cursor, details, filter, viewRows, }: {
|
|
13
26
|
title: string;
|
|
14
27
|
legend: string;
|
|
15
28
|
rows: FleetListRow[];
|
|
@@ -17,6 +30,8 @@ export declare function FleetListScreen({ title, legend, rows, cursor, details,
|
|
|
17
30
|
details?: string[];
|
|
18
31
|
/** active type-to-search string; rendered on the legend line so the operator sees the narrowing */
|
|
19
32
|
filter?: string;
|
|
33
|
+
/** list viewport capacity; rows beyond it window around the cursor with elision markers */
|
|
34
|
+
viewRows?: number;
|
|
20
35
|
}): import("react").JSX.Element;
|
|
21
36
|
export declare function FleetReviewScreen({ title, legend, diff, }: {
|
|
22
37
|
title: string;
|
|
@@ -17,8 +17,22 @@ export function ToggleMark({ active }) {
|
|
|
17
17
|
? _jsx(Text, { color: "ansi256(41)", children: GLYPHS.toggleActive })
|
|
18
18
|
: _jsx(Text, { dimColor: true, children: GLYPHS.toggleInactive });
|
|
19
19
|
}
|
|
20
|
-
|
|
21
|
-
|
|
20
|
+
/**
|
|
21
|
+
* v1.90.9 (operator field report: "can't choose models for omp"): a list longer than the
|
|
22
|
+
* terminal rendered EVERY row, so the title, legend, and cursor scrolled off the top — omp's
|
|
23
|
+
* 218 unclassified models left the operator staring at a tail of unselectable rows. The window
|
|
24
|
+
* is cursor-centered and clamped; the markers say exactly what is elided and that the list is
|
|
25
|
+
* still fully reachable (arrows walk it, type-to-search narrows it).
|
|
26
|
+
*/
|
|
27
|
+
export function windowRows(rows, cursor, capacity) {
|
|
28
|
+
if (capacity <= 0 || rows.length <= capacity)
|
|
29
|
+
return { visible: rows, start: 0, above: 0, below: 0 };
|
|
30
|
+
const start = Math.min(Math.max(cursor - Math.floor(capacity / 2), 0), rows.length - capacity);
|
|
31
|
+
return { visible: rows.slice(start, start + capacity), start, above: start, below: rows.length - start - capacity };
|
|
32
|
+
}
|
|
33
|
+
export function FleetListScreen({ title, legend, rows, cursor, details = [], filter, viewRows, }) {
|
|
34
|
+
const { visible, start, above, below } = windowRows(rows, cursor, viewRows ?? Number.POSITIVE_INFINITY);
|
|
35
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: title }), _jsx(Text, { dimColor: true, children: filter ? `${legend} · search: ${filter}` : legend }), above > 0 && _jsxs(Text, { dimColor: true, children: [" \u2026 ", above, " above"] }), visible.map((row, index) => (_jsxs(Text, { bold: start + index === cursor, children: [start + index === cursor ? `${GLYPHS.pointer} ` : " ", row.content] }, row.id))), below > 0 && _jsxs(Text, { dimColor: true, children: [" \u2026 ", below, " below \u2014 type to search"] }), _jsx(TextLines, { lines: details })] }));
|
|
22
36
|
}
|
|
23
37
|
export function FleetReviewScreen({ title, legend, diff, }) {
|
|
24
38
|
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: title }), _jsx(Text, { dimColor: true, children: legend }), _jsx(Text, { children: diff })] }));
|
|
@@ -42,6 +42,10 @@ type AgentCli = {
|
|
|
42
42
|
version: string;
|
|
43
43
|
authed: boolean;
|
|
44
44
|
};
|
|
45
|
+
export type FleetModelSuggestion = {
|
|
46
|
+
tier: Tier;
|
|
47
|
+
note: string;
|
|
48
|
+
};
|
|
45
49
|
export type FleetModelGroup = {
|
|
46
50
|
adapter: string;
|
|
47
51
|
vendor?: string;
|
|
@@ -49,6 +53,7 @@ export type FleetModelGroup = {
|
|
|
49
53
|
model: string;
|
|
50
54
|
tier?: Tier;
|
|
51
55
|
detectedAt?: string;
|
|
56
|
+
suggestion?: FleetModelSuggestion;
|
|
52
57
|
}>;
|
|
53
58
|
};
|
|
54
59
|
export type FleetClassification = {
|
|
@@ -76,7 +81,7 @@ export type FleetCandidateOption = {
|
|
|
76
81
|
};
|
|
77
82
|
};
|
|
78
83
|
export declare function formatDoctorAge(ageMs: number | null): string;
|
|
79
|
-
export declare function FleetApp({ ageMs, agents, initialDenyAdapters, initialDenyModels, modelGroups, initialMode, modeOptions, initialMap, modePreview, shapeRows, candidatesForShape, preferOptionsForShape, initialSteering, steeringOptionsFor, reviewOverlay, reloadGuard, entry, initialJudge, judgeSeats, }: {
|
|
84
|
+
export declare function FleetApp({ ageMs, agents, initialDenyAdapters, initialDenyModels, modelGroups, initialMode, modeOptions, initialMap, modePreview, shapeRows, candidatesForShape, preferOptionsForShape, initialSteering, steeringOptionsFor, reviewOverlay, reloadGuard, entry, initialJudge, judgeSeats, viewRows, }: {
|
|
80
85
|
ageMs: number | null;
|
|
81
86
|
agents: AgentCli[];
|
|
82
87
|
initialDenyAdapters: string[];
|
|
@@ -98,6 +103,8 @@ export declare function FleetApp({ ageMs, agents, initialDenyAdapters, initialDe
|
|
|
98
103
|
initialJudge?: string;
|
|
99
104
|
/** discovered adapter:model seats — the judge picker universe */
|
|
100
105
|
judgeSeats?: string[];
|
|
106
|
+
/** list viewport capacity (terminal rows minus chrome) — long lists window around the cursor */
|
|
107
|
+
viewRows?: number;
|
|
101
108
|
}): import("react").JSX.Element;
|
|
102
109
|
export declare function runFleetInkEditor({ ageMs, adapters, health, initialDenyAdapters, initialDenyModels, modelGroups, initialMode, modeOptions, initialMap, modePreview, shapeRows, candidatesForShape, preferOptionsForShape, initialSteering, steeringOptionsFor, reviewOverlay, reloadGuard, entry, initialJudge, judgeSeats, initialInput, input, output, debug, }: {
|
|
103
110
|
ageMs: number | null;
|