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.
Files changed (79) hide show
  1. package/README.md +10 -2
  2. package/dist/adapters/catalog-remote.d.ts +23 -0
  3. package/dist/adapters/catalog-remote.js +38 -0
  4. package/dist/adapters/claude-code.d.ts +1 -1
  5. package/dist/adapters/claude-code.js +4 -1
  6. package/dist/adapters/codex-commit-check.d.ts +25 -0
  7. package/dist/adapters/codex-commit-check.js +108 -0
  8. package/dist/adapters/codex.d.ts +2 -1
  9. package/dist/adapters/codex.js +26 -5
  10. package/dist/adapters/model-discovery-check.d.ts +33 -0
  11. package/dist/adapters/model-discovery-check.js +123 -0
  12. package/dist/adapters/model-lints.d.ts +2 -1
  13. package/dist/adapters/model-lints.js +52 -1
  14. package/dist/adapters/model-windows.d.ts +2 -1
  15. package/dist/adapters/model-windows.js +18 -7
  16. package/dist/adapters/prompt.js +3 -2
  17. package/dist/adapters/types.d.ts +13 -0
  18. package/dist/cli/commands/approve.js +37 -6
  19. package/dist/cli/commands/doctor.d.ts +4 -0
  20. package/dist/cli/commands/doctor.js +20 -3
  21. package/dist/cli/commands/fleet.d.ts +10 -1
  22. package/dist/cli/commands/fleet.js +279 -95
  23. package/dist/cli/commands/init.js +10 -4
  24. package/dist/cli/commands/plan.js +65 -4
  25. package/dist/cli/commands/report.d.ts +39 -2
  26. package/dist/cli/commands/report.js +227 -10
  27. package/dist/cli/commands/resume.js +2 -4
  28. package/dist/cli/commands/run.d.ts +11 -0
  29. package/dist/cli/commands/run.js +33 -5
  30. package/dist/cli/commands/status.js +30 -3
  31. package/dist/cli/commands/verify.d.ts +1 -1
  32. package/dist/cli/commands/verify.js +356 -123
  33. package/dist/compile/native.js +130 -0
  34. package/dist/config/config.d.ts +25 -7
  35. package/dist/config/config.js +92 -11
  36. package/dist/config/fleet-overlay.d.ts +2 -1
  37. package/dist/config/fleet-overlay.js +77 -4
  38. package/dist/drivers/orca.d.ts +19 -1
  39. package/dist/drivers/orca.js +161 -31
  40. package/dist/gates/cache.d.ts +4 -0
  41. package/dist/gates/cache.js +10 -2
  42. package/dist/gates/llm.d.ts +4 -1
  43. package/dist/gates/llm.js +6 -1
  44. package/dist/gates/review.js +8 -3
  45. package/dist/gates/run-gates.d.ts +3 -2
  46. package/dist/gates/run-gates.js +91 -50
  47. package/dist/route/router.d.ts +11 -0
  48. package/dist/route/router.js +29 -8
  49. package/dist/run/daemon.js +158 -22
  50. package/dist/run/git.js +9 -0
  51. package/dist/run/journal.d.ts +101 -1
  52. package/dist/run/journal.js +352 -2
  53. package/dist/run/launchservices-check.d.ts +56 -0
  54. package/dist/run/launchservices-check.js +117 -0
  55. package/dist/run/operator-state.d.ts +22 -2
  56. package/dist/run/operator-state.js +20 -3
  57. package/dist/run/title-environment.d.ts +45 -0
  58. package/dist/run/title-environment.js +127 -0
  59. package/dist/run/title-preload.cjs +38 -0
  60. package/dist/run/title-preload.d.cts +1 -0
  61. package/dist/tui/cockpit/board.js +12 -6
  62. package/dist/tui/cockpit/keys.d.ts +8 -0
  63. package/dist/tui/cockpit/keys.js +4 -1
  64. package/dist/tui/cockpit/layout.d.ts +2 -2
  65. package/dist/tui/cockpit/layout.js +2 -2
  66. package/dist/tui/cockpit/live-runtime.js +46 -20
  67. package/dist/tui/cockpit/live-store.d.ts +30 -1
  68. package/dist/tui/cockpit/live-store.js +134 -18
  69. package/dist/tui/cockpit/log-view.d.ts +28 -0
  70. package/dist/tui/cockpit/log-view.js +60 -0
  71. package/dist/tui/cockpit/run-view.js +4 -0
  72. package/dist/tui/cockpit/shell.d.ts +2 -1
  73. package/dist/tui/cockpit/shell.js +6 -4
  74. package/dist/tui/ink/fleet-app.d.ts +12 -6
  75. package/dist/tui/ink/fleet-app.js +17 -11
  76. package/package.json +1 -1
  77. package/skills/tickmarkr-auto/SKILL.md +33 -3
  78. package/skills/tickmarkr-loop/SKILL.md +55 -5
  79. package/skills/tickmarkr-overseer/SKILL.md +83 -8
