tickmarkr 2.6.2 → 2.6.4

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 +12 -2
  2. package/dist/adapters/codex.d.ts +2 -1
  3. package/dist/adapters/codex.js +26 -5
  4. package/dist/adapters/model-lints.d.ts +1 -0
  5. package/dist/adapters/model-lints.js +48 -1
  6. package/dist/adapters/model-windows.d.ts +2 -1
  7. package/dist/adapters/model-windows.js +18 -7
  8. package/dist/adapters/types.d.ts +6 -0
  9. package/dist/cli/commands/approve.d.ts +6 -3
  10. package/dist/cli/commands/approve.js +43 -6
  11. package/dist/cli/commands/doctor.d.ts +8 -2
  12. package/dist/cli/commands/doctor.js +6 -1
  13. package/dist/cli/commands/fleet.js +264 -59
  14. package/dist/cli/commands/init.js +5 -3
  15. package/dist/cli/commands/plan.js +13 -8
  16. package/dist/cli/commands/report.d.ts +40 -2
  17. package/dist/cli/commands/report.js +278 -11
  18. package/dist/cli/commands/resume.js +2 -4
  19. package/dist/cli/commands/run.d.ts +11 -0
  20. package/dist/cli/commands/run.js +33 -5
  21. package/dist/cli/commands/status.js +49 -4
  22. package/dist/cli/commands/verify.js +332 -121
  23. package/dist/compile/native.js +137 -0
  24. package/dist/config/config.d.ts +40 -9
  25. package/dist/config/config.js +133 -15
  26. package/dist/config/fleet-overlay.d.ts +13 -3
  27. package/dist/config/fleet-overlay.js +12 -8
  28. package/dist/drivers/herdr.d.ts +12 -0
  29. package/dist/drivers/herdr.js +51 -0
  30. package/dist/drivers/orca.d.ts +9 -1
  31. package/dist/drivers/orca.js +29 -7
  32. package/dist/drivers/types.d.ts +2 -0
  33. package/dist/drivers/types.js +2 -2
  34. package/dist/gates/acceptance.d.ts +7 -0
  35. package/dist/gates/acceptance.js +27 -5
  36. package/dist/gates/baseline.d.ts +20 -1
  37. package/dist/gates/baseline.js +100 -20
  38. package/dist/gates/cache.d.ts +8 -0
  39. package/dist/gates/cache.js +12 -2
  40. package/dist/gates/llm.d.ts +6 -0
  41. package/dist/gates/llm.js +27 -8
  42. package/dist/gates/review.d.ts +6 -1
  43. package/dist/gates/review.js +122 -32
  44. package/dist/gates/run-gates.d.ts +54 -3
  45. package/dist/gates/run-gates.js +331 -45
  46. package/dist/gates/test-manifest.d.ts +42 -0
  47. package/dist/gates/test-manifest.js +69 -10
  48. package/dist/route/router.d.ts +23 -1
  49. package/dist/route/router.js +54 -16
  50. package/dist/run/consult.d.ts +3 -1
  51. package/dist/run/consult.js +4 -2
  52. package/dist/run/daemon.d.ts +2 -1
  53. package/dist/run/daemon.js +349 -79
  54. package/dist/run/interactive-seed.d.ts +4 -0
  55. package/dist/run/interactive-seed.js +35 -9
  56. package/dist/run/journal.d.ts +123 -1
  57. package/dist/run/journal.js +480 -17
  58. package/dist/run/lease.d.ts +13 -0
  59. package/dist/run/lease.js +45 -0
  60. package/dist/run/protocol.d.ts +15 -0
  61. package/dist/run/protocol.js +11 -1
  62. package/dist/run/receipt-resolver.d.ts +22 -0
  63. package/dist/run/receipt-resolver.js +40 -1
  64. package/dist/run/repair-selection.d.ts +11 -1
  65. package/dist/run/repair-selection.js +17 -9
  66. package/dist/run/wall-budget.d.ts +48 -0
  67. package/dist/run/wall-budget.js +280 -0
  68. package/dist/tui/cockpit/live-store.d.ts +6 -0
  69. package/dist/tui/cockpit/live-store.js +36 -11
  70. package/dist/tui/cockpit/run-cockpit.js +2 -2
  71. package/dist/tui/cockpit/run-view.d.ts +2 -1
  72. package/dist/tui/cockpit/run-view.js +13 -9
  73. package/dist/tui/cockpit/setup-cockpit.d.ts +2 -0
  74. package/dist/tui/cockpit/setup-cockpit.js +4 -0
  75. package/package.json +2 -1
  76. package/schema/config.schema.json +8 -1
  77. package/skills/tickmarkr-auto/SKILL.md +2 -2
  78. package/skills/tickmarkr-loop/SKILL.md +17 -5
  79. package/skills/tickmarkr-overseer/scripts/classify-vitest-log.sh +4 -1
