tickmarkr 2.5.4 → 2.5.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 (59) hide show
  1. package/dist/adapters/registry.js +6 -1
  2. package/dist/adapters/types.d.ts +3 -0
  3. package/dist/cli/commands/approve.d.ts +1 -0
  4. package/dist/cli/commands/approve.js +54 -6
  5. package/dist/cli/commands/doctor.js +44 -29
  6. package/dist/cli/commands/fleet.js +195 -33
  7. package/dist/cli/commands/init.js +196 -6
  8. package/dist/cli/commands/resume.js +4 -2
  9. package/dist/cli/commands/run.js +11 -2
  10. package/dist/cli/commands/verify.js +1 -1
  11. package/dist/cli/help.d.ts +2 -0
  12. package/dist/cli/help.js +3 -1
  13. package/dist/config/config.d.ts +14 -8
  14. package/dist/config/config.js +29 -26
  15. package/dist/config/fleet-overlay.d.ts +3 -9
  16. package/dist/config/fleet-overlay.js +68 -11
  17. package/dist/config/fleet-why.d.ts +7 -0
  18. package/dist/config/fleet-why.js +5 -0
  19. package/dist/drivers/index.d.ts +15 -1
  20. package/dist/drivers/index.js +38 -10
  21. package/dist/drivers/orca.d.ts +99 -10
  22. package/dist/drivers/orca.js +586 -97
  23. package/dist/gates/baseline.d.ts +3 -0
  24. package/dist/gates/baseline.js +2 -1
  25. package/dist/gates/cache.d.ts +100 -0
  26. package/dist/gates/cache.js +389 -0
  27. package/dist/gates/run-gates.d.ts +3 -0
  28. package/dist/gates/run-gates.js +117 -10
  29. package/dist/gates/test-manifest.d.ts +99 -0
  30. package/dist/gates/test-manifest.js +389 -0
  31. package/dist/gates/test-reporter.d.ts +4 -0
  32. package/dist/gates/test-reporter.js +49 -0
  33. package/dist/route/preference.d.ts +22 -1
  34. package/dist/route/preference.js +123 -25
  35. package/dist/route/router.js +31 -6
  36. package/dist/run/daemon.d.ts +13 -0
  37. package/dist/run/daemon.js +160 -70
  38. package/dist/run/git.d.ts +10 -1
  39. package/dist/run/git.js +43 -9
  40. package/dist/run/journal.d.ts +14 -2
  41. package/dist/run/journal.js +78 -10
  42. package/dist/run/lease.d.ts +14 -0
  43. package/dist/run/lease.js +87 -0
  44. package/dist/run/merge.d.ts +2 -0
  45. package/dist/run/merge.js +91 -3
  46. package/dist/run/operator-state.d.ts +11 -0
  47. package/dist/run/operator-state.js +17 -3
  48. package/dist/tui/cockpit/board.d.ts +96 -0
  49. package/dist/tui/cockpit/board.js +346 -0
  50. package/dist/tui/cockpit/decision-actions.js +2 -0
  51. package/dist/tui/cockpit/layout.d.ts +5 -1
  52. package/dist/tui/cockpit/layout.js +8 -3
  53. package/dist/tui/cockpit/live-runtime.js +71 -21
  54. package/dist/tui/cockpit/run-view.d.ts +7 -5
  55. package/dist/tui/cockpit/run-view.js +12 -11
  56. package/dist/tui/ink/fleet-app.d.ts +41 -27
  57. package/dist/tui/ink/fleet-app.js +204 -31
  58. package/package.json +1 -1
  59. package/skills/tickmarkr-overseer/SKILL.md +173 -113
@@ -1,6 +1,6 @@
1
1
  // Fleet-overlay mutation, serialization, and diff rendering for the `tickmarkr fleet` write path.
2
2
  import { isMap, isScalar, isSeq, parseDocument, stringify, visit } from "yaml";
3
- import { universeCovers } from "./config.js";
3
+ import { universeCovers, universeEntryMatches } from "./config.js";
4
4
  /** Fleet-owned overlay keys — the only config surface `tickmarkr fleet` may write. */
5
5
  export const FLEET_OVERLAY_KEYS = ["routing", "tiers"];
