tickmarkr 1.37.0 → 1.38.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.
Files changed (58) hide show
  1. package/README.md +1 -6
  2. package/dist/adapters/claude-code.js +4 -4
  3. package/dist/adapters/codex.js +1 -1
  4. package/dist/adapters/fake.d.ts +2 -2
  5. package/dist/adapters/fake.js +8 -8
  6. package/dist/adapters/grok.js +5 -5
  7. package/dist/adapters/model-lints.d.ts +4 -4
  8. package/dist/adapters/model-lints.js +1 -1
  9. package/dist/adapters/prompt.js +8 -13
  10. package/dist/adapters/registry.d.ts +8 -8
  11. package/dist/adapters/registry.js +5 -5
  12. package/dist/adapters/types.d.ts +3 -3
  13. package/dist/brand.d.ts +2 -0
  14. package/dist/brand.js +17 -0
  15. package/dist/cli/commands/approve.js +3 -3
  16. package/dist/cli/commands/compile.js +1 -1
  17. package/dist/cli/commands/doctor.js +3 -3
  18. package/dist/cli/commands/init.js +14 -9
  19. package/dist/cli/commands/profile.js +6 -6
  20. package/dist/cli/commands/status.js +2 -1
  21. package/dist/cli/commands/unlock.js +1 -1
  22. package/dist/cli/index.d.ts +1 -2
  23. package/dist/cli/index.js +5 -15
  24. package/dist/compile/gsd.js +1 -1
  25. package/dist/compile/index.js +9 -3
  26. package/dist/compile/native.d.ts +2 -0
  27. package/dist/compile/native.js +4 -2
  28. package/dist/config/config.d.ts +6 -6
  29. package/dist/config/config.js +8 -12
  30. package/dist/drivers/herdr.js +5 -5
  31. package/dist/drivers/index.d.ts +2 -2
  32. package/dist/drivers/subprocess.js +3 -3
  33. package/dist/drivers/types.js +4 -6
  34. package/dist/gates/acceptance.js +1 -1
  35. package/dist/gates/baseline.d.ts +2 -2
  36. package/dist/gates/llm.js +12 -9
  37. package/dist/gates/review.d.ts +2 -2
  38. package/dist/gates/review.js +1 -1
  39. package/dist/gates/run-gates.d.ts +2 -2
  40. package/dist/graph/graph.d.ts +2 -2
  41. package/dist/graph/graph.js +4 -16
  42. package/dist/plan/prompt.js +1 -1
  43. package/dist/plan/scope.d.ts +2 -2
  44. package/dist/plan/scope.js +5 -5
  45. package/dist/route/preference.d.ts +4 -4
  46. package/dist/route/router.d.ts +3 -3
  47. package/dist/run/consult.d.ts +2 -2
  48. package/dist/run/consult.js +5 -5
  49. package/dist/run/daemon.js +13 -13
  50. package/dist/run/git.d.ts +1 -1
  51. package/dist/run/git.js +5 -9
  52. package/dist/run/journal.d.ts +2 -2
  53. package/dist/run/journal.js +4 -4
  54. package/dist/run/lock.js +11 -11
  55. package/dist/run/merge.d.ts +2 -2
  56. package/dist/run/merge.js +3 -3
  57. package/dist/run/reconcile.js +1 -1
  58. package/package.json +2 -2
@@ -5,7 +5,7 @@ import { extractJson, gatePaneName } from "../gates/llm.js";
5
5
  import { sh } from "./git.js";
6
6
  const ACTIONS = ["retry", "reroute", "decompose", "human"];
7
7
  export function buildDossierPrompt(d) {
8
- return `DROVR-CONSULT
8
+ return `TICKMARKR-CONSULT
9
9
  You are a senior engineering consult for the tickmarkr orchestrator. A worker task hit trouble.
10
10
  Read the dossier and return a verdict. Be decisive; cost is the lowest priority, quality the highest.
11
11
 
@@ -37,7 +37,7 @@ Respond with ONLY this JSON:
37
37
  }