@@ -1,4 +1,5 @@
1
1
  import { type Assignment, type WorkerAdapter } from "../adapters/types.js";
2
+ import { DeliveryReadinessError } from "../drivers/herdr.js";
2
3
  import type { ExecutorDriver, Slot } from "../drivers/types.js";
3
4
  export interface InteractiveSeedResult {
4
5
  output: string;
@@ -8,6 +9,9 @@ export interface InteractiveSeedResult {
8
9
  trustAnswered: boolean;
9
10
  }
10
11
  type SeedDriver = Pick<ExecutorDriver, "run" | "waitOutput" | "read" | "sendKey">;
12
+ export declare class SeedReadinessError extends DeliveryReadinessError {
13
+ constructor(waitedMs: number, transcript: string, readinessMatch: string);
14
+ }
11
15
  export declare function runInteractiveSeed(opts: {
12
16
  driver: SeedDriver;
13
17
  slot: Slot;
@@ -1,4 +1,5 @@
1
1
  import { matchesTrustDialog } from "../adapters/types.js";
2
+ import { DeliveryReadinessError } from "../drivers/herdr.js";
2
3
  // The workspace-trust prompt is a STARTUP gate: it renders before the readiness banner and blocks
3
4
  // it, so this wait is the only window in which anything can answer it. Observation therefore runs
4
5
  // for the WHOLE readiness budget. Review round 7 (material): a 60-second cutoff here was a window
@@ -9,6 +10,18 @@ import { matchesTrustDialog } from "../adapters/types.js";
9
10
  // blocked anyway, and it ends at the first of readiness, a modal, or the deadline.
10
11
  const TRUST_POLL_MS = 1_000;
11
12
  const TRUST_PANE_ROWS = 80;
13
+ // OBS-1205 add.1: a launch whose readiness never appeared never received the brief, so its deadline is
14
+ // the delivery-readiness class (OBS-142: the interface never became interactive), not a launched
15
+ // worker. Thrown as that class, the daemon's existing catch journals `delivery-readiness-failed`,
16
+ // closes the slot and walks the escalation ladder to a failover — with no `worker-launch` row, which is
17
+ // where a funded repair's findings expire, so the repair the seed never delivered stays owed.
18
+ export class SeedReadinessError extends DeliveryReadinessError {
19
+ constructor(waitedMs, transcript, readinessMatch) {
20
+ super(waitedMs, transcript);
21
+ this.name = "SeedReadinessError";
22
+ this.message = `seed readiness pattern not seen after ${waitedMs}ms: ${readinessMatch}`;
23
+ }
24
+ }
12
25
  // v1.89 T19 / OBS-406: wait for readiness, answering a fingerprint-matched trust modal at most ONCE
13
26
  // on the way. The daemon's own trust loop runs only after runInteractiveSeed returns, and this wait
14
27
  // is exactly the window the modal blocks in — a declaration consulted only there is unreachable
@@ -17,23 +30,34 @@ async function awaitReadiness(opts) {
17
30
  const { driver, slot, readinessMatch, deadline } = opts;
18
31
  const dialog = opts.adapter.trustDialog;
19
32
  const left = () => Math.max(0, deadline - Date.now());
33
+ // Every wait below is awaited, and the budget can run out inside any of them (OrcaDriver.waitOutput
34
+ // checks its sweep before its clock). Readiness a wait returns after the deadline is expired — it may
35
+ // be what a key pressed at the edge produced — and a launch whose deadline passed has already
36
+ // failed, so neither a key nor a seed follows it. Both branches apply it.
37
+ const expired = () => Date.now() >= deadline;
38
+ const readyInTime = async (ms) => (await driver.waitOutput(slot, readinessMatch, ms)) && !expired();
20
39
  // Nothing this launch could answer (no declaration, an honest {kind:"none"}, or a driver with no
21
- // keystroke surface): the single long wait, byte-identical to pre-T19 behaviour.
40
+ // keystroke surface): the single long wait, pre-T19 behaviour plus the post-await deadline check.
22
41
  if (!dialog || dialog.kind === "none" || !driver.sendKey) {
23
- return { ready: await driver.waitOutput(slot, readinessMatch, left()), trustAnswered: false };
42
+ return { ready: await readyInTime(left()), trustAnswered: false };
24
43
  }
25
44
  let trustAnswered = false;
26
- while (!trustAnswered && left() > 0) {
27
- if (await driver.waitOutput(slot, readinessMatch, Math.min(TRUST_POLL_MS, left()))) {
45
+ while (!trustAnswered && !expired()) {
46
+ if (await readyInTime(Math.min(TRUST_POLL_MS, left()))) {
28
47
  return { ready: true, trustAnswered };
29
48
  }
30
49
  let paneText;
31
50
  try {
32
- paneText = await driver.read(slot, TRUST_PANE_ROWS);
51
+ // OBS-1205: a cursor-drawn modal has text only on the RENDERED frame, so the slot's screen read
52
+ // is preferred when its driver has one. An unreadable or foreign frame is a failed read, never
53
+ // a cue to match the stream instead; drivers without a screen keep polling `read`.
54
+ paneText = await (slot.readScreen ? slot.readScreen() : driver.read(slot, TRUST_PANE_ROWS));
33
55
  }
34
56
  catch {
35
57
  continue; // a failed read is not a matched modal — keep observing, spend nothing
36
58
  }
59
+ if (expired())
60
+ break; // the wait or the read outlived the budget: no key after the deadline
37
61
  if (!matchesTrustDialog(paneText, dialog))
38
62
  continue;
39
63
  // Review round 7 (material): the latch is spent BEFORE the awaited send, not after it. A send
@@ -54,7 +78,8 @@ async function awaitReadiness(opts) {
54
78
  /* dispatched-then-rejected: never retried here, and never re-tried by the daemon either */
55
79
  }
56
80
  }
57
- return { ready: await driver.waitOutput(slot, readinessMatch, left()), trustAnswered };
81
+ // An expired budget gets no closing sweep: a zero-length wait still reads the terminal once.
82
+ return { ready: !expired() && await readyInTime(left()), trustAnswered };
58
83
  }
59
84
  // v1.69 T6: launch-then-seed handoff for adapters whose real TUI cannot be argv-seeded.
60
85
  // Both the launch command and the seed line are delivered through the driver's existing `run`
@@ -65,6 +90,8 @@ export async function runInteractiveSeed(opts) {
65
90
  await opts.driver.run(opts.slot, seed.launch(opts.assignment.model));
66
91
  // `trustAnswered` rides EVERY return below, including both early ones: the daemon initializes its
67
92
  // per-slot latch from it, and an omission there reads as "no key was sent" — the second-Enter defect.
93
+ // The readiness deadline throws instead; `onTrustAnswered` already carried the latch out.
94
+ const launchedAt = Date.now();
68
95
  const { ready, trustAnswered } = await awaitReadiness({
69
96
  driver: opts.driver,
70
97
  slot: opts.slot,
@@ -74,9 +101,8 @@ export async function runInteractiveSeed(opts) {
74
101
  ...(opts.onTrustAnswered ? { onTrustAnswered: opts.onTrustAnswered } : {}),
75
102
  });
76
103
  const banner = await opts.driver.read(opts.slot, 1000);
77
- if (!ready) {
78
- return { output: banner, seedFailed: true, seedError: `readiness pattern not seen: ${seed.readinessMatch}`, trustAnswered };
79
- }
104
+ if (!ready)
105
+ throw new SeedReadinessError(Date.now() - launchedAt, banner, seed.readinessMatch);
80
106
  let sessionId;
81
107
  if (seed.confirmBanner) {
82
108
  const confirm = seed.confirmBanner(banner, opts.assignment.model);
@@ -107,6 +107,8 @@ export interface StructuredFinding {
107
107
  observedFingerprints?: string[];
108
108
  /** Prior id validated against the reviewer's reraised list by the review gate. */
109
109
  reraisedFrom?: string;
110
+ /** OBS-1195: an anchor's explicitly named parent chain; it retires when that chain is resolved. */
111
+ boundTo?: string;
110
112
  /** Resolved definition AND defect identity; bare symbol spelling is never lineage. */
111
113
  codeIdentity?: {
112
114
  definition: string;
@@ -185,7 +187,10 @@ export declare function formatPriorFindingEvidence(evidence: PriorFindingEvidenc
185
187
  /** Normalized identity of a gate failure: the same defect, seen twice, normalizes to the same bytes. */
186
188
  export declare function normalizeGateFailure(details: string): string;
187
189
  export declare const GATE_FINGERPRINT_CAP = 2;
190
+ /** The invocation a gate row's receipt names — the identity of the execution that observed it. */
191
+ export declare const receiptOrigin: (data: Record<string, unknown>) => string | undefined;
188
192
  export declare function identicalGateFailures(events: JournalEvent[], taskId: string, gate: string, normalized: string): number;
193
+ export declare const readableExcerpt: (text: string) => string;
189
194
  /** What each funded repair's next battery actually reached. A repair spends its budget only
190
195
  * when that battery reaches one of the gates it was funded to fix; an earlier red is evidence that
191
196
  * this repair never got its funded turn, not a charge against the repair ladder. */
@@ -309,7 +314,28 @@ export declare function carryReviewFindings(priors: readonly StructuredFinding[]
309
314
  * after round re-seats ONE fingerprint, and a revised rationale replaces the prior rationale on that
310
315
  * row. N rounds of the same concern therefore carry the newest accepted explanation once, not N rows.
311
316
  */
317
+ /** OBS-1151: this task's journaled judgments, newest first — subjects to compare a fresh ruling against,
318
+ * never verdicts to reuse. A row without a well-formed judgment (legacy, park, unparseable) is skipped. */
319
+ export declare function priorJudgments(events: readonly JournalEvent[], taskId: string): Array<{
320
+ commit: string;
321
+ judge?: string;
322
+ criteria: Array<{
323
+ id: string;
324
+ key: string;
325
+ met: boolean;
326
+ paths: string[];
327
+ }>;
328
+ }>;
312
329
  export declare function outstandingReviewFindings(events: JournalEvent[], taskId: string): StructuredFinding[];
330
+ /**
331
+ * OBS-1019 add.2: each outstanding material chain with the number of valid review verdicts that
332
+ * re-raised it since the last review pass or review-gate waive. Only a chain's own observed spellings
333
+ * count, so an unrelated finding sharing a path never lengthens it.
334
+ */
335
+ export declare function reraisedReviewChains(events: JournalEvent[], taskId: string): Array<{
336
+ finding: StructuredFinding;
337
+ reraises: number;
338
+ }>;
313
339
  /** The findings a funded repair must carry into the next dispatch, or undefined if none is pending. */
314
340
  export declare function pendingRepairFindings(events: JournalEvent[], taskId: string): string | undefined;
315
341
  /**
@@ -409,8 +435,8 @@ export declare const TelemetryRowSchema: z.ZodObject<{
409
435
  quotaFailover: z.ZodOptional<z.ZodLiteral<true>>;
410
436
  overrun: z.ZodOptional<z.ZodLiteral<true>>;
411
437
  retryMode: z.ZodOptional<z.ZodEnum<{
412
- repair: "repair";
413
438
  resume: "resume";
439
+ repair: "repair";
414
440
  fresh: "fresh";
415
441
  }>>;
416
442
  signalQuality: z.ZodOptional<z.ZodUnion<readonly [z.ZodLiteral<0>, z.ZodLiteral<0.25>, z.ZodLiteral<0.5>, z.ZodLiteral<0.75>, z.ZodLiteral<1>]>>;
@@ -567,4 +593,100 @@ export declare class Journal {
567
593
  readTelemetry(): TelemetryRow[];
568
594
  readJudgeTelemetry(): TelemetryRow[];
569
595
  }
596
+ export declare const OWED_CHECK_VERSION: 1;
597
+ export declare const OWED_DISCHARGE_EVENT: "owed-check-discharged";
598
+ export interface OwedCheck {
599
+ version: typeof OWED_CHECK_VERSION;
600
+ id: string;
601
+ runId: string;
602
+ taskId: string;
603
+ gate: GateName;
604
+ /** The immutable range the waived gate measured, and its canonical subject (daemon gateCommitSubject). */
605
+ base: string;
606
+ head: string;
607
+ subject: string;
608
+ /** Verbatim (whitespace-sensitive) patch identity of the whole range and of each commit, in order — the integration mapping key. */
609
+ patch: string;
610
+ patches: string[];
611
+ criteria: string;
612
+ /** The task's file scope at waive time: a discharge gates exactly this allowlist, never a wider one. */
613
+ files: string[];
614
+ authors: string[];
615
+ /** The channels this repository's adapters declared under its config at waive time (key → vendor):
616
+ * the only seats a review discharge's reviewer and author channels may resolve to. */
617
+ declared: ChannelFact[];
618
+ cause: string;
619
+ evidence: string;
620
+ disposition: "accepted-risk";
621
+ known: boolean;
622
+ unknownReason?: string;
623
+ }
624
+ export interface OwedFold {
625
+ known: boolean;
626
+ debt: number | "unknown";
627
+ outstanding: OwedCheck[];
628
+ acceptedRisk: OwedCheck[];
629
+ discharged: string[];
630
+ unknown: Array<{
631
+ taskId?: string;
632
+ gate?: string;
633
+ reason: string;
634
+ }>;
635
+ }
636
+ export declare const owedCriteria: (acceptance: unknown) => string;
637
+ /** The daemon's canonical gate subject for base..head (daemon.ts gateCommitSubject), byte for byte. */
638
+ export declare const owedSubject: (cwd: string, base: string, head: string) => string;
639
+ /** Ordered verbatim patch ids of the commits in base..head; an empty commit authored no patch. */
640
+ export declare const rangePatches: (cwd: string, base: string, head: string) => string[];
641
+ /** Verbatim patch identity of the whole change base..head introduces. */
642
+ export declare const rangePatch: (cwd: string, base: string, head: string) => string;
643
+ /**
644
+ * `head` is the task's own recorded integration merge of the obligation's range — exactly. Its first
645
+ * parent is `mergeBase`; its second parent replays the waived history source tree for source tree from
646
+ * a base with the waived base's tree (a carry may mint new commit objects, never new content — and
647
+ * patch ids alone ignore where a hunk lands); its tree is git's own clean merge of the two (no
648
+ * resolution, no amend); and it introduced the same ordered verbatim patch series and total patch.
649
+ * Squashed, reordered, relocated, re-indented, reverted or conflict-resolved history fails one of
650
+ * these and is refused as ambiguous; containment never counts.
651
+ * Both verify (before discharging) and the fold (re-reading every discharge) decide through here.
652
+ */
653
+ export declare function integrationMapped(cwd: string, events: readonly JournalEvent[], o: OwedCheck, mergeBase: unknown, head: unknown): boolean;
654
+ /**
655
+ * Lifetime patch authors of base..head, read from the run: the daemon's subjectAuthors fold
656
+ * (daemon.ts, not exported and not this task's to edit) restated synchronously so the waive row is
657
+ * written without yielding. Recreation rows name SOURCE hashes, so ownership joins by stable patch
658
+ * identity across changed commit objects; a gate-only restore (resume-restore's seat, a recreation
659
+ * after work started) never becomes an author. Unreadable history names an unknown author.
660
+ */
661
+ export declare function owedAuthors(events: readonly JournalEvent[], taskId: string, cwd: string, base: string, head: string): string[];
662
+ /**
663
+ * The obligation a waive of `gate` leaves on the task branch's current range. Synchronous on purpose:
664
+ * approve writes it into the waive row under the approval serialization without yielding, so a live
665
+ * daemon's boundary sees the same single append it always did. Anything not bound exactly — the
666
+ * waived row measured another subject, unreadable refs, unresolved authors, unreadable criteria — is
667
+ * recorded, never guessed, as known: false.
668
+ */
669
+ export declare function captureOwedCheck(input: {
670
+ cwd: string;
671
+ runId: string;
672
+ taskId: string;
673
+ gate: GateName;
674
+ cause: string;
675
+ acceptance: unknown;
676
+ files: unknown;
677
+ declared: readonly ChannelFact[] | undefined;
678
+ taskRef: string;
679
+ integrationRef: string;
680
+ events: readonly JournalEvent[];
681
+ }): OwedCheck;
682
+ export type ChannelFact = {
683
+ key: string;
684
+ vendor: string;
685
+ };
686
+ /**
687
+ * The CURRENT owed-check fold: accepted-risk history, outstanding debt, and whether it is known.
688
+ * `cwd` is any checkout of the run's repository (integration discharges are re-proved from git).
689
+ * It never throws: a fold that cannot be computed is unknown debt, not a crash and never zero.
690
+ */
691
+ export declare function foldOwedChecks(events: readonly JournalEvent[], cwd: string): OwedFold;
570
692
  export {};