6
6
  function fleetSubset(obj) {
@@ -21,17 +21,18 @@ function allowFormFromExclusions(universe, edited) {
21
21
  if (!universe.length) {
22
22
  throw new Error("fleet write: universe is empty — no classified models to compute routing.allow from; classify models in `tickmarkr fleet` first");
23
23
  }
24
- const denyAdapters = new Set(edited.denyAdapters);
25
- const denyModels = new Set(edited.denyModels);
24
+ // LEG2-T3 round 2 finding 1: every staged entry excludes what it NAMES — a bare model id every
25
+ // adapter serving it, an identity its alias — never only an adapter id or an adapter:model key.
26
+ const entries = [...edited.denyAdapters, ...edited.denyModels];
26
27
  const adapters = [];
27
28
  const models = [];
28
29
  let excluded = false;
29
30
  for (const row of universe) {
30
- if (denyAdapters.has(row.adapter)) {
31
+ if (entries.includes(row.adapter)) {
31
32
  excluded = true;
32
33
  continue;
33
34
  }
34
- const inFleet = row.models.filter((m) => !denyModels.has(`${row.adapter}:${m}`));
35
+ const inFleet = row.models.filter((m) => !entries.some((entry) => universeEntryMatches(row, m, entry)));
35
36
  if (inFleet.length === row.models.length) {
36
37
  adapters.push(row.adapter);
37
38
  }
@@ -49,6 +50,21 @@ function allowFormFromExclusions(universe, edited) {
49
50
  function residualDeny(universe, entries) {
50
51
  return sortedUnique(entries.filter((entry) => !universeCovers(universe, entry)));
51
52
  }
53
+ // LEG2-T3 round 2 finding 2: the flat deny list a membership write leaves behind. Every entry the
54
+ // repo overlay authored in THIS list that is still staged stays verbatim, in its authored order and
55
+ // with its comments — one cleared reason never takes an independent one with it, and an untouched
56
+ // list keeps its node. A staged entry the allow form cannot express as a membership key (outside the
57
+ // probe universe, or a bare-model/identity spelling) is written verbatim too; canonical keys the
58
+ // session added ride the allow form alone.
59
+ function flatDenyAfterWrite(doc, path, after, universe) {
60
+ const node = doc.getIn(path, true);
61
+ const authored = isSeq(node) ? node.items.flatMap((item) => (isScalar(item) ? [String(item.value)] : [])) : [];
62
+ const canonical = (entry) => universe.some((row) => entry === row.adapter || row.models.some((m) => entry === `${row.adapter}:${m}`));
63
+ const kept = authored.filter((entry) => after.includes(entry));
64
+ const verbatim = sortedUnique(after.filter((entry) => !kept.includes(entry)
65
+ && (!universeCovers(universe, entry) || !canonical(entry))));
66
+ return [...new Set([...kept, ...verbatim])];
67
+ }
52
68
  // fleet.ts deliberately remains the sole overlay builder and writer. Its established classification
53
69
  // seam copies only `tier` and `note` into FleetEditable, so first-touch entry metadata rides inside a
54
70
  // private provenance envelope until this module writes the YAML. The envelope never reaches disk.
@@ -199,18 +215,35 @@ export function renderFleetOverlayWrite(priorBytes, write) {
199
215
  // Whole fleet in: no restriction to express — the allow block goes away entirely.
200
216
  deleteAt(doc, ["routing", "allow"]);
201
217
  }
202
- // Residuals stay in deny; covered scopes are tombstoned so a lower layer can never
203
- // re-exclude behind the operator's back (workers untouched).
204
- const residualAdapters = residualDeny(write.universe, edited.denyAdapters);
205
- const residualModels = residualDeny(write.universe, edited.denyModels);
206
- setStringSequencePreservingComments(doc, ["routing", "deny", "adapters"], residualAdapters.length ? residualAdapters : null);
207
- setStringSequencePreservingComments(doc, ["routing", "deny", "models"], residualModels.length ? residualModels : null);
218
+ // Authored and non-canonical entries stay in deny (LEG2-T3 finding 4, round 2 finding 2); a
219
+ // list left with nothing is tombstoned so a lower layer can never re-exclude behind the
220
+ // operator's back (workers untouched).
221
+ for (const [scope, after] of [["adapters", edited.denyAdapters], ["models", edited.denyModels]]) {
222
+ const path = ["routing", "deny", scope];
223
+ const remaining = flatDenyAfterWrite(doc, path, after, write.universe);
224
+ setStringSequencePreservingComments(doc, path, remaining.length ? remaining : null);
225
+ }
208
226
  }
209
227
  else {
210
228
  setStringSequencePreservingComments(doc, ["routing", "deny", "adapters"], edited.denyAdapters.length ? sortedUnique(edited.denyAdapters) : null);
211
229
  setStringSequencePreservingComments(doc, ["routing", "deny", "models"], edited.denyModels.length ? sortedUnique(edited.denyModels) : null);
212
230
  }
213
231
  }
232
+ // OBS-994/FL-1: routing.deny.workers is a literal deny list, never a universe-derived
233
+ // membership scope — it never routes through the allow-complement dance above, in either
234
+ // branch. Same tombstone/comment-preserving rules as the flat scopes.
235
+ const initialWorkersAdapters = initial.denyWorkersAdapters ?? [];
236
+ const editedWorkersAdapters = edited.denyWorkersAdapters ?? [];
237
+ const initialWorkersModels = initial.denyWorkersModels ?? [];
238
+ const editedWorkersModels = edited.denyWorkersModels ?? [];
239
+ // Each sub-path mutates independently — an untouched sibling must not be rewritten (a `null`
240
+ // tombstone over an absent/untouched sibling would mask a lower layer's own workers scope).
241
+ if (sortedUnique(initialWorkersAdapters).join() !== sortedUnique(editedWorkersAdapters).join()) {
242
+ setStringSequencePreservingComments(doc, ["routing", "deny", "workers", "adapters"], editedWorkersAdapters.length ? sortedUnique(editedWorkersAdapters) : null);
243
+ }
244
+ if (sortedUnique(initialWorkersModels).join() !== sortedUnique(editedWorkersModels).join()) {
245
+ setStringSequencePreservingComments(doc, ["routing", "deny", "workers", "models"], editedWorkersModels.length ? sortedUnique(editedWorkersModels) : null);
246
+ }
214
247
  for (const shape of new Set([...Object.keys(initial.map), ...Object.keys(edited.map)])) {
215
248
  const before = initial.map[shape];
216
249
  const after = edited.map[shape];
@@ -379,6 +412,30 @@ export function fleetRepoOverlayFromDelta(initial, edited, existingRepo = {}, fi
379
412
  }
380
413
  routingTouched = true;
381
414
  }
415
+ // OBS-994/FL-1: workers deny is a literal list, independent of the universe/allow dance above.
416
+ // Each sub-path is included only when it actually changed — an untouched sibling must not be
417
+ // rewritten as a `null` tombstone over whatever the existing repo overlay already held.
418
+ const initialWorkersAdapters = initial.denyWorkersAdapters ?? [];
419
+ const editedWorkersAdapters = edited.denyWorkersAdapters ?? [];
420
+ const initialWorkersModels = initial.denyWorkersModels ?? [];
421
+ const editedWorkersModels = edited.denyWorkersModels ?? [];
422
+ const workersAdaptersChanged = sortedUnique(initialWorkersAdapters).join() !== sortedUnique(editedWorkersAdapters).join();
423
+ const workersModelsChanged = sortedUnique(initialWorkersModels).join() !== sortedUnique(editedWorkersModels).join();
424
+ if (workersAdaptersChanged || workersModelsChanged) {
425
+ routing.deny = {
426
+ ...routing.deny,
427
+ workers: {
428
+ ...(routing.deny?.workers),
429
+ ...(workersAdaptersChanged
430
+ ? { adapters: editedWorkersAdapters.length ? editedWorkersAdapters : null }
431
+ : {}),
432
+ ...(workersModelsChanged
433
+ ? { models: editedWorkersModels.length ? editedWorkersModels : null }
434
+ : {}),
435
+ },
436
+ };
437
+ routingTouched = true;
438
+ }
382
439
  // pool widened to accept the null tombstone; MapEntry itself never carries null in memory.
383
440
  const mapDelta = {};
384
441
  for (const shape of new Set([...Object.keys(initial.map), ...Object.keys(edited.map)])) {
@@ -21,3 +21,10 @@ export type FleetWhyOptions = {
21
21
  export declare function projectFleetWhy<Id extends string>(values: readonly FleetWhyValue<Id>[], options: FleetWhyOptions): FleetWhyRow<Id>[];
22
22
  /** Plain line-mode twin of the Shapes rows; labels are projected, never reconstructed here. */
23
23
  export declare function renderFleetWhy(rows: readonly FleetWhyRow[]): string;
24
+ /** LEG2-T3: one exclusion-collector scope as the reason a fleet row shows — its config path, then
25
+ * the entry that matched (a deny) or the fact the allowlist does not admit the channel. */
26
+ export declare function exclusionReason(scope: {
27
+ by: "deny" | "allow";
28
+ configPath: string;
29
+ entry: string;
30
+ }): string;
@@ -40,3 +40,8 @@ export function projectFleetWhy(values, options) {
40
40
  export function renderFleetWhy(rows) {
41
41
  return ["tickmarkr fleet --why — effective shape routing", ...rows.map((row) => row.label)].join("\n");
42
42
  }
43
+ /** LEG2-T3: one exclusion-collector scope as the reason a fleet row shows — its config path, then
44
+ * the entry that matched (a deny) or the fact the allowlist does not admit the channel. */
45
+ export function exclusionReason(scope) {
46
+ return scope.by === "allow" ? `${scope.configPath} (not admitted)` : `${scope.configPath} (${scope.entry})`;
47
+ }
@@ -2,6 +2,20 @@ import type { TickmarkrConfig } from "../config/config.js";
2
2
  import type { ExecutorDriver } from "./types.js";
3
3
  export declare const DRIVER_CHOICES: readonly ["auto", "herdr", "subprocess", "orca"];
4
4
  export type DriverChoice = (typeof DRIVER_CHOICES)[number];
5
+ export type ClassifiedHost = "herdr" | "orca" | "none";
6
+ /**
7
+ * One host classifier beside the driver chooser reads the launching environment:
8
+ * herdr only when HERDR_ENV is exactly 1, orca only when both Orca markers are present,
9
+ * none otherwise. run and resume call it once at the refs-preflight point and thread the
10
+ * result into pickDriver; nothing downstream reads the markers again.
11
+ */
12
+ export declare function classifyHost(env?: NodeJS.ProcessEnv): ClassifiedHost;
13
+ /**
14
+ * A config driver of herdr or orca whose host is not the classified one is refused naming the
15
+ * detected host, the config line and the --driver remedy. Any explicit --driver value bypasses
16
+ * this; auto and subprocess are never refused.
17
+ */
18
+ export declare function preflightHostDriver(cfg: TickmarkrConfig, driverOverride: string | undefined, host: ClassifiedHost): void;
5
19
  /**
6
20
  * Orca authors both markers on every terminal it creates. Requiring the pair avoids treating an
7
21
  * unrelated TERM_PROGRAM value or a copied terminal handle as host identity. This is deliberately
@@ -11,4 +25,4 @@ export declare function orcaHostDetected(env?: NodeJS.ProcessEnv): boolean;
11
25
  /** Validate argv at the CLI boundary rather than casting an arbitrary string into a driver choice. */
12
26
  export declare function parseDriverOverride(override?: string): DriverChoice | undefined;
13
27
  export declare function driverEvidence(cfg: TickmarkrConfig, driver: ExecutorDriver, override?: string): string;
14
- export declare function pickDriver(cfg: TickmarkrConfig, override?: string): ExecutorDriver;
28
+ export declare function pickDriver(cfg: TickmarkrConfig, override?: string, host?: ClassifiedHost): ExecutorDriver;
@@ -3,13 +3,42 @@ import { OrcaDriver } from "./orca.js";
3
3
  import { SubprocessDriver } from "./subprocess.js";
4
4
  export const DRIVER_CHOICES = ["auto", "herdr", "subprocess", "orca"];
5
5
  const overrideByDriver = new WeakMap();
6
+ // The host snapshot the driver was selected under: driverEvidence reads this, never process.env,
7
+ // so preflight, selection and the journal row all describe the same instant.
8
+ const hostByDriver = new WeakMap();
9
+ /**
10
+ * One host classifier beside the driver chooser reads the launching environment:
11
+ * herdr only when HERDR_ENV is exactly 1, orca only when both Orca markers are present,
12
+ * none otherwise. run and resume call it once at the refs-preflight point and thread the
13
+ * result into pickDriver; nothing downstream reads the markers again.
14
+ */
15
+ export function classifyHost(env = process.env) {
16
+ if (env.HERDR_ENV === "1")
17
+ return "herdr";
18
+ if (env.TERM_PROGRAM === "Orca" && env.ORCA_TERMINAL_HANDLE !== undefined && env.ORCA_TERMINAL_HANDLE !== "")
19
+ return "orca";
20
+ return "none";
21
+ }
22
+ /**
23
+ * A config driver of herdr or orca whose host is not the classified one is refused naming the
24
+ * detected host, the config line and the --driver remedy. Any explicit --driver value bypasses
25
+ * this; auto and subprocess are never refused.
26
+ */
27
+ export function preflightHostDriver(cfg, driverOverride, host) {
28
+ if (driverOverride !== undefined)
29
+ return;
30
+ if ((cfg.driver === "herdr" || cfg.driver === "orca") && cfg.driver !== host) {
31
+ const remedy = host === "none" ? "subprocess" : host;
32
+ throw new Error(`refusing driver '${cfg.driver}' (config line 'driver: ${cfg.driver}'): detected host is ${host}; use --driver ${remedy} to override`);
33
+ }
34
+ }
6
35
  /**
7
36
  * Orca authors both markers on every terminal it creates. Requiring the pair avoids treating an
8
37
  * unrelated TERM_PROGRAM value or a copied terminal handle as host identity. This is deliberately
9
38
  * environment-only: selection must not execute a binary or contact the Orca runtime.
10
39
  */
11
40
  export function orcaHostDetected(env = process.env) {
12
- return env.TERM_PROGRAM === "Orca" && env.ORCA_TERMINAL_HANDLE !== undefined;
41
+ return classifyHost(env) === "orca";
13
42
  }
14
43
  /** Validate argv at the CLI boundary rather than casting an arbitrary string into a driver choice. */
15
44
  export function parseDriverOverride(override) {
@@ -27,18 +56,16 @@ export function driverEvidence(cfg, driver, override) {
27
56
  return `${driver.id} (--driver)`;
28
57
  if (want !== "auto")
29
58
  return `${driver.id} (config)`;
30
- const herdrAvailable = process.env.HERDR_ENV === "1";
31
- if (herdrAvailable && driver.id === "herdr")
59
+ const host = hostByDriver.get(driver);
60
+ if (host === "herdr" && driver.id === "herdr")
32
61
  return "auto → herdr (HERDR_ENV=1)";
33
- if (!herdrAvailable && orcaHostDetected() && driver.id === "orca") {
62
+ if (host === "orca" && driver.id === "orca")
34
63
  return "auto → orca (TERM_PROGRAM+ORCA_TERMINAL_HANDLE)";
35
- }
36
- if (!herdrAvailable && !orcaHostDetected() && driver.id === "subprocess") {
64
+ if (host === "none" && driver.id === "subprocess")
37
65
  return "auto → subprocess (HERDR_ENV unset)";
38
- }
39
66
  return `auto → ${driver.id} (runtime)`;
40
67
  }
41
- export function pickDriver(cfg, override) {
68
+ export function pickDriver(cfg, override, host = classifyHost()) {
42
69
  const selectedOverride = parseDriverOverride(override);
43
70
  const want = selectedOverride ?? cfg.driver;
44
71
  // VIS-09 item 2: plumb the per-tab cap into the HerdrDriver — the driver takes it as a constructor
@@ -49,10 +76,11 @@ export function pickDriver(cfg, override) {
49
76
  // Orca is an operator-selected execution surface. Its runtime failure stays on Orca; selection
50
77
  // must never substitute a hidden subprocess worker after an explicit or detected choice.
51
78
  : want === "orca" ? new OrcaDriver()
52
- : HerdrDriver.available() ? new HerdrDriver("herdr", cfg.visibility.workersPerTab)
53
- : orcaHostDetected() ? new OrcaDriver()
79
+ : host === "herdr" ? new HerdrDriver("herdr", cfg.visibility.workersPerTab)
80
+ : host === "orca" ? new OrcaDriver()
54
81
  : new SubprocessDriver();
55
82
  if (selectedOverride !== undefined)
56
83
  overrideByDriver.set(driver, selectedOverride);
84
+ hostByDriver.set(driver, host);
57
85
  return driver;
58
86
  }
@@ -1,8 +1,9 @@
1
1
  import { type ShResult } from "../run/git.js";
2
2
  import { type JournalEvent } from "../run/journal.js";
3
+ import { type WatchBoardOwner } from "../run/supervision.js";
3
4
  import { type ExecutorDriver, type FocusTarget, type FocusResult, type NotifyOpts, type Slot, type SlotOpts } from "./types.js";
4
5
  /** The response families the ONE shared envelope parser serves. There is no second JSON seam. */
5
- export declare const ORCA_RESPONSE_FAMILIES: readonly ["status", "create", "list", "read", "send", "wait", "show", "close", "worktree-current", "worktree-set", "hooks-status"];
6
+ export declare const ORCA_RESPONSE_FAMILIES: readonly ["status", "create", "list", "read", "send", "wait", "show", "close", "worktree-current", "worktree-set", "hooks-status", "split"];
6
7
  export type OrcaFamily = (typeof ORCA_RESPONSE_FAMILIES)[number];
7
8
  export declare const ORCA_FIXTURE_VERSION = "1.4.195";
8
9
  export declare const ORCA_CLI_COMMAND_ENV = "ORCA_CLI_COMMAND";
@@ -14,7 +15,6 @@ export declare const NOT_WRITABLE_CODE = "terminal_not_writable";
14
15
  /** The ONLY terminal status that licenses reading a terminal's bytes or its agent state. */
15
16
  export declare const RUNNING_STATUS = "running";
16
17
  export declare const STATUS_GOVERNED_METHODS: readonly ["read", "waitOutput", "status", "waitAgentStatus"];
17
- export declare const WORKTREE_ADOPTION_TIMEOUT_MS = 60000;
18
18
  /** A missing slot gets the same bounded chance to appear as a reaped shell gets to settle. */
19
19
  export declare const PENDING_PROJECT_GRACE_MS = 2000;
20
20
  export interface OrcaExec {
@@ -81,11 +81,62 @@ export declare function terminalWorktree(term: Record<string, unknown>): string
81
81
  * it keeps its resolved spelling: deterministic, and still comparable to another spelling of itself.
82
82
  */
83
83
  export declare function canonicalWorktreePath(path: string): string;
84
+ /** The proof line a worker terminal prints first: recovery and focus read it back (FX-N01). */
85
+ export declare const CHECKOUT_MARK = "TICKMARKR_CHECKOUT";
86
+ /** The exact bytes the create command prints as its first line. */
87
+ export declare function checkoutProofLine(checkout: string): string;
88
+ /** Every complete frame in a scrollback, decoded and canonicalized; whether an incomplete one was seen. */
89
+ export declare function checkoutFrames(text: string): {
90
+ complete: string[];
91
+ incomplete: boolean;
92
+ };
93
+ /** Does a scrollback prove exactly `checkout`: at least one complete frame equals it, no complete
94
+ * frame names anything else, and no frame is incomplete. Full-path equality after canonicalization —
95
+ * never a prefix, a substring, or a whitespace-terminated fragment. */
96
+ export declare function provesCheckout(text: string, checkout: string): boolean;
97
+ /**
98
+ * Everything a terminal on the tracked worktree runs before the payload: enter the checkout (a
99
+ * failed cd stops the whole line — nothing of the payload ever runs in the enclosing path), print
100
+ * the proof line, then hand the WHOLE payload to one `sh -c` so a background list, a `;` list or a
101
+ * subshell inside it all start in the checkout and its exit status is the payload's (FX-N02).
102
+ */
103
+ export declare function checkoutPrefix(checkout: string): string;
104
+ /** The command a terminal on the tracked worktree runs so that it executes INSIDE the checkout. */
105
+ export declare function inCheckout(checkout: string, cmd: string): string;
106
+ /** Every checkout the complete proof frames in a scrollback name, in order of appearance. */
107
+ export declare function checkoutsNamed(text: string): string[];
84
108
  /** Conservative agent-state mapping over orca's ACTUAL surfaces: `blocked` only when the show
85
109
  * record reports agentWait:true, `idle` only when the `terminal wait --for tui-idle` condition is
86
110
  * satisfied. The recorded 1.4.186 show response carries NO agent field at all — an absent signal
87
111
  * is "unknown", never a fabricated definite status. */
88
112
  export declare function mapAgentState(term: Record<string, unknown>, tuiIdle: boolean): string;
113
+ /**
114
+ * The Orca board owner record is the board's ONE lifecycle, and it lives on disk: every step below is
115
+ * decided from the file (plus Orca's own terminal table), so a fresh OrcaDriver — a restarted daemon —
116
+ * reaches the same answer as the instance that placed the board. No instance map or set carries it.
117
+ *
118
+ * reserved pane "", no claim narrator, create-only, before the split can read its token
119
+ * claimed pid + armId, pane "" observer (observeNamedRun), written exactly once
120
+ * bound claim + the receipt's handle and the split envelope's runtimeId
121
+ * retired bound + retired:true tombstone, CAS on the bound bytes; never answered again
122
+ *
123
+ * A retired record is replaced by a new reservation (CAS on the tombstone bytes) only once its pane
124
+ * is proven gone. Everything else — a reservation or claim with no bound pane, a failed cleanup, a
125
+ * record another driver holds with a live observer — refuses and keeps the record exactly as it is.
126
+ */
127
+ type BoardRecord = WatchBoardOwner & {
128
+ retired?: true;
129
+ runtimeId?: string;
130
+ };
131
+ /**
132
+ * Whole-record compare-and-swap. The canonical path stays readable until commit: a mkdir lock
133
+ * excludes other writers, then create-only `link`s the new inode (fails if anything exists) or
134
+ * `rename`s the new file over the live path (POSIX atomic replace — readers see old or new, never
135
+ * absence). A crash that leaves a `.tmp` or `.lock` does not drop the previous record.
136
+ * ponytail: observeNamedRun (supervision.ts) renames without CAS. It cannot interleave with a swap
137
+ * because the narrator writes nothing between reserve and claim, and no transition here swaps one.
138
+ */
139
+ export declare function casBoard(family: string, path: string, expected: string | undefined, next: BoardRecord): string;
89
140
  /**
90
141
  * The renderer hard-wraps long lines, paints margin chrome, and a cursor page boundary splits a
91
142
  * marker exactly like a wrap does. `parseWorkerResult` (src/adapters/prompt.ts) already de-wraps
@@ -105,6 +156,7 @@ export interface OrcaDriverOpts {
105
156
  pollMs?: number;
106
157
  /** Bounded, seam-adjustable staleness window for runtime probes before mutations. */
107
158
  probeStalenessMs?: number;
159
+ launchingHandle?: string;
108
160
  }
109
161
  export declare class OrcaDriver implements ExecutorDriver {
110
162
  id: string;
@@ -121,7 +173,12 @@ export declare class OrcaDriver implements ExecutorDriver {
121
173
  private narrate?;
122
174
  private hookCoverage?;
123
175
  private taskWorktrees;
176
+ private trackedByCheckout;
124
177
  private pendingProjects;
178
+ private env;
179
+ private launchingHandle?;
180
+ private serialQueue;
181
+ private serial;
125
182
  constructor(opts?: OrcaDriverOpts);
126
183
  private call;
127
184
  /** The live runtime's identity, or an explicit failure. A missing or unreachable runtime is a
@@ -144,14 +201,13 @@ export declare class OrcaDriver implements ExecutorDriver {
144
201
  private sendReceipt;
145
202
  private create;
146
203
  /**
147
- * A freshly-created git checkout does not become a valid Orca selector atomically. Ask
148
- * `worktree current` FROM that checkout until Orca itself resolves the exact filesystem identity;
149
- * an enclosing checkout is still not adoption. Only selector_not_found is a retryable refusal —
150
- * malformed envelopes and every other refusal remain explicit driver failures.
204
+ * OBS-1004: the tracked worktree that encloses a checkout, asked ONCE of `worktree current` from
205
+ * inside that checkout. Orca answers the exact path when it tracks the checkout itself, the
206
+ * enclosing tracked clone for a git worktree the daemon added beneath it (1.4.200, verified from
207
+ * `.tickmarkr/worktrees.noindex/<task>`), and selector_not_found when nothing it tracks encloses
208
+ * the cwd — which is a driver failure, not something to wait out: Orca has no adopt verb.
151
209
  */
152
- private awaitWorktreeAdoption;
153
- /** Same repo/run/narration path Herdr uses for its driver-owned dispatch-retry row. */
154
- private appendAdoptionWait;
210
+ private trackedWorktree;
155
211
  /**
156
212
  * Every terminal-addressed call — read AND write — goes through here, and the runtime identity is
157
213
  * established BEFORE the runtime-scoped handle goes on the wire. Discarding a lookalike's answer
@@ -161,6 +217,19 @@ export declare class OrcaDriver implements ExecutorDriver {
161
217
  * the handle exactly once, then re-issues the operation against the replacement.
162
218
  */
163
219
  private terminalOp;
220
+ /**
221
+ * FX-N01/N05/N06: under a shared enclosing worktree every task terminal lists with the same
222
+ * worktreePath, so the tracked path + owned title cannot tell two nested checkouts apart. The
223
+ * runtime's own proof is the terminal's earliest scrollback, where the create command printed a
224
+ * framed `TICKMARKR_CHECKOUT` line before its payload (checkoutProofLine). READ-only calls: the
225
+ * anchor (for `oldestCursor`), then pages from the oldest cursor until a frame is complete or the
226
+ * bound is hit. Every page is evidence only when the response's own identity is the candidate's:
227
+ * the terminal record must name `handle` and `_meta.runtimeId` must be `runtimeId` — the runtime
228
+ * that supplied the ownership listing — else another terminal's or another runtime's bytes were
229
+ * answered and nothing is proven. Proven means provesCheckout: exact canonical full-path equality
230
+ * of a complete frame, no other checkout named, no incomplete frame.
231
+ */
232
+ private checkoutProven;
164
233
  private recover;
165
234
  /** Validated READ terminal record, or an explicit unavailable failure. Called BEFORE any caller
166
235
  * looks at tail bytes — on every page, on every read-governed method. Read records are the one
@@ -200,7 +269,24 @@ export declare class OrcaDriver implements ExecutorDriver {
200
269
  waitAgentStatus(slot: Slot, status: string, timeoutMs: number): Promise<boolean>;
201
270
  sendKey(slot: Slot, key: string): Promise<void>;
202
271
  nudge(slot: Slot, message: string): Promise<boolean>;
203
- narrator(_cwd: string, _command: string, runId?: string): Promise<Slot>;
272
+ narrator(cwd: string, command: string, runId?: string): Promise<Slot>;
273
+ private boardPath;
274
+ /** A recorded pane (a receipt's handle bound to the split envelope's runtime, never a guess) is
275
+ * gone when that runtime no longer lists it, or when a handle-bound close receipt names it.
276
+ * A handle listed by a different runtime is a different pane — not closed, treated as gone. */
277
+ private closeRecordedPane;
278
+ /** bound → retired, decided from the record alone: it must be this driver's board for exactly this
279
+ * slot's pane. Already retired is returned as it is. */
280
+ private retireBoard;
281
+ /** bound → retired first: whatever fails below, no later call answers this board again. A live
282
+ * observer is asked to stop and its acknowledgement awaited on injected time before the
283
+ * handle-bound close (timeout keeps the tombstone and the pane); a dead one never acknowledges,
284
+ * so it is only asked. */
285
+ private retireAndClose;
286
+ /** WB-1 seam: the daemon reports this board lost. "Lost" can be a stale beat or missing presence
287
+ * under a still-live owner pid, so it is not proof of a dead observer — retirement keeps close's
288
+ * acknowledgement discipline (Leg-2 T9 P1). */
289
+ retireLostWatch(slot: Slot): Promise<void>;
204
290
  focus(target: FocusTarget): Promise<FocusResult>;
205
291
  project(taskId: string, state: "in-progress" | "in-review" | "completed"): Promise<void>;
206
292
  private setWorkspaceStatus;
@@ -243,5 +329,8 @@ export declare class OrcaDriver implements ExecutorDriver {
243
329
  reconcile(desired: Set<string>, runId: string, opts?: {
244
330
  spareLiveLlm?: boolean;
245
331
  }): Promise<void>;
332
+ private isRecordedWatchHandle;
333
+ private isRecordedWorkerHandle;
246
334
  worktree(repo: string, branch: string, baseRef: string): Promise<string>;
247
335
  }
336
+ export {};