@@ -6,10 +6,11 @@ import { loadGraph, onDiskSpecHash, readCompileRefusal } from "../../graph/graph
6
6
  import { formatSummary, resolveRunMode, runDaemon } from "../../run/daemon.js";
7
7
  import { isRunLockLive } from "../../run/lock.js";
8
8
  import { route, NO_EXPLORE_ENV } from "../../route/router.js";
9
- import { formatJournalNarration, loadRoutingProfile, newRunId } from "../../run/journal.js";
9
+ import { formatJournalNarration, Journal, loadRoutingProfile, newRunId } from "../../run/journal.js";
10
10
  import { normalizeGateOutcome } from "../../run/outcome.js";
11
11
  import { GLYPHS, LIVE } from "../../brand.js";
12
12
  import { cellWidth, fitCells } from "../../tui/cockpit/width.js";
13
+ import { currentOwed, outstandingText } from "./report.js";
13
14
  import { assertRefsWritable } from "../../run/git.js";
14
15
  // ── the operator event rail (v1.99 T2) ──────────────────────────────────────────────────────────
15
16
  // A run's TTY narration is an operator EVENT RAIL, not the journal dump it used to echo. The
@@ -166,17 +167,25 @@ const bucket = (value) => (Array.isArray(value) ? value.length : 0);
166
167
  *
167
168
  * a fatal crash, a failed task or a failed integration tip is a FAILURE;
168
169
  * an incomplete run — parked, blocked or still-pending work — wants ATTENTION;
170
+ * so does a complete execution that owes a check, or whose owed-check fold is unknown (CG2);
169
171
  * only a run with none of those is a pass.
170
172
  *
171
173
  * A rail that paints the terminal record by the same predicate the exit code uses can never show a
172
- * green tickmark over a run whose exit code is 2.
174
+ * green tickmark over a run whose exit code is 2 — and, since the exit code is execution-based, the
175
+ * debt clause is the one place the tone is stricter than it: exit 0 with an owed check is not green.
176
+ * The fold is the one this row carries, reduced at its own append: live, that IS the current fold.
173
177
  */
174
178
  const runEndTone = (data) => {
175
179
  if (data.fatal === true || bucket(data.failed) > 0 || data.tipVerify === "failed")
176
180
  return "fail";
177
181
  if (bucket(data.human) + bucket(data.blocked) + bucket(data.pending) > 0)
178
182
  return "attention";
179
- return "pass";
183
+ return recordedDebt(data) === 0 ? "pass" : "attention";
184
+ };
185
+ /** The outstanding count a run-end record's own fold carries; undefined when it carries none or an unknown one. */
186
+ const recordedDebt = (data) => {
187
+ const owed = data.owedChecks;
188
+ return owed?.known === true && Array.isArray(owed.outstanding) ? owed.outstanding.length : undefined;
180
189
  };