38
38
  let consultSeq = 0;
39
39
  export async function consult(d, cfg, adapters, driver, cwd, runDir,
40
- // T2 ownership contract: runId names the consult pane canonically (drovr:consult:<task>:0:<runId>)
40
+ // T2 ownership contract: runId names the consult pane canonically (tickmarkr:consult:<task>:0:<runId>)
41
41
  // via SlotOpts.owned; without it the legacy gatePaneName shape survives (non-daemon callers/tests).
42
42
  opts = {}) {
43
43
  const n = ++consultSeq;
@@ -58,10 +58,10 @@ opts = {}) {
58
58
  ...(opts.runId ? { owned: { role: "consult", taskId: d.taskId, attempt: 0, runId: opts.runId } } : {}),
59
59
  });
60
60
  opts.onSlot?.(slot);
61
- await driver.run(slot, `${adapter.headlessCommand(promptFile, cfg.consult.model)}; printf '\\nDROVR_''EXIT:%s\\n' $?`);
62
- // digit-suffixed marker only: a dossier quoting drovr's own "DROVR_EXIT:" literal must not
61
+ await driver.run(slot, `${adapter.headlessCommand(promptFile, cfg.consult.model)}; printf '\\nTICKMARKR_''EXIT:%s\\n' $?`);
62
+ // digit-suffixed marker only: a dossier quoting tickmarkr's own "TICKMARKR_EXIT:" literal must not
63
63
  // false-complete the consult pane before its verdict (same guard as daemon.ts:193 / llm.ts).
64
- await driver.waitOutput(slot, "DROVR_EXIT:\\d", cfg.consult.stallMinutes * 60_000, { regex: true });
64
+ await driver.waitOutput(slot, "TICKMARKR_EXIT:\\d", cfg.consult.stallMinutes * 60_000, { regex: true });
65
65
  out = await driver.read(slot, 300);
66
66
  if (!opts.keep)
67
67
  await driver.close(slot);