181
190
  /** A gate row's non-failure is spelled across `pass`, `skipped`, `verdict` and `infra`, and a row may
182
191
  * state NONE of them (src/run/outcome.ts) — so the rail reads the canonical outcome rather than
@@ -330,6 +339,8 @@ const RAIL_PROJECTION = {
330
339
  "tip-moved": (d) => typeof d.gatedCommit === "string" && typeof d.branchTip === "string"
331
340
  ? `gated ${d.gatedCommit.slice(0, 12)}, tip ${d.branchTip.slice(0, 12)}`
332
341
  : undefined,
342
+ // CG2: the terminal row names its debt — a legacy record without a fold is unknown, never zero
343
+ "run-end": (d) => `outstanding ${recordedDebt(d) ?? "unknown"}`,
333
344
  "trust-auto-answer": (d) => typeof d.adapter === "string" ? `${d.adapter}${typeof d.phase === "string" ? ` ${d.phase}` : ""}` : undefined,
334
345
  // SB-1: the census the ceiling released beside, and the budget the round ran under versus the one it
335
346
  // would have had on an empty census — none of `{count, occupancyCap, conservativeCap}` is on the ladder
@@ -420,8 +431,25 @@ export function bindNarration(driver, narrate) {
420
431
  driver.narrateWith?.(narrate);
421
432
  return driver;
422
433
  }
423
- const summaryGreen = (s) => s.failed.length === 0 && s.human.length === 0 && s.blocked.length === 0 && s.pending.length === 0
434
+ /** Execution green: the exit-code predicate run and resume share. Users' scripts read it; debt never moves it. */
435
+ export const summaryGreen = (s) => s.failed.length === 0 && s.human.length === 0 && s.blocked.length === 0 && s.pending.length === 0
424
436
  && s.tipVerify !== "failed";
437
+ /**
438
+ * CG2 (v2.6.4): the last line of a run or resume, read from the CURRENT owed-check fold after the
439
+ * daemon returns — never the run-end row's copy, which a later `verify --record` discharge leaves
440
+ * historical. "verified" requires execution green AND outstanding empty AND known; an execution-green
441
+ * run that owes a check, or whose debt is unknown, says so and is not green. A partial or failed run
442
+ * adds nothing: its summary keeps its own wording and is never called complete.
443
+ */
444
+ export function finalRail(s, cwd) {
445
+ if (!summaryGreen(s))
446
+ return "";
447
+ const owed = currentOwed(Journal.open(cwd, s.runId).read(), cwd);
448
+ if (owed.known && owed.outstanding.length === 0)
449
+ return `\nverified — execution complete, ${outstandingText(owed)}`;
450
+ const why = owed.known ? "" : ` (${owed.unknown[0]?.reason ?? "owed checks unreadable"})`;
451
+ return `\nexecution complete; ${outstandingText(owed)}${why} — not green until outstanding is empty and known`;
452
+ }
425
453
  // v1.51 T2: --quality is a pure compatibility alias for `--mode partner-led` (this run only). It
426
454
  // carries no one-band floor raise of its own — and since the OBS-89 rip (v1.60) route() no longer
427
455
  // reads the retired TICKMARKR_QUALITY env at all, so no downstream code can raise a floor on its
@@ -526,7 +554,7 @@ export async function run(argv, cwd = process.cwd()) {
526
554
  supersedes: values.supersedes,
527
555
  narrate,
528
556
  });
529
- const out = `run ${s.runId} finished — ${formatSummary(s)} (merge to main is a human decision)`;
557
+ const out = `run ${s.runId} finished — ${formatSummary(s)} (merge to main is a human decision)${finalRail(s, cwd)}`;
530
558
  return { out, code: summaryGreen(s) ? 0 : 2 };
531
559
  }