@@ -53,7 +53,7 @@ export async function runDaemon(repoRoot, opts = {}) {
53
53
  const recordedBranch = typeof branchEvent?.data.branch === "string" ? branchEvent.data.branch : undefined;
54
54
  const branch = recordedBranch
55
55
  ? branchEvent.event === "merge" ? recordedBranch.slice(0, recordedBranch.lastIndexOf("--")) : recordedBranch
56
- : opts.resume ? `drovr/${runId}` : integrationBranch(cfg, runId);
56
+ : integrationBranch(cfg, runId);
57
57
  if (lock.reclaimed)
58
58
  journal.append("lock-reclaimed", undefined, lock.reclaimed); // HARD-02 audit trail
59
59
  // GATE-08 (v1.12): the humanGate guard consults this run's journaled approvals, not just the compiled
@@ -127,14 +127,14 @@ export async function runDaemon(repoRoot, opts = {}) {
127
127
  };
128
128
  await reconcile(); // run start/resume boundary: nothing in flight — full sweep, incl. older runs' leftovers
129
129
  // v1.4 self-reference guard: a random nonce on the worker trailer AND exit marker. Displayed
130
- // source/diffs (e.g. a worker editing drovr's own prompt.ts/daemon.ts) can't know it, so an echoed
131
- // DROVR_RESULT/DROVR_EXIT literal can never premature-harvest the worker. Quote-split keeps the
130
+ // source/diffs (e.g. a worker editing tickmarkr's own prompt.ts/daemon.ts) can't know it, so an echoed
131
+ // TICKMARKR_RESULT/TICKMARKR_EXIT literal can never premature-harvest the worker. Quote-split keeps the
132
132
  // echoed command line itself from matching the marker it prints.
133
133
  //
134
134
  // v1.13 (VIS-09 safety, 43-02): the nonce is per-ATTEMPT, declared at the top of the attempt loop
135
135
  // below — NOT here at run scope. A run-scoped nonce is a latent hazard: HerdrDriver.read() is
136
136
  // `pane read --lines 1000` over scrollback and SubprocessDriver never clears s.buf, so any transcript
137
- // retention across attempts would let attempt N harvest attempt N-1's DROVR_RESULT out of scrollback
137
+ // retention across attempts would let attempt N harvest attempt N-1's TICKMARKR_RESULT out of scrollback
138
138
  // as its OWN completion — silently LYING about a worker's outcome. Pinned by the stale-trailer oracle
139
139
  // in tests/run/daemon.test.ts ("a retained prior-attempt trailer cannot complete a retry"); a future
140
140
  // hoist back to run scope reddens it.
@@ -294,8 +294,8 @@ export async function runDaemon(repoRoot, opts = {}) {
294
294
  // v1.13 (VIS-09 safety): one FRESH nonce per attempt — see the run-scope comment above. A retained
295
295
  // prior-attempt trailer (herdr scrollback / subprocess buffer) must never satisfy this attempt.
296
296
  const nonce = randomBytes(4).toString("hex");
297
- const exitMarkerCmd = `printf '\\nDROVR_''EXIT_${nonce}:%s\\n' $?`;
298
- const exitRe = new RegExp(`DROVR_EXIT_${nonce}:(\\d+)`);
297
+ const exitMarkerCmd = `printf '\\nTICKMARKR_''EXIT_${nonce}:%s\\n' $?`;
298
+ const exitRe = new RegExp(`TICKMARKR_EXIT_${nonce}:(\\d+)`);
299
299
  if (attempt >= MAX_ATTEMPTS) {
300
300
  await park(t, `attempt cap (${MAX_ATTEMPTS}) reached`, "attempt-cap", assignment, attempt, startMs, gateFails, consults, tokens, metered, retryMode);
301
301
  return;
@@ -408,9 +408,9 @@ export async function runDaemon(repoRoot, opts = {}) {
408
408
  while (Date.now() < deadline) {
409
409
  const sliceStart = Date.now();
410
410
  const slice = Math.min(BLOCKED_POLL_MS, deadline - sliceStart);
411
- if (await driver.waitOutput(slot, `(${trailerPattern(nonce)})|DROVR_EXIT_${nonce}:\\d`, slice, { regex: true })) {
412
- // verify before accepting: a worker that merely DISPLAYS a marker (e.g. editing drovr's
413
- // own source, where "DROVR_EXIT:" is a string literal) must not end the wait. Only a
411
+ if (await driver.waitOutput(slot, `(${trailerPattern(nonce)})|TICKMARKR_EXIT_${nonce}:\\d`, slice, { regex: true })) {
412
+ // verify before accepting: a worker that merely DISPLAYS a marker (e.g. editing tickmarkr's
413
+ // own source, where "TICKMARKR_EXIT:" is a string literal) must not end the wait. Only a
414
414
  // parseable trailer or a digit-suffixed exit marker in the harvest is completion.
415
415
  output = await driver.read(slot, 1000); // TUI transcripts carry chrome — read deeper than print's 500
416
416
  finished = new RegExp(trailerPattern(nonce)).test(output);
@@ -429,7 +429,7 @@ export async function runDaemon(repoRoot, opts = {}) {
429
429
  const st = paged ? "" : await driver.status(slot);
430
430
  if (!paged && (st === "blocked" || st === "idle")) {
431
431
  // T5: once-per-slot auto-answer when the adapter declares a trust dialog and the pane
432
- // text matches. drovr created the worktree from the operator's own repo — safe by construction.
432
+ // text matches. tickmarkr created the worktree from the operator's own repo — safe by construction.
433
433
  if (!trustAnswered && adapter.trustDialog && driver.sendKey) {
434
434
  try {
435
435
  const paneText = await driver.read(slot, 80);
@@ -473,7 +473,7 @@ export async function runDaemon(repoRoot, opts = {}) {
473
473
  else {
474
474
  const inv = adapter.invoke(t, wt, assignment, { promptFile });
475
475
  await driver.run(slot, `${inv.command}; ${exitMarkerCmd}`);
476
- finished = await driver.waitOutput(slot, `DROVR_EXIT_${nonce}:\\d`, cfg.taskTimeoutMinutes * 60_000, { regex: true });
476
+ finished = await driver.waitOutput(slot, `TICKMARKR_EXIT_${nonce}:\\d`, cfg.taskTimeoutMinutes * 60_000, { regex: true });
477
477
  output = await driver.read(slot, 500);
478
478
  exitCode = Number(exitRe.exec(output)?.[1] ?? 1);
479
479
  }
@@ -548,7 +548,7 @@ export async function runDaemon(repoRoot, opts = {}) {
548
548
  return;
549
549
  const g = e.result;
550
550
  // GATE-09 (ROADMAP SC-4): journal every judge retry as an attributable event — which gate flaked,
551
- // which channel flaked, which channel retried — so `drovr journal`/report can distinguish "judge
551
+ // which channel flaked, which channel retried — so `tickmarkr journal`/report can distinguish "judge
552
552
  // flaked, retried" from "worker failed" (run-20260711-185020 P43-03 L70-72 billed a judge flake as
553
553
  // a worker attempt; 47-01 fixed WHO retries, this closes the audit-trail half). The condition is
554
554
  // META-ONLY (D-03): gate === "acceptance" + typeof-shape guards on meta.judgeRetry — never a
@@ -583,7 +583,7 @@ export async function runDaemon(repoRoot, opts = {}) {
583
583
  // D-07: judge/review panes self-clean when their verdict is read (keepLlm) — only "forever" keeps them.
584
584
  keep: keepLlm,
585
585
  onSlot: keepLlm ? (s) => keptSlots.push(s) : undefined,
586
- // T2 ownership contract: canonical names (drovr:<role>:<task>:0:<runId>) so reconcile
586
+ // T2 ownership contract: canonical names (tickmarkr:<role>:<task>:0:<runId>) so reconcile
587
587
  // owns judge/review panes; run-gates' -r1 retry suffix becomes attempt 1 in llm.ts.
588
588
  // Same-name reuse across worker attempts is safe: panes self-clean when read (keepLlm),
589
589
  // and herdr's DEFECT-01 reclaim covers a kept holdover under keepPanes:forever.
package/dist/run/git.d.ts CHANGED
@@ -14,6 +14,6 @@ export declare function cleanupRunWorktrees(repo: string, branch: string, opts:
14
14
  removeIntegration: boolean;
15
15
  removeTaskIds: string[];
16
16
  }): Promise<void>;
17
- export declare function resolveIntegrationBranch(repo: string, branch: string): Promise<string>;
17
+ export declare function resolveIntegrationBranch(_repo: string, branch: string): Promise<string>;
18
18
  export declare function createWorktree(repo: string, branch: string, baseRef: string): Promise<string>;
19
19
  export declare function removeWorktree(repo: string, dir: string): Promise<void>;
package/dist/run/git.js CHANGED
@@ -2,7 +2,7 @@ import { spawn } from "node:child_process";
2
2
  import { existsSync, symlinkSync } from "node:fs";
3
3
  import { join } from "node:path";
4
4
  import { shq } from "../adapters/types.js";
5
- import { drovrDir } from "../graph/graph.js";
5
+ import { tickmarkrDir } from "../graph/graph.js";
6
6
  // stdin "ignore": same class as HARD-05 / SubprocessDriver — never leave an open pipe a child can block on
7
7
  // (pi -p / codex exec wait for stdin EOF). timedOut distinguishes SIGKILL-timeout from a real nonzero exit.
8
8
  export function sh(cmd, cwd, timeoutMs = 600000) {
@@ -49,7 +49,7 @@ export async function gitHead(cwd) {
49
49
  }
50
50
  export const sanitizeBranch = (branch) => branch.replace(/[^\w.-]+/g, "-");
51
51
  const sanitize = sanitizeBranch;
52
- export const worktreePath = (repo, branch) => join(drovrDir(repo), "worktrees", sanitize(branch));
52
+ export const worktreePath = (repo, branch) => join(tickmarkrDir(repo), "worktrees", sanitize(branch));
53
53
  /** OBS-28: remove this run's recorded worktrees; tolerates already-gone paths. */
54
54
  export async function cleanupRunWorktrees(repo, branch, opts) {
55
55
  if (opts.removeIntegration)
@@ -57,12 +57,8 @@ export async function cleanupRunWorktrees(repo, branch, opts) {
57
57
  for (const id of opts.removeTaskIds)
58
58
  await removeWorktree(repo, worktreePath(repo, `${branch}--${id}`));
59
59
  }
60
- const branchExists = async (repo, branch) => (await sh(`git rev-parse --verify ${shq(`refs/heads/${branch}`)}`, repo)).code === 0;
61
- export async function resolveIntegrationBranch(repo, branch) {
62
- if (!branch.startsWith("drovr/"))
63
- return branch;
64
- const legacy = `drover/${branch.slice("drovr/".length)}`;
65
- return !(await branchExists(repo, branch)) && await branchExists(repo, legacy) ? legacy : branch;
60
+ export async function resolveIntegrationBranch(_repo, branch) {
61
+ return branch;
66
62
  }
67
63
  const resolveTaskBranch = async (repo, branch) => {
68
64
  const split = branch.lastIndexOf("--");
@@ -73,7 +69,7 @@ const resolveTaskBranch = async (repo, branch) => {
73
69
  };
74
70
  export async function createWorktree(repo, branch, baseRef) {
75
71
  branch = await resolveTaskBranch(repo, branch);
76
- const dir = join(drovrDir(repo), "worktrees", sanitize(branch));
72
+ const dir = join(tickmarkrDir(repo), "worktrees", sanitize(branch));
77
73
  if (existsSync(dir))
78
74
  await removeWorktree(repo, dir);
79
75
  await shOk(`git worktree add -B ${shq(branch)} ${shq(dir)} ${shq(baseRef)}`, repo);
@@ -1,6 +1,6 @@
1
1
  import { z } from "zod";
2
2
  import { type Assignment } from "../adapters/types.js";
3
- import type { DrovrConfig } from "../config/config.js";
3
+ import type { TickmarkrConfig } from "../config/config.js";
4
4
  import { type TaskStatus } from "../graph/schema.js";
5
5
  import { type RoutingProfile } from "../route/profile.js";
6
6
  export interface JournalEvent {
@@ -70,7 +70,7 @@ export declare function readAllTelemetry(repoRoot: string, lastK: number, opts?:
70
70
  })[];
71
71
  export declare const RUNS_WINDOW = 50;
72
72
  export declare function readProfileCursor(repoRoot: string): string | undefined;
73
- export declare function loadRoutingProfile(repoRoot: string, cfg: DrovrConfig, opts?: {
73
+ export declare function loadRoutingProfile(repoRoot: string, cfg: TickmarkrConfig, opts?: {
74
74
  preview?: boolean;
75
75
  }): RoutingProfile | undefined;
76
76
  export declare class Journal {
@@ -123,8 +123,8 @@ export function readAllTelemetry(repoRoot, lastK, opts = {}) {
123
123
  }
124
124
  // ponytail: fixed 50-run window; promote to a routing.learned.* config knob only if operators need to tune it.
125
125
  export const RUNS_WINDOW = 50;
126
- // VIS-03 reset cursor — one trimmed runId line at .drovr/profile-since; absent/empty ⇒ undefined.
127
- // Opaque: used ONLY in the runId > comparison above, never a shell or path join beyond .drovr/.
126
+ // VIS-03 reset cursor — one trimmed runId line at .tickmarkr/profile-since; absent/empty ⇒ undefined.
127
+ // Opaque: used ONLY in the runId > comparison above, never a shell or path join beyond .tickmarkr/.
128
128
  export function readProfileCursor(repoRoot) {
129
129
  const path = join(repoRoot, stateDirName(repoRoot), "profile-since");
130
130
  if (!existsSync(path))
@@ -132,7 +132,7 @@ export function readProfileCursor(repoRoot) {
132
132
  return readFileSync(path, "utf8").trim() || undefined;
133
133
  }
134
134
  // The one shared profile builder (criterion 4: plan and daemon share ONE code path).
135
- // preview:true bypasses the routing.learned:off short-circuit so `drovr plan` can render the
135
+ // preview:true bypasses the routing.learned:off short-circuit so `tickmarkr plan` can render the
136
136
  // trust-ramp preview while the daemon (no preview) stays inert (VALIDATION 13-01-11).
137
137
  export function loadRoutingProfile(repoRoot, cfg, opts = {}) {
138
138
  if (cfg.routing.learned === "off" && !opts.preview)
@@ -218,7 +218,7 @@ export class Journal {
218
218
  // lastAssignment} from EXISTING events only (task-dispatch + consult-verdict + optional v1.24
219
219
  // task-approved{release:attempt-cap}). Additive-only: no new required event, no schema change, the
220
220
  // status replay above is byte-untouched (corpus criterion 3 is git-diff-provable). Motivated by the
221
- // 2026-07-11 incident (run-20260711-185020, P43-03): `drovr resume` re-dispatched at attempt 0 on
221
+ // 2026-07-11 incident (run-20260711-185020, P43-03): `tickmarkr resume` re-dispatched at attempt 0 on
222
222
  // pi:zai/glm-5.2, the exact channel a frontier consult had just banned, because execTask's
223
223
  // attempt/tried/assignment state is loop-local and dies with the process while the journal held every fact needed.
224
224
  //
package/dist/run/lock.js CHANGED
@@ -1,14 +1,14 @@
1
1
  import { linkSync, readFileSync, statSync, unlinkSync, utimesSync, writeFileSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { z } from "zod";
4
- import { drovrDir, stateDirName } from "../graph/graph.js";
5
- // HARD-01/02: coarse per-run advisory lock over .drovr/graph.json. LOCK-02: the lock is created by
4
+ import { tickmarkrDir, stateDirName } from "../graph/graph.js";
5
+ // HARD-01/02: coarse per-run advisory lock over .tickmarkr/graph.json. LOCK-02: the lock is created by
6
6
  // the link(2) idiom — write the full payload to graph.lock.<pid>.tmp, then linkSync(tmp, lockPath),
7
7
  // which is atomic and throws EEXIST if the lock already exists (the mutual-exclusion primitive).
8
8
  // rename() was REJECTED: it silently clobbers an existing destination, destroying that mutual
9
9
  // exclusion (two daemons would both "acquire"). LOCK-02 (OBS-05): a PROVABLY-dead holder (ESRCH)
10
10
  // self-clears immediately — expiry is NOT required. ESRCH is proof-positive death; the PID-reuse
11
- // hazard runs the OTHER way (a reused pid reads ALIVE and refuses until `drovr unlock`). mtime stays
11
+ // hazard runs the OTHER way (a reused pid reads ALIVE and refuses until `tickmarkr unlock`). mtime stays
12
12
  // the heartbeat — it feeds the reclaim race guard (ino+mtime re-stat) and the reclaimed audit value.
13
13
  // Zero new deps — node:fs stdlib.
14
14
  export const HEARTBEAT_MS = 10_000;
@@ -16,7 +16,7 @@ export const STALE_MS = 60_000; // 6× heartbeat headroom; PITFALLS floor is ≥
16
16
  // T-10-01: the payload is a trust boundary — parse with zod, fail closed on garbage. Only
17
17
  // pid/runId/startedAt are ever read; anything else is ignored (info-disclosure surface).
18
18
  const PayloadSchema = z.object({ pid: z.number().int().positive(), runId: z.string(), startedAt: z.number() });
19
- const lockPath = (repoRoot) => join(drovrDir(repoRoot), "graph.lock");
19
+ const lockPath = (repoRoot) => join(tickmarkrDir(repoRoot), "graph.lock");
20
20
  let heartbeat;
21
21
  let heldPath;
22
22
  // Best-effort release if the daemon exits without hitting its finally (crash mid-body). NO
@@ -33,12 +33,12 @@ process.once("exit", () => { if (heldPath)
33
33
  // `expired` for the race guard + reclaim audit); the heartbeat mechanism itself is untouched.
34
34
  // ponytail: a pid REUSED by an unrelated live process reads ALIVE (kill(pid,0) succeeds) and now
35
35
  // refuses indefinitely instead of expiring out in ≤60s — that is the fail-closed direction; the
36
- // escape hatch is `drovr unlock`. Acceptable for a single-machine tool (ESRCH is the only
36
+ // escape hatch is `tickmarkr unlock`. Acceptable for a single-machine tool (ESRCH is the only
37
37
  // proof-positive death; narrowing further needs pid-start-time correlation, out of scope here).
38
38
  // LOCK-01: garbage ⇒ always refuse (mtime irrelevant). It short-circuits before dead can matter —
39
39
  // so inspect()'s `dead = pid === undefined` fallback for the garbage row stays harmless. Safe post-16-01:
40
- // the atomic link(2) write means drovr can no longer mint garbage itself; a garbage payload can only
41
- // come from external corruption, which is exactly what must refuse. Only `drovr unlock` removes it
40
+ // the atomic link(2) write means tickmarkr can no longer mint garbage itself; a garbage payload can only
41
+ // come from external corruption, which is exactly what must refuse. Only `tickmarkr unlock` removes it
42
42
  // — a self-heal reclaim would silently overwrite whatever corrupted the file.
43
43
  export function shouldRefuse(i) {
44
44
  return i.garbage || !i.dead;
@@ -49,7 +49,7 @@ function inspect(p) {
49
49
  const mtimeMs = st.mtimeMs;
50
50
  const expired = Date.now() - mtimeMs > STALE_MS;
51
51
  const parsed = PayloadSchema.safeParse(readPayload(p));
52
- const garbage = !parsed.success; // LOCK-01: its own state — shouldRefuse refuses it unconditionally; only `drovr unlock` removes it
52
+ const garbage = !parsed.success; // LOCK-01: its own state — shouldRefuse refuses it unconditionally; only `tickmarkr unlock` removes it
53
53
  const pid = parsed.success ? parsed.data.pid : undefined;
54
54
  let dead = pid === undefined; // harmless fallback for the garbage row — garbage short-circuits shouldRefuse before this is read
55
55
  if (pid !== undefined) {
@@ -81,7 +81,7 @@ function unlinkIfOurs(p) {
81
81
  }
82
82
  export function acquireRunLock(repoRoot, runId) {
83
83
  const p = lockPath(repoRoot);
84
- const tmp = join(drovrDir(repoRoot), `graph.lock.${process.pid}.tmp`);
84
+ const tmp = join(tickmarkrDir(repoRoot), `graph.lock.${process.pid}.tmp`);
85
85
  try {
86
86
  try {
87
87
  unlinkSync(tmp);
@@ -127,10 +127,10 @@ export function acquireRunLock(repoRoot, runId) {
127
127
  }
128
128
  const stateDir = stateDirName(repoRoot);
129
129
  if (garbage)
130
- throw new Error(`${stateDir}/graph.lock holds an unreadable/garbage payload — refusing to reclaim it; run \`drovr unlock\` to remove it`);
130
+ throw new Error(`${stateDir}/graph.lock holds an unreadable/garbage payload — refusing to reclaim it; run \`tickmarkr unlock\` to remove it`);
131
131
  // LOCK-02: shouldRefuse is false whenever dead, so this throw is reached only for a LIVE holder
132
132
  // (incl. EPERM = alive-but-not-ours). The dead-but-fresh case self-clears via the reclaim branch.
133
- throw new Error(`${stateDir}/graph.lock held by pid ${pid ?? "?"}${heldRun ? ` (run ${heldRun})` : ""} — another drovr run? (operator escape: \`drovr unlock\`)`);
133
+ throw new Error(`${stateDir}/graph.lock held by pid ${pid ?? "?"}${heldRun ? ` (run ${heldRun})` : ""} — another tickmarkr run? (operator escape: \`tickmarkr unlock\`)`);
134
134
  }
135
135
  }
136
136
  export function releaseRunLock(repoRoot) {
@@ -1,4 +1,4 @@
1
- import type { DrovrConfig } from "../config/config.js";
1
+ import type { TickmarkrConfig } from "../config/config.js";
2
2
  export interface TipVerifyResult {
3
3
  gate: string;
4
4
  cmd: string;
@@ -7,7 +7,7 @@ export interface TipVerifyResult {
7
7
  fingerprints: string[];
8
8
  details: string;
9
9
  }
10
- export declare function integrationBranch(cfg: DrovrConfig, runId: string): string;
10
+ export declare function integrationBranch(cfg: TickmarkrConfig, runId: string): string;
11
11
  export declare function ensureIntegration(repo: string, branch: string, baseRef: string): Promise<string>;
12
12
  export declare function integrationHead(intWt: string): Promise<string>;
13
13
  export declare function mergeTask(intWt: string, taskBranch: string, message: string, gatedCommit: string): Promise<{
package/dist/run/merge.js CHANGED
@@ -2,15 +2,15 @@ import { existsSync } from "node:fs";
2
2
  import { join } from "node:path";
3
3
  import { shq } from "../adapters/types.js";
4
4
  import { fingerprint } from "../gates/baseline.js";
5
- import { drovrDir } from "../graph/graph.js";
5
+ import { tickmarkrDir } from "../graph/graph.js";
6
6
  import { gitHead, resolveIntegrationBranch, sh, shOk } from "./git.js";
7
7
  export function integrationBranch(cfg, runId) {
8
- return `tickmarkr/${runId}`;
8
+ return `${cfg.integrationBranchPrefix}${runId}`;
9
9
  }
10
10
  const sanitize = (branch) => branch.replace(/[^\w.-]+/g, "-");
11
11
  export async function ensureIntegration(repo, branch, baseRef) {
12
12
  branch = await resolveIntegrationBranch(repo, branch);
13
- const dir = join(drovrDir(repo), "worktrees", sanitize(branch));
13
+ const dir = join(tickmarkrDir(repo), "worktrees", sanitize(branch));
14
14
  if (existsSync(join(dir, ".git")))
15
15
  return dir; // resume: keep existing tip
16
16
  const exists = (await sh(`git rev-parse --verify refs/heads/${shq(branch)}`, repo)).code === 0;
@@ -1,6 +1,6 @@
1
1
  import { formatOwnedName, isForeignName, panesToClose, parseOwnedName } from "../drivers/types.js";
2
2
  export { isForeignName, panesToClose, parseOwnedName };
3
- // T1: pure fold over journal rows — the exact set of drovr-owned pane/tab names that SHOULD exist
3
+ // T1: pure fold over journal rows — the exact set of tickmarkr-owned pane/tab names that SHOULD exist
4
4
  // right now for a live run. No I/O: the caller supplies rows (Journal.read()) and a live pane listing
5
5
  // separately. Anything owned (parseOwnedName succeeds) but not in this set is garbage to close;
6
6
  // anything foreign (parseOwnedName fails) is never a candidate, no matter what state it's in.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tickmarkr",
3
- "version": "1.37.0",
3
+ "version": "1.38.0",
4
4
  "description": "Spec in, verified work out.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -22,7 +22,7 @@
22
22
  "test": "vitest run",
23
23
  "test:coverage": "vitest run --coverage",
24
24
  "schema": "tsx scripts/emit-schema.ts",
25
- "e2e": "DROVR_E2E=1 vitest run tests/e2e --testTimeout 900000"
25
+ "e2e": "TICKMARKR_E2E=1 vitest run tests/e2e --testTimeout 900000"
26
26
  },
27
27
  "dependencies": {
28
28
  "picomatch": "^4.0.2",