532
560
  finally {
@@ -12,6 +12,7 @@ import { authorsNote, doneAuthors } from "../../run/operator-state.js";
12
12
  import { projectOperatorSummary } from "../../run/operator-summary.js";
13
13
  import { trackJournalRows } from "../../run/protocol.js";
14
14
  import { newestPark, permittedDecisionVerbs } from "./approve.js";
15
+ import { currentOwed, endToEndFirstPass, firstPassText, leadLines, leadRows, needsYouText, wallText } from "./report.js";
15
16
  import { Journal, bindingToken, effectiveEvents, formatJournalNarration, parseJournalText, physicalLine, engagementComparable, isQualityFailureParkKind, parseRunId, preservedRefsByTask, recordedTaskFailureKind, runHasEnded, upheldFeedbackByTask, } from "../../run/journal.js";
16
17
  import { isPidLive, runLockRunId, runStatusLine } from "../../run/lock.js";
17
18
  import { normalizeGateOutcome } from "../../run/outcome.js";
@@ -448,6 +449,8 @@ export const shortGoal = (goal, max) => {
448
449
  };
449
450
  /** Columns the machine row always spends on the task title, however long its machinery segment runs. */
450
451
  const MACHINE_TITLE_FLOOR = 24;
452
+ /** The plain print's own width when none is configured: its machine rows are laid out, and parsed whole, at it. */
453
+ const MACHINE_COLUMNS = 120;
451
454
  // The approved task board derives an area from files[] rather than adding another graph field.
452
455
  // Known tickmarkr domains keep their reviewed names; other repositories fall back to their first
453
456
  // meaningful path segment so the shipped surface never turns a foreign project into UNMAPPED rows.
@@ -1037,7 +1040,7 @@ binaryVersion, now = Date.now(), animationFrame = 0, workerLiveness = new Map(),
1037
1040
  // SUP-01: derived by stat() alone, from THIS frame's clock. status stays a reader — nothing here
1038
1041
  // creates, touches or reaps a beat file, so an absent supervision dir stays absent.
1039
1042
  const supervision = readSupervision(cwd, now);
1040
- const width = process.stdout.columns ?? 120;
1043
+ const width = process.stdout.columns ?? MACHINE_COLUMNS;
1041
1044
  const done = journalRowsOnly
1042
1045
  ? recordedDone(renderedTasks, taskRows)
1043
1046
  : effective.tasks.filter((task) => task.status === "done").length;
@@ -1055,6 +1058,16 @@ binaryVersion, now = Date.now(), animationFrame = 0, workerLiveness = new Map(),
1055
1058
  if (!taskIds.has(taskId))
1056
1059
  phases.delete(taskId);
1057
1060
  const hotPhase = [...phases.values()].sort((a, b) => a.order - b.order).at(-1);
1061
+ // CG1: the three lead lines — finished, wall, needs you from the CURRENT owed-check fold — open
1062
+ // every frame, plain and terminal; the header below keeps every comparability, supersession and verify fact.
1063
+ const lead = runId
1064
+ ? leadLines(events, currentOwed(events, cwd), {
1065
+ // The header's own rule: while the tip is unverified the tally never reads as a finished run.
1066
+ tally: comparable ? tipPhase ? `${done}/${total} tasks done` : `${done}/${total} done` : NOT_COMPARABLE_CLAIM,
1067
+ tip: verify ? `tip ${verify}` : "tip verify pending",
1068
+ notes: supersededBy ? [`superseded by ${supersededBy}`] : [],
1069
+ }).map(sanitizeTaskText)
1070
+ : [];
1058
1071
  // OBS-738: recovery facts stay on the journal's two established reducers. The prefix fold keeps an
1059
1072
  // older journal equally readable by asking upheldFeedbackByTask what was active at that exact
1060
1073
  // restore boundary; current resume-restore rows additionally carry the same task for narration.
@@ -1151,8 +1164,16 @@ binaryVersion, now = Date.now(), animationFrame = 0, workerLiveness = new Map(),
1151
1164
  ? `tickmarkr status${divider}${runLine ?? `run ${runId}`}${zone}${supersededBy ? `${divider}superseded by ${supersededBy}` : ""}${!comparable ? `${divider}${NOT_COMPARABLE_NOTICE}` : ""}${divider}${liveness(events, daemon, now).replaceAll(" · ", divider)}${verify ? `${divider}${verify}` : ""}${divider}${tipPhase ? `${done}/${total} tasks done${divider}run not verified` : `${done}/${total} done`}`
1152
1165
  : `tickmarkr status${zone}${divider}no runs yet${divider}${done}/${total} done`;
1153
1166
  const legendLine = ` gates: ${GATE_NAMES.map((gate) => `${GATE_KEYS[gate]} ${gate}`).join(divider)}`;
1167
+ // At its machine width and wider the plain print keeps each machine row whole — one row per task and
1168
+ // its evidence line after it, which consumers parse. A configured width narrower than that holds for
1169
+ // EVERY row: one that overflows wraps beneath its own indent, its identity kept on the first.
1170
+ const body = [header, legendLine, ` ${supervisionText(supervision, divider)}`, ...rows];
1171
+ const wrapRow = (line) => wrapCells(line, width, { continuationPrefix: `${/^ */u.exec(line)[0]} ` });
1154
1172
  return {
1155
- content: [header, legendLine, ` ${supervisionText(supervision, divider)}`, ...rows].join("\n"),
1173
+ content: [
1174
+ ...leadRows(lead, width),
1175
+ ...(width >= MACHINE_COLUMNS ? body : body.flatMap(wrapRow)),
1176
+ ].join("\n"),
1156
1177
  ...(hotPhase ? { hotPhase } : {}),
1157
1178
  ...(runId ? { runId } : {}),
1158
1179
  workerPhases: [...phases.values()].filter((phase) => phase.phase === "worker"),
@@ -1349,6 +1370,7 @@ binaryVersion, now = Date.now(), animationFrame = 0, workerLiveness = new Map(),
1349
1370
  const effort = effortPanel(g.tasks, record && !comparable ? undefined : events, boardColumns);
1350
1371
  return {
1351
1372
  content: boardRows([
1373
+ ...leadRows(lead, boardColumns),
1352
1374
  ...headerRows,
1353
1375
  ...nowLine,
1354
1376
  supervisionLegend,
@@ -1404,7 +1426,12 @@ const oneLine = (cwd, namedRunId) => {
1404
1426
  `verify ${recordedVerify(record.events)}`,
1405
1427
  ]
1406
1428
  : [NOT_COMPARABLE_CLAIM, "verify unavailable"];
1407
- return sanitizeTaskText([record.runId, ...claims].join(" · "));
1429
+ // CG1: the lead's facts in one line — lineage first pass, the wall window, and what needs you from
1430
+ // the CURRENT fold, so a discharge after run-end moves this line too.
1431
+ const { events } = record;
1432
+ return sanitizeTaskText([
1433
+ record.runId, ...claims, firstPassText(endToEndFirstPass(events)), wallText(events), needsYouText(events, currentOwed(events, cwd)),
1434
+ ].join(" · "));
1408
1435
  };
1409
1436
  export async function status(argv, cwd = process.cwd(), opts = {}) {
1410
1437
  if (argv.some((arg) => arg === "--help" || arg === "-h"))
@@ -7,7 +7,7 @@ export declare const HUMAN_AUTHOR: Assignment;
7
7
  export declare function verifyStateDir(cwd: string): string;
8
8
  /** Linked worktrees share operational state with the repository that owns the common git dir. */
9
9
  export declare function verifyStateRoot(cwd: string): Promise<string>;
10
- /** Cache identity is repository content, never a checkout path. */
10
+ /** Repository content plus the effective runner inputs the daemon hashes; never a checkout path. */
11
11
  export declare function baselineCachePath(cwd: string, baseSha: string, commands: Record<string, string>): string;
12
12
  export declare function excludeAuthorProvider(channels: BillingChannel[], author: BillingChannel): BillingChannel[];
13
13
  export declare const VERIFY_HELP = "usage: tickmarkr verify [--base <ref>] [--criteria <file> | --task <id>] [--json]\nThe final verdict and JSON result are written to stdout; progress and diagnostics are written to stderr.\nDo not merge stdout and stderr (for example with 2>&1): doing so corrupts the verdict stream.";