@clipboard-health/groundcrew 4.18.2 → 4.20.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 (144) hide show
  1. package/README.md +22 -22
  2. package/crew.config.example.ts +5 -5
  3. package/dist/cli.d.ts.map +1 -1
  4. package/dist/cli.js +39 -33
  5. package/dist/commands/cleaner.d.ts +4 -4
  6. package/dist/commands/cleaner.d.ts.map +1 -1
  7. package/dist/commands/cleaner.js +7 -7
  8. package/dist/commands/cleanupWorkspace.d.ts +1 -1
  9. package/dist/commands/cleanupWorkspace.d.ts.map +1 -1
  10. package/dist/commands/cleanupWorkspace.js +14 -14
  11. package/dist/commands/dispatcher.d.ts +4 -4
  12. package/dist/commands/dispatcher.d.ts.map +1 -1
  13. package/dist/commands/dispatcher.js +32 -32
  14. package/dist/commands/doctor.js +1 -1
  15. package/dist/commands/eligibility.d.ts +2 -2
  16. package/dist/commands/eligibility.d.ts.map +1 -1
  17. package/dist/commands/eligibility.js +4 -4
  18. package/dist/commands/init.js +1 -1
  19. package/dist/commands/interruptWorkspace.d.ts +1 -1
  20. package/dist/commands/interruptWorkspace.d.ts.map +1 -1
  21. package/dist/commands/interruptWorkspace.js +18 -18
  22. package/dist/commands/orchestrator.d.ts +1 -1
  23. package/dist/commands/orchestrator.js +2 -2
  24. package/dist/commands/resumeWorkspace.d.ts +1 -1
  25. package/dist/commands/resumeWorkspace.d.ts.map +1 -1
  26. package/dist/commands/resumeWorkspace.js +35 -35
  27. package/dist/commands/reviewer.d.ts +7 -7
  28. package/dist/commands/reviewer.d.ts.map +1 -1
  29. package/dist/commands/reviewer.js +27 -27
  30. package/dist/commands/setupWorkspace.d.ts +5 -5
  31. package/dist/commands/setupWorkspace.d.ts.map +1 -1
  32. package/dist/commands/setupWorkspace.js +39 -39
  33. package/dist/commands/source.d.ts +2 -0
  34. package/dist/commands/source.d.ts.map +1 -0
  35. package/dist/commands/source.js +126 -0
  36. package/dist/commands/status.d.ts +1 -1
  37. package/dist/commands/status.d.ts.map +1 -1
  38. package/dist/commands/status.js +103 -103
  39. package/dist/commands/teardownReporter.js +10 -10
  40. package/dist/index.d.ts +2 -2
  41. package/dist/index.d.ts.map +1 -1
  42. package/dist/index.js +2 -2
  43. package/dist/lib/adapterDefinition.d.ts +3 -3
  44. package/dist/lib/adapterDefinition.d.ts.map +1 -1
  45. package/dist/lib/adapters/linear/factory.d.ts +3 -3
  46. package/dist/lib/adapters/linear/factory.d.ts.map +1 -1
  47. package/dist/lib/adapters/linear/factory.js +6 -6
  48. package/dist/lib/adapters/linear/fetch.d.ts +18 -18
  49. package/dist/lib/adapters/linear/fetch.d.ts.map +1 -1
  50. package/dist/lib/adapters/linear/fetch.js +20 -20
  51. package/dist/lib/adapters/linear/index.js +2 -2
  52. package/dist/lib/adapters/linear/parsing.d.ts +1 -1
  53. package/dist/lib/adapters/linear/parsing.d.ts.map +1 -1
  54. package/dist/lib/adapters/linear/parsing.js +4 -4
  55. package/dist/lib/adapters/linear/statusNames.d.ts +1 -1
  56. package/dist/lib/adapters/linear/statusNames.d.ts.map +1 -1
  57. package/dist/lib/adapters/linear/writeback.d.ts +3 -3
  58. package/dist/lib/adapters/linear/writeback.d.ts.map +1 -1
  59. package/dist/lib/adapters/linear/writeback.js +1 -1
  60. package/dist/lib/adapters/shell/factory.d.ts +4 -4
  61. package/dist/lib/adapters/shell/factory.d.ts.map +1 -1
  62. package/dist/lib/adapters/shell/factory.js +40 -14
  63. package/dist/lib/adapters/shell/index.js +2 -2
  64. package/dist/lib/adapters/shell/invoke.d.ts +2 -2
  65. package/dist/lib/adapters/shell/invoke.js +3 -3
  66. package/dist/lib/adapters/shell/schema.d.ts +5 -1
  67. package/dist/lib/adapters/shell/schema.d.ts.map +1 -1
  68. package/dist/lib/adapters/shell/schema.js +24 -3
  69. package/dist/lib/adapters/todo-txt/index.d.ts +5 -0
  70. package/dist/lib/adapters/todo-txt/index.d.ts.map +1 -0
  71. package/dist/lib/adapters/todo-txt/index.js +8 -0
  72. package/dist/lib/adapters/todo-txt/normalizer.d.ts +22 -0
  73. package/dist/lib/adapters/todo-txt/normalizer.d.ts.map +1 -0
  74. package/dist/lib/adapters/todo-txt/normalizer.js +104 -0
  75. package/dist/lib/adapters/todo-txt/parser.d.ts +20 -0
  76. package/dist/lib/adapters/todo-txt/parser.d.ts.map +1 -0
  77. package/dist/lib/adapters/todo-txt/parser.js +93 -0
  78. package/dist/lib/adapters/todo-txt/schema.d.ts +12 -0
  79. package/dist/lib/adapters/todo-txt/schema.d.ts.map +1 -0
  80. package/dist/lib/adapters/todo-txt/schema.js +13 -0
  81. package/dist/lib/adapters/todo-txt/source.d.ts +5 -0
  82. package/dist/lib/adapters/todo-txt/source.d.ts.map +1 -0
  83. package/dist/lib/adapters/todo-txt/source.js +142 -0
  84. package/dist/lib/adapters/todo-txt/writeback.d.ts +18 -0
  85. package/dist/lib/adapters/todo-txt/writeback.d.ts.map +1 -0
  86. package/dist/lib/adapters/todo-txt/writeback.js +352 -0
  87. package/dist/lib/agentLaunch.js +1 -1
  88. package/dist/lib/board.d.ts +13 -13
  89. package/dist/lib/board.d.ts.map +1 -1
  90. package/dist/lib/board.js +5 -5
  91. package/dist/lib/buildSources.d.ts +8 -4
  92. package/dist/lib/buildSources.d.ts.map +1 -1
  93. package/dist/lib/buildSources.js +2 -2
  94. package/dist/lib/config.d.ts +8 -7
  95. package/dist/lib/config.d.ts.map +1 -1
  96. package/dist/lib/config.js +8 -8
  97. package/dist/lib/launchCommand.js +1 -1
  98. package/dist/lib/repositoryValidation.d.ts +2 -2
  99. package/dist/lib/repositoryValidation.d.ts.map +1 -1
  100. package/dist/lib/repositoryValidation.js +1 -1
  101. package/dist/lib/runState.d.ts +11 -11
  102. package/dist/lib/runState.d.ts.map +1 -1
  103. package/dist/lib/runState.js +19 -19
  104. package/dist/lib/runStateCleanup.js +2 -2
  105. package/dist/lib/sourceCapabilities.d.ts +17 -0
  106. package/dist/lib/sourceCapabilities.d.ts.map +1 -0
  107. package/dist/lib/sourceCapabilities.js +63 -0
  108. package/dist/lib/srtLaunch.d.ts +1 -1
  109. package/dist/lib/srtLaunch.d.ts.map +1 -1
  110. package/dist/lib/srtLaunch.js +1 -1
  111. package/dist/lib/srtPolicy.js +1 -1
  112. package/dist/lib/stagedLaunch.d.ts +3 -3
  113. package/dist/lib/stagedLaunch.d.ts.map +1 -1
  114. package/dist/lib/stagedLaunch.js +3 -3
  115. package/dist/lib/{ticketSource.d.ts → taskSource.d.ts} +30 -30
  116. package/dist/lib/taskSource.d.ts.map +1 -0
  117. package/dist/lib/{ticketSource.js → taskSource.js} +9 -9
  118. package/dist/lib/testing/canonicalFixtures.d.ts +1 -1
  119. package/dist/lib/testing/canonicalFixtures.d.ts.map +1 -1
  120. package/dist/lib/testing/canonicalFixtures.js +1 -1
  121. package/dist/lib/tmuxAdapter.d.ts +1 -1
  122. package/dist/lib/tmuxAdapter.js +2 -2
  123. package/dist/lib/util.d.ts +3 -3
  124. package/dist/lib/util.d.ts.map +1 -1
  125. package/dist/lib/util.js +4 -4
  126. package/dist/lib/workspaceAdapter.d.ts +8 -8
  127. package/dist/lib/workspaceAdapter.d.ts.map +1 -1
  128. package/dist/lib/workspaceAdapter.js +2 -2
  129. package/dist/lib/workspaces.d.ts +1 -1
  130. package/dist/lib/workspaces.js +1 -1
  131. package/dist/lib/worktrees.d.ts +11 -11
  132. package/dist/lib/worktrees.d.ts.map +1 -1
  133. package/dist/lib/worktrees.js +47 -47
  134. package/docs/adr/{0002-one-ticket-source-path-linear-is-an-adapter.md → 0002-one-task-source-path-linear-is-an-adapter.md} +3 -3
  135. package/docs/commands.md +10 -10
  136. package/docs/configuration.md +19 -19
  137. package/docs/credentials.md +2 -2
  138. package/docs/runners.md +1 -1
  139. package/docs/setup-hook-agent-prompt.md +2 -2
  140. package/docs/setup-hooks.md +1 -1
  141. package/docs/{ticket-sources.md → task-sources.md} +9 -9
  142. package/docs/troubleshooting.md +5 -5
  143. package/package.json +13 -13
  144. package/dist/lib/ticketSource.d.ts.map +0 -1
@@ -1,5 +1,5 @@
1
1
  /**
2
- * Per-iteration decider that picks Todo tickets to start and acts on the
2
+ * Per-iteration decider that picks Todo tasks to start and acts on the
3
3
  * picks. Stateless across iterations. The Board adapter owns its own writeback
4
4
  * caches (e.g., Linear's team-state cache lives in `src/lib/adapters/linear/writeback.ts`).
5
5
  *
@@ -7,7 +7,7 @@
7
7
  * for telemetry, writeback via Board, and side-effecting setupWorkspace calls.
8
8
  */
9
9
  import { dispatchableRepository } from "../lib/repositoryValidation.js";
10
- import { isGroundcrewIssue, naturalIdFromCanonical, } from "../lib/ticketSource.js";
10
+ import { isGroundcrewIssue, naturalIdFromCanonical, } from "../lib/taskSource.js";
11
11
  import { errorMessage, failMark, log, logEvent } from "../lib/util.js";
12
12
  import { workspaces } from "../lib/workspaces.js";
13
13
  import { classifyBlockers, classifyEligibility, classifyUsageExhaustion, } from "./eligibility.js";
@@ -17,7 +17,7 @@ function logSkip(verdict) {
17
17
  logEvent("dispatch", {
18
18
  outcome: "skipped",
19
19
  reason: verdict.eventReason,
20
- ticket: naturalIdFromCanonical(verdict.issue.id),
20
+ task: naturalIdFromCanonical(verdict.issue.id),
21
21
  blockers: verdict.blockers,
22
22
  model: verdict.model,
23
23
  });
@@ -34,18 +34,18 @@ export function createDispatcher(deps) {
34
34
  }
35
35
  async function startEligibleIssue(start, dryRun, signal) {
36
36
  const { issue, recovery } = start;
37
- const ticketId = naturalIdFromCanonical(issue.id);
37
+ const taskId = naturalIdFromCanonical(issue.id);
38
38
  if (start.resolvedFromAny) {
39
- log(`Resolved agent-any for ${ticketId} → ${issue.model}`);
39
+ log(`Resolved agent-any for ${taskId} → ${issue.model}`);
40
40
  }
41
41
  if (dryRun) {
42
42
  log(
43
43
  /* v8 ignore next @preserve -- classifyTodo forces recovery=false in dry-run, so the resume branch can't fire here */
44
- `[dry-run] Would ${recovery ? "resume" : "start"} ${ticketId} in ${issue.repository} (${issue.model})`);
44
+ `[dry-run] Would ${recovery ? "resume" : "start"} ${taskId} in ${issue.repository} (${issue.model})`);
45
45
  logEvent("dispatch", {
46
46
  outcome: "skipped",
47
47
  reason: "dry_run",
48
- ticket: ticketId,
48
+ task: taskId,
49
49
  model: issue.model,
50
50
  repository: issue.repository,
51
51
  });
@@ -53,12 +53,12 @@ export function createDispatcher(deps) {
53
53
  }
54
54
  try {
55
55
  if (recovery) {
56
- log(`Worktree and workspace already exist for ${ticketId}; resuming with markInProgress`);
56
+ log(`Worktree and workspace already exist for ${taskId}; resuming with markInProgress`);
57
57
  }
58
58
  else {
59
59
  const setupOptions = {
60
60
  repository: issue.repository,
61
- ticket: ticketId,
61
+ task: taskId,
62
62
  model: issue.model,
63
63
  details: {
64
64
  title: issue.title,
@@ -73,16 +73,16 @@ export function createDispatcher(deps) {
73
73
  await board.markInProgress(issue);
74
74
  logEvent("dispatch", {
75
75
  outcome: recovery ? "resumed" : "started",
76
- ticket: ticketId,
76
+ task: taskId,
77
77
  model: issue.model,
78
78
  repository: issue.repository,
79
79
  });
80
80
  }
81
81
  catch (error) {
82
- log(`${failMark()} Failed to start ${ticketId}: ${errorMessage(error)}`);
82
+ log(`${failMark()} Failed to start ${taskId}: ${errorMessage(error)}`);
83
83
  logEvent("dispatch", {
84
84
  outcome: "failed",
85
- ticket: ticketId,
85
+ task: taskId,
86
86
  model: issue.model,
87
87
  repository: issue.repository,
88
88
  error: errorMessage(error),
@@ -91,16 +91,16 @@ export function createDispatcher(deps) {
91
91
  }
92
92
  async function runOnce(arguments_) {
93
93
  const { state, worktreeEntries, usage, dryRun, signal, idleSuffix = "" } = arguments_;
94
- // Surface parent tickets that fetch silently dropped. Without this
95
- // an operator sees "No Todo tickets to pick up" with no signal that an
96
- // expected Todo+labelled ticket was skipped because it has sub-issues.
94
+ // Surface parent tasks that fetch silently dropped. Without this
95
+ // an operator sees "No Todo tasks to pick up" with no signal that an
96
+ // expected Todo+labelled task was skipped because it has sub-issues.
97
97
  for (const skip of state.parentSkips) {
98
- const ticket = naturalIdFromCanonical(skip.id);
99
- log(`Skipping ${ticket}: parent ticket with ${skip.childCount} sub-issue(s) — groundcrew works sub-issues, not parents`);
98
+ const task = naturalIdFromCanonical(skip.id);
99
+ log(`Skipping ${task}: parent task with ${skip.childCount} sub-issue(s) — groundcrew works sub-issues, not parents`);
100
100
  logEvent("dispatch", {
101
101
  outcome: "skipped",
102
102
  reason: "parent_with_children",
103
- ticket,
103
+ task,
104
104
  children: skip.childCount,
105
105
  });
106
106
  }
@@ -109,9 +109,9 @@ export function createDispatcher(deps) {
109
109
  .toSorted((a, b) => a.id.localeCompare(b.id));
110
110
  const activeCount = active.length;
111
111
  const slots = config.orchestrator.maximumInProgress - activeCount;
112
- // Narrow Todo to tickets that opted in via an `agent-*` label.
113
- // Unlabeled tickets are not groundcrew's concern even when in Todo.
114
- // Sort by priority so higher-priority tickets fill slots first.
112
+ // Narrow Todo to tasks that opted in via an `agent-*` label.
113
+ // Unlabeled tasks are not groundcrew's concern even when in Todo.
114
+ // Sort by priority so higher-priority tasks fill slots first.
115
115
  const todo = state.issues
116
116
  .filter((issue) => issue.status === "todo" && isGroundcrewIssue(issue))
117
117
  .toSorted((a, b) => prioritySortKey(a.priority) - prioritySortKey(b.priority));
@@ -120,7 +120,7 @@ export function createDispatcher(deps) {
120
120
  return;
121
121
  }
122
122
  if (todo.length === 0) {
123
- log(`No Todo tickets to pick up${idleSuffix}`);
123
+ log(`No Todo tasks to pick up${idleSuffix}`);
124
124
  return;
125
125
  }
126
126
  // Run the blocker pre-pass first so an all-blocked board short-circuits
@@ -130,21 +130,21 @@ export function createDispatcher(deps) {
130
130
  logSkip(skip);
131
131
  }
132
132
  if (unblocked.length === 0) {
133
- log(`No eligible Todo tickets after blocker filtering${idleSuffix}`);
133
+ log(`No eligible Todo tasks after blocker filtering${idleSuffix}`);
134
134
  return;
135
135
  }
136
136
  // Validate repositories BEFORE the expensive probes so a tick whose only
137
137
  // candidates have unknown repos short-circuits without paying for the
138
138
  // usage() HTTP call or the workspaces.probe shell-out. Doing this filter
139
- // here also keeps an unknown-repo ticket at the head of the queue from
139
+ // here also keeps an unknown-repo task at the head of the queue from
140
140
  // consuming a slot in classifyEligibility and starving later valid
141
- // tickets. Each unknown repo still emits a WARN via dispatchableRepository.
141
+ // tasks. Each unknown repo still emits a WARN via dispatchableRepository.
142
142
  const dispatchableUnblocked = unblocked.filter((issue) => {
143
143
  const repository = dispatchableRepository(issue, config.workspace.knownRepositories, log);
144
144
  return repository !== undefined;
145
145
  });
146
146
  if (dispatchableUnblocked.length === 0) {
147
- log(`No eligible Todo tickets after repository validation${idleSuffix}`);
147
+ log(`No eligible Todo tasks after repository validation${idleSuffix}`);
148
148
  return;
149
149
  }
150
150
  // usage() is an HTTP call; workspaces.probe shells tmux/cmux. Kick off
@@ -186,14 +186,14 @@ export function createDispatcher(deps) {
186
186
  logSkip(skip);
187
187
  }
188
188
  if (starts.length === 0) {
189
- log(`No eligible Todo tickets after eligibility filtering${idleSuffix}`);
189
+ log(`No eligible Todo tasks after eligibility filtering${idleSuffix}`);
190
190
  return;
191
191
  }
192
192
  const dispatchable = starts;
193
- log(`Slots ${activeCount}/${config.orchestrator.maximumInProgress} used${formatActiveSlotList(active)}, starting ${dispatchable.length} ticket(s): ${dispatchable.map(({ issue }) => `${naturalIdFromCanonical(issue.id)}(${issue.model})`).join(", ")}`);
193
+ log(`Slots ${activeCount}/${config.orchestrator.maximumInProgress} used${formatActiveSlotList(active)}, starting ${dispatchable.length} task(s): ${dispatchable.map(({ issue }) => `${naturalIdFromCanonical(issue.id)}(${issue.model})`).join(", ")}`);
194
194
  logEvent("dispatch", {
195
195
  outcome: "starting",
196
- tickets: dispatchable.map(({ issue }) => `${naturalIdFromCanonical(issue.id)}:${issue.model}`),
196
+ tasks: dispatchable.map(({ issue }) => `${naturalIdFromCanonical(issue.id)}:${issue.model}`),
197
197
  });
198
198
  for (const start of dispatchable) {
199
199
  // oxlint-disable-next-line no-await-in-loop -- one workspace at a time avoids racing on git
@@ -205,15 +205,15 @@ export function createDispatcher(deps) {
205
205
  function hasRecoverableCandidate(issues, worktreeEntries) {
206
206
  return issues.some((issue) => {
207
207
  const naturalId = naturalIdFromCanonical(issue.id);
208
- return worktreeEntries.some((entry) => entry.repository === issue.repository && entry.ticket === naturalId);
208
+ return worktreeEntries.some((entry) => entry.repository === issue.repository && entry.task === naturalId);
209
209
  });
210
210
  }
211
211
  function formatUsageExhaustion(exhaustion) {
212
212
  if (exhaustion.kind === "session") {
213
213
  const mins = exhaustion.resetMinutes ?? "?";
214
- return `${exhaustion.model} session at ${exhaustion.usedPercentage.toFixed(0)}% (> ${exhaustion.limitPercentage}%), resets in ${mins}m — skipping its tickets`;
214
+ return `${exhaustion.model} session at ${exhaustion.usedPercentage.toFixed(0)}% (> ${exhaustion.limitPercentage}%), resets in ${mins}m — skipping its tasks`;
215
215
  }
216
- return `${exhaustion.model} weekly at ${exhaustion.usedPercentage.toFixed(1)}% (> ${exhaustion.allowedPercentage.toFixed(1)}% paced budget), resets in ${exhaustion.resetMinutes}m — skipping its tickets`;
216
+ return `${exhaustion.model} weekly at ${exhaustion.usedPercentage.toFixed(1)}% (> ${exhaustion.allowedPercentage.toFixed(1)}% paced budget), resets in ${exhaustion.resetMinutes}m — skipping its tasks`;
217
217
  }
218
218
  /** Undefined priority sorts last. */
219
219
  function prioritySortKey(priority) {
@@ -34,7 +34,7 @@ async function checkCmd(cmd, required, hint) {
34
34
  return result;
35
35
  }
36
36
  /**
37
- * Source-agnostic reachability check: build every configured ticket source
37
+ * Source-agnostic reachability check: build every configured task source
38
38
  * and run the Board's `verify()` fan-out. Replaces the old Linear-only
39
39
  * "api key + reachability" probe so a misconfigured shell (or future Jira)
40
40
  * source surfaces here too. A missing Linear API key still fails verify with
@@ -1,13 +1,13 @@
1
1
  /**
2
2
  * Pure eligibility classifier — takes the per-iteration board snapshot plus
3
3
  * derived state (worktrees, live workspaces, usage, slot count) and returns
4
- * a verdict per Todo ticket. No logging, no Linear calls, no shell-outs.
4
+ * a verdict per Todo task. No logging, no Linear calls, no shell-outs.
5
5
  *
6
6
  * The Dispatcher consumes the verdict list to drive logging and side
7
7
  * effects.
8
8
  */
9
9
  import { type ResolvedConfig } from "../lib/config.ts";
10
- import { type GroundcrewIssue } from "../lib/ticketSource.ts";
10
+ import { type GroundcrewIssue } from "../lib/taskSource.ts";
11
11
  import type { UsageByModel } from "../lib/usage.ts";
12
12
  import type { WorkspaceProbe } from "../lib/workspaces.ts";
13
13
  import type { WorktreeEntry } from "../lib/worktrees.ts";
@@ -1 +1 @@
1
- {"version":3,"file":"eligibility.d.ts","sourceRoot":"","sources":["../../src/commands/eligibility.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAmB,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACxE,OAAO,EAAwC,KAAK,eAAe,EAAE,MAAM,wBAAwB,CAAC;AACpG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAOzD,KAAK,UAAU,GACX,SAAS,GACT,oBAAoB,GACpB,oBAAoB,GACpB,iBAAiB,GACjB,4BAA4B,GAC5B,mBAAmB,CAAC;AAExB,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,eAAe,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;IAClB,8EAA8E;IAC9E,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,eAAe,CAAC;IACvB,sBAAsB;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,WAAW,EAAE,UAAU,CAAC;IACxB,kDAAkD;IAClD,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,KAAK,OAAO,GAAG,YAAY,GAAG,WAAW,CAAC;AAE1C,MAAM,MAAM,oBAAoB,GAC5B;IACE,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,GACD;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEN,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,cAAc,CAAC;IACvB;;;;;OAKG;IACH,SAAS,EAAE,SAAS,eAAe,EAAE,CAAC;IACtC,eAAe,EAAE,SAAS,aAAa,EAAE,CAAC;IAC1C,cAAc,EAAE,cAAc,CAAC;IAC/B,KAAK,EAAE,YAAY,CAAC;IACpB,oDAAoD;IACpD,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACvB,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,UAAU,qBAAqB;IAC7B,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB;AAgCD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,YAAY,EACnB,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,GACrB,MAAM,GAAG,SAAS,CAepB;AAaD,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,YAAY,GAClB,oBAAoB,EAAE,CAmCxB;AA6CD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,eAAe,EAAE,GAAG,qBAAqB,CAYxF;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,iBAAiB,GAAG,OAAO,EAAE,CAgE5E"}
1
+ {"version":3,"file":"eligibility.d.ts","sourceRoot":"","sources":["../../src/commands/eligibility.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAmB,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AACxE,OAAO,EAAwC,KAAK,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAClG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAC3D,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAOzD,KAAK,UAAU,GACX,SAAS,GACT,oBAAoB,GACpB,oBAAoB,GACpB,iBAAiB,GACjB,4BAA4B,GAC5B,mBAAmB,CAAC;AAExB,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,eAAe,CAAC;IACvB,QAAQ,EAAE,OAAO,CAAC;IAClB,8EAA8E;IAC9E,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,eAAe,CAAC;IACvB,sBAAsB;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,4DAA4D;IAC5D,WAAW,EAAE,UAAU,CAAC;IACxB,kDAAkD;IAClD,QAAQ,CAAC,EAAE,MAAM,EAAE,CAAC;IACpB;;;;;OAKG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,KAAK,OAAO,GAAG,YAAY,GAAG,WAAW,CAAC;AAE1C,MAAM,MAAM,oBAAoB,GAC5B;IACE,IAAI,EAAE,SAAS,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;CAC7B,GACD;IACE,IAAI,EAAE,QAAQ,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;CACtB,CAAC;AAEN,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,cAAc,CAAC;IACvB;;;;;OAKG;IACH,SAAS,EAAE,SAAS,eAAe,EAAE,CAAC;IACtC,eAAe,EAAE,SAAS,aAAa,EAAE,CAAC;IAC1C,cAAc,EAAE,cAAc,CAAC;IAC/B,KAAK,EAAE,YAAY,CAAC;IACpB,oDAAoD;IACpD,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;IACvB,qDAAqD;IACrD,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,UAAU,qBAAqB;IAC7B,SAAS,EAAE,eAAe,EAAE,CAAC;IAC7B,KAAK,EAAE,WAAW,EAAE,CAAC;CACtB;AAgCD;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,YAAY,EACnB,SAAS,EAAE,GAAG,CAAC,MAAM,CAAC,GACrB,MAAM,GAAG,SAAS,CAepB;AAaD,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,YAAY,GAClB,oBAAoB,EAAE,CAmCxB;AA6CD;;;;;GAKG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,SAAS,eAAe,EAAE,GAAG,qBAAqB,CAYxF;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,UAAU,EAAE,iBAAiB,GAAG,OAAO,EAAE,CAgE5E"}
@@ -1,13 +1,13 @@
1
1
  /**
2
2
  * Pure eligibility classifier — takes the per-iteration board snapshot plus
3
3
  * derived state (worktrees, live workspaces, usage, slot count) and returns
4
- * a verdict per Todo ticket. No logging, no Linear calls, no shell-outs.
4
+ * a verdict per Todo task. No logging, no Linear calls, no shell-outs.
5
5
  *
6
6
  * The Dispatcher consumes the verdict list to drive logging and side
7
7
  * effects.
8
8
  */
9
9
  import { AGENT_ANY_MODEL } from "../lib/config.js";
10
- import { naturalIdFromCanonical } from "../lib/ticketSource.js";
10
+ import { naturalIdFromCanonical } from "../lib/taskSource.js";
11
11
  const PERCENT_FRACTION_DIVISOR = 100;
12
12
  const DAYS_PER_WEEK = 7;
13
13
  const MINUTES_PER_DAY = 24 * 60;
@@ -104,14 +104,14 @@ export function classifyUsageExhaustion(config, usage) {
104
104
  return exhausted;
105
105
  }
106
106
  // Stale worktrees with no matching live workspace are filtered out here so
107
- // they don't permanently block later tickets in the Todo queue.
107
+ // they don't permanently block later tasks in the Todo queue.
108
108
  function classifyRecovery(arguments_) {
109
109
  const { issue, worktreeEntries, workspaceProbe, dryRun } = arguments_;
110
110
  if (dryRun) {
111
111
  return { kind: "go", recovery: false };
112
112
  }
113
113
  const naturalId = naturalIdFromCanonical(issue.id);
114
- const exists = worktreeEntries.some((entry) => entry.repository === issue.repository && entry.ticket === naturalId);
114
+ const exists = worktreeEntries.some((entry) => entry.repository === issue.repository && entry.task === naturalId);
115
115
  if (!exists) {
116
116
  return { kind: "go", recovery: false };
117
117
  }
@@ -209,7 +209,7 @@ function writeInitGuidance(destination, options) {
209
209
  writeCloneGuidance(options);
210
210
  writeOutput(" - If using Linear, export your API key:");
211
211
  writeOutput(' export GROUNDCREW_LINEAR_API_KEY="lin_api_..."');
212
- writeOutput(" - In Linear, assign tickets to yourself and add an agent-* label to opt them in");
212
+ writeOutput(" - In Linear, assign tasks to yourself and add an agent-* label to opt them in");
213
213
  writeOutput(" - Validate and start:");
214
214
  writeOutput(" crew doctor");
215
215
  writeOutput(" crew run --watch");
@@ -1,6 +1,6 @@
1
1
  import { type ResolvedConfig } from "../lib/config.ts";
2
2
  export interface InterruptWorkspaceOptions {
3
- ticket: string;
3
+ task: string;
4
4
  reason?: string;
5
5
  }
6
6
  export declare function interruptWorkspace(config: ResolvedConfig, options: InterruptWorkspaceOptions): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"interruptWorkspace.d.ts","sourceRoot":"","sources":["../../src/commands/interruptWorkspace.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAMnE,MAAM,WAAW,yBAAyB;IACxC,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAqGD,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,IAAI,CAAC,CAyBf;AAED,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAGzE"}
1
+ {"version":3,"file":"interruptWorkspace.d.ts","sourceRoot":"","sources":["../../src/commands/interruptWorkspace.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAMnE,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAqGD,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,yBAAyB,GACjC,OAAO,CAAC,IAAI,CAAC,CAyBf;AAED,wBAAsB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAGzE"}
@@ -22,19 +22,19 @@ function parseArguments(argv) {
22
22
  continue;
23
23
  }
24
24
  if (argument.startsWith("-")) {
25
- throw new Error(`Unknown option: ${argument}\nUsage: crew stop <ticket> [--reason <text>]`);
25
+ throw new Error(`Unknown option: ${argument}\nUsage: crew stop <task> [--reason <text>]`);
26
26
  }
27
27
  positionals.push(argument);
28
28
  }
29
- const [ticket, ...extras] = positionals;
30
- if (ticket === undefined || ticket.length === 0 || extras.length > 0) {
31
- throw new Error("Usage: crew stop <ticket> [--reason <text>]");
29
+ const [task, ...extras] = positionals;
30
+ if (task === undefined || task.length === 0 || extras.length > 0) {
31
+ throw new Error("Usage: crew stop <task> [--reason <text>]");
32
32
  }
33
- return { ticket: ticket.toLowerCase(), ...(reason === undefined ? {} : { reason }) };
33
+ return { task: task.toLowerCase(), ...(reason === undefined ? {} : { reason }) };
34
34
  }
35
35
  function sourceFromState(state) {
36
36
  return {
37
- ticket: state.ticket,
37
+ task: state.task,
38
38
  repository: state.repository,
39
39
  model: state.model,
40
40
  worktreeDir: state.worktreeDir,
@@ -43,14 +43,14 @@ function sourceFromState(state) {
43
43
  resumeCount: state.resumeCount,
44
44
  };
45
45
  }
46
- function sourceFromWorktree(config, ticket, entry) {
46
+ function sourceFromWorktree(config, task, entry) {
47
47
  return {
48
- ticket,
48
+ task,
49
49
  repository: entry.repository,
50
50
  model: config.models.default,
51
51
  worktreeDir: entry.dir,
52
52
  branchName: entry.branchName,
53
- workspaceName: ticket,
53
+ workspaceName: task,
54
54
  resumeCount: 0,
55
55
  };
56
56
  }
@@ -59,9 +59,9 @@ function resolveInterruptSource(arguments_) {
59
59
  return sourceFromState(arguments_.state);
60
60
  }
61
61
  if (arguments_.entry !== undefined) {
62
- return sourceFromWorktree(arguments_.config, arguments_.ticket, arguments_.entry);
62
+ return sourceFromWorktree(arguments_.config, arguments_.task, arguments_.entry);
63
63
  }
64
- throw new Error(`No run state or worktree found for ${arguments_.ticket}; nothing to interrupt.`);
64
+ throw new Error(`No run state or worktree found for ${arguments_.task}; nothing to interrupt.`);
65
65
  }
66
66
  function interruptDetail(result) {
67
67
  if (result.kind === "missing") {
@@ -77,17 +77,17 @@ function failOnUnavailable(result) {
77
77
  throw new Error(`Could not interrupt workspace: ${detail}`);
78
78
  }
79
79
  export async function interruptWorkspace(config, options) {
80
- const ticket = options.ticket.toLowerCase();
81
- const state = readRunState(config, ticket);
82
- const [entry] = worktrees.findByTicket(config, ticket);
83
- const source = resolveInterruptSource({ config, ticket, state, entry });
80
+ const task = options.task.toLowerCase();
81
+ const state = readRunState(config, task);
82
+ const [entry] = worktrees.findByTask(config, task);
83
+ const source = resolveInterruptSource({ config, task, state, entry });
84
84
  const result = await workspaces.interrupt(config, source.workspaceName);
85
85
  failOnUnavailable(result);
86
86
  const detail = interruptDetail(result);
87
87
  recordRunState({
88
88
  config,
89
89
  state: {
90
- ticket,
90
+ task,
91
91
  repository: source.repository,
92
92
  model: source.model,
93
93
  worktreeDir: source.worktreeDir,
@@ -99,8 +99,8 @@ export async function interruptWorkspace(config, options) {
99
99
  ...(detail === undefined ? {} : { detail }),
100
100
  },
101
101
  });
102
- log(`Interrupted ${ticket}; worktree preserved at ${source.worktreeDir}`);
103
- log(`Next: crew status ${ticket}`);
102
+ log(`Interrupted ${task}; worktree preserved at ${source.worktreeDir}`);
103
+ log(`Next: crew status ${task}`);
104
104
  }
105
105
  export async function interruptWorkspaceCli(argv) {
106
106
  const config = await loadConfig();
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * groundcrew orchestrator — polls Linear projects and spins up workspace +
3
- * git-worktree pairs for ready tickets. Each tick fetches the board, runs
3
+ * git-worktree pairs for ready tasks. Each tick fetches the board, runs
4
4
  * the cleaner, the reviewer, and the dispatcher; logging from those modules is
5
5
  * the orchestrator's user-facing output.
6
6
  */
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * groundcrew orchestrator — polls Linear projects and spins up workspace +
3
- * git-worktree pairs for ready tickets. Each tick fetches the board, runs
3
+ * git-worktree pairs for ready tasks. Each tick fetches the board, runs
4
4
  * the cleaner, the reviewer, and the dispatcher; logging from those modules is
5
5
  * the orchestrator's user-facing output.
6
6
  */
@@ -8,7 +8,7 @@ import { createBoard } from "../lib/board.js";
8
8
  import { buildSources, sourcesFromConfig } from "../lib/buildSources.js";
9
9
  import { loadConfig } from "../lib/config.js";
10
10
  import { findPullRequestsForBranch } from "../lib/pullRequests.js";
11
- import { RepositoryResolutionError } from "../lib/ticketSource.js";
11
+ import { RepositoryResolutionError } from "../lib/taskSource.js";
12
12
  import { getUsageByModel } from "../lib/usage.js";
13
13
  import { errorMessage, log, sleep } from "../lib/util.js";
14
14
  import { worktrees } from "../lib/worktrees.js";
@@ -1,6 +1,6 @@
1
1
  import { type ResolvedConfig } from "../lib/config.ts";
2
2
  export interface ResumeWorkspaceOptions {
3
- ticket: string;
3
+ task: string;
4
4
  }
5
5
  export declare function resumeWorkspace(config: ResolvedConfig, options: ResumeWorkspaceOptions): Promise<void>;
6
6
  export declare function resumeWorkspaceCli(argv: string[]): Promise<void>;
@@ -1 +1 @@
1
- {"version":3,"file":"resumeWorkspace.d.ts","sourceRoot":"","sources":["../../src/commands/resumeWorkspace.ts"],"names":[],"mappings":"AAEA,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAenE,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,MAAM,CAAC;CAChB;AA6HD,wBAAsB,eAAe,CACnC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,IAAI,CAAC,CA0Ff;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAGtE"}
1
+ {"version":3,"file":"resumeWorkspace.d.ts","sourceRoot":"","sources":["../../src/commands/resumeWorkspace.ts"],"names":[],"mappings":"AAEA,OAAO,EAAc,KAAK,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAenE,MAAM,WAAW,sBAAsB;IACrC,IAAI,EAAE,MAAM,CAAC;CACd;AA6HD,wBAAsB,eAAe,CACnC,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,sBAAsB,GAC9B,OAAO,CAAC,IAAI,CAAC,CA0Ff;AAED,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAGtE"}
@@ -10,29 +10,29 @@ import { errorMessage, log } from "../lib/util.js";
10
10
  import { workspaces } from "../lib/workspaces.js";
11
11
  import { worktrees } from "../lib/worktrees.js";
12
12
  function parseArguments(argv) {
13
- const [ticket, ...extras] = argv;
14
- if (ticket === undefined || ticket.length === 0 || extras.length > 0 || ticket.startsWith("-")) {
15
- throw new Error("Usage: crew resume <ticket>");
13
+ const [task, ...extras] = argv;
14
+ if (task === undefined || task.length === 0 || extras.length > 0 || task.startsWith("-")) {
15
+ throw new Error("Usage: crew resume <task>");
16
16
  }
17
- return { ticket: ticket.toLowerCase() };
17
+ return { task: task.toLowerCase() };
18
18
  }
19
- async function fetchTicketDetails(ticket) {
19
+ async function fetchTaskDetails(task) {
20
20
  try {
21
- const issue = await getLinearClient().issue(ticket.toUpperCase());
21
+ const issue = await getLinearClient().issue(task.toUpperCase());
22
22
  return {
23
23
  title: issue.title,
24
24
  description: issue.description ?? "",
25
25
  };
26
26
  }
27
27
  catch (error) {
28
- log(`Resume Linear detail lookup failed for ${ticket}: ${errorMessage(error)}`);
28
+ log(`Resume Linear detail lookup failed for ${task}: ${errorMessage(error)}`);
29
29
  return undefined;
30
30
  }
31
31
  }
32
- async function contextFromLinear(config, ticket, worktree) {
33
- const resolved = await fetchResolvedIssue({ client: getLinearClient(), config, ticket });
32
+ async function contextFromLinear(config, task, worktree) {
33
+ const resolved = await fetchResolvedIssue({ client: getLinearClient(), config, task });
34
34
  return {
35
- ticket,
35
+ task,
36
36
  repository: resolved.repository,
37
37
  model: resolved.model,
38
38
  worktree,
@@ -41,38 +41,38 @@ async function contextFromLinear(config, ticket, worktree) {
41
41
  resumeCount: 0,
42
42
  };
43
43
  }
44
- async function contextFromState(ticket, state, worktree) {
45
- const details = await fetchTicketDetails(ticket);
44
+ async function contextFromState(task, state, worktree) {
45
+ const details = await fetchTaskDetails(task);
46
46
  return {
47
- ticket,
47
+ task,
48
48
  repository: state.repository,
49
49
  model: state.model,
50
50
  worktree,
51
- title: details?.title ?? ticket.toUpperCase(),
51
+ title: details?.title ?? task.toUpperCase(),
52
52
  description: details?.description ?? "",
53
53
  ...(state.reason === undefined ? {} : { reason: state.reason }),
54
54
  resumeCount: state.resumeCount,
55
55
  };
56
56
  }
57
- async function buildResumeContext(config, ticket) {
58
- const state = readRunState(config, ticket);
59
- const entries = worktrees.findByTicket(config, ticket);
57
+ async function buildResumeContext(config, task) {
58
+ const state = readRunState(config, task);
59
+ const entries = worktrees.findByTask(config, task);
60
60
  const worktree = state === undefined
61
61
  ? entries[0]
62
62
  : (entries.find((entry) => entry.repository === state.repository) ?? entries[0]);
63
63
  if (worktree === undefined) {
64
- throw new Error(`No worktree found for ${ticket}; cannot resume.`);
64
+ throw new Error(`No worktree found for ${task}; cannot resume.`);
65
65
  }
66
66
  if (state !== undefined) {
67
- return await contextFromState(ticket, state, worktree);
67
+ return await contextFromState(task, state, worktree);
68
68
  }
69
- return await contextFromLinear(config, ticket, worktree);
69
+ return await contextFromLinear(config, task, worktree);
70
70
  }
71
71
  function renderResumePrompt(context) {
72
72
  return [
73
- `You are resuming Groundcrew ticket ${context.ticket} (${context.title}) in an existing worktree.`,
73
+ `You are resuming Groundcrew task ${context.task} (${context.title}) in an existing worktree.`,
74
74
  "",
75
- "Ticket description:",
75
+ "Task description:",
76
76
  "",
77
77
  context.description,
78
78
  "",
@@ -89,20 +89,20 @@ function renderResumePrompt(context) {
89
89
  "Run the repository's documented verification before stopping, then leave the branch ready or open a PR when possible.",
90
90
  ].join("\n");
91
91
  }
92
- async function failIfWorkspaceAlreadyLive(config, ticket) {
92
+ async function failIfWorkspaceAlreadyLive(config, task) {
93
93
  const probe = await workspaces.probe(config);
94
94
  if (probe.kind === "unavailable") {
95
95
  const detail = probe.error === undefined ? "" : `: ${errorMessage(probe.error)}`;
96
- throw new Error(`Could not verify whether workspace for ${ticket} is already live${detail}. Retry or inspect the workspace backend manually before resuming.`);
96
+ throw new Error(`Could not verify whether workspace for ${task} is already live${detail}. Retry or inspect the workspace backend manually before resuming.`);
97
97
  }
98
- if (probe.names.has(ticket)) {
99
- throw new Error(`Workspace for ${ticket} is already live; attach to it instead of resuming.`);
98
+ if (probe.names.has(task)) {
99
+ throw new Error(`Workspace for ${task} is already live; attach to it instead of resuming.`);
100
100
  }
101
101
  }
102
102
  export async function resumeWorkspace(config, options) {
103
- const ticket = options.ticket.toLowerCase();
104
- await failIfWorkspaceAlreadyLive(config, ticket);
105
- const context = await buildResumeContext(config, ticket);
103
+ const task = options.task.toLowerCase();
104
+ await failIfWorkspaceAlreadyLive(config, task);
105
+ const context = await buildResumeContext(config, task);
106
106
  const definition = config.models.definitions[context.model];
107
107
  if (definition === undefined) {
108
108
  throw new Error(`Unknown model: ${context.model}`);
@@ -116,7 +116,7 @@ export async function resumeWorkspace(config, options) {
116
116
  await ensureReady();
117
117
  const stagedPrompt = stagePromptText({
118
118
  prefix: "groundcrew-resume",
119
- ticket,
119
+ task,
120
120
  text: renderResumePrompt(context),
121
121
  });
122
122
  const secretsFile = stageBuildSecrets(stagedPrompt.directory);
@@ -131,7 +131,7 @@ export async function resumeWorkspace(config, options) {
131
131
  const staged = buildAndStageSrtLaunch({
132
132
  config,
133
133
  repository: context.repository,
134
- ticket,
134
+ task,
135
135
  worktreeDir: context.worktree.dir,
136
136
  definition,
137
137
  });
@@ -156,7 +156,7 @@ export async function resumeWorkspace(config, options) {
156
156
  try {
157
157
  await openAgentWorkspace({
158
158
  config,
159
- name: ticket,
159
+ name: task,
160
160
  cwd: context.worktree.dir,
161
161
  command: launchCmd,
162
162
  model: context.model,
@@ -175,18 +175,18 @@ export async function resumeWorkspace(config, options) {
175
175
  recordRunState({
176
176
  config,
177
177
  state: {
178
- ticket,
178
+ task,
179
179
  repository: context.repository,
180
180
  model: context.model,
181
181
  worktreeDir: context.worktree.dir,
182
182
  branchName: context.worktree.branchName,
183
- workspaceName: ticket,
183
+ workspaceName: task,
184
184
  state: "resumed",
185
185
  resumeCount: context.resumeCount + 1,
186
186
  ...(context.reason === undefined ? {} : { reason: context.reason }),
187
187
  },
188
188
  });
189
- log(`Resumed ${ticket} in ${context.worktree.dir} (${context.model})`);
189
+ log(`Resumed ${task} in ${context.worktree.dir} (${context.model})`);
190
190
  }
191
191
  export async function resumeWorkspaceCli(argv) {
192
192
  const config = await loadConfig();
@@ -1,13 +1,13 @@
1
1
  /**
2
- * Per-iteration scanner that advances a ticket based on its worktree's pull
2
+ * Per-iteration scanner that advances a task based on its worktree's pull
3
3
  * request state. Sits between the cleaner and the dispatcher in each
4
4
  * `orchestrate()` tick.
5
5
  *
6
- * - An **open** PR on an **in-progress** ticket → `markInReview`: frees a
6
+ * - An **open** PR on an **in-progress** task → `markInReview`: frees a
7
7
  * dispatch slot (slot math counts only in-progress) while leaving the
8
8
  * worktree intact for review, since the cleaner only tears down `done`.
9
- * - A **merged** PR (on an in-progress or in-review ticket) → `markDone`:
10
- * the work has landed, so the ticket is terminal and the cleaner tears the
9
+ * - A **merged** PR (on an in-progress or in-review task) → `markDone`:
10
+ * the work has landed, so the task is terminal and the cleaner tears the
11
11
  * worktree down on a later tick. `merged` never routes to `in-review`.
12
12
  *
13
13
  * Sources that don't implement `markDone` (e.g. Linear) return `unsupported`;
@@ -15,14 +15,14 @@
15
15
  * fallback. (Linear's own GitHub integration moves merged issues to Done,
16
16
  * which groundcrew observes via `fetch()`.)
17
17
  *
18
- * The write-back lands in the ticket source, not the in-memory `BoardState`,
18
+ * The write-back lands in the task source, not the in-memory `BoardState`,
19
19
  * so the dispatcher in the SAME tick still sees prior state; the slot frees on
20
20
  * the NEXT tick's `board.fetch()`. That one-tick latency is deliberate. One
21
21
  * per `orchestrate()`; stateless across iterations. Mirrors `Cleaner`.
22
22
  */
23
23
  import type { Board } from "../lib/board.ts";
24
24
  import type { PullRequestSummary } from "../lib/pullRequests.ts";
25
- import { type BoardState } from "../lib/ticketSource.ts";
25
+ import { type BoardState } from "../lib/taskSource.ts";
26
26
  import type { WorktreeEntry } from "../lib/worktrees.ts";
27
27
  /**
28
28
  * Injected PR lookup. Matches `findPullRequestsForBranch`'s shape: best-effort,
@@ -47,7 +47,7 @@ interface ReviewArguments {
47
47
  signal?: AbortSignal;
48
48
  }
49
49
  export interface Reviewer {
50
- runOnce(arguments_: ReviewArguments): Promise<void>;
50
+ runOnce: (arguments_: ReviewArguments) => Promise<void>;
51
51
  }
52
52
  export declare function createReviewer(deps: ReviewerDeps): Reviewer;
53
53
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"reviewer.d.ts","sourceRoot":"","sources":["../../src/commands/reviewer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EACL,KAAK,UAAU,EAIhB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,UAAU,EAAE;IAC1C,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,KAAK,OAAO,CAAC,SAAS,kBAAkB,EAAE,CAAC,CAAC;AAE7C,UAAU,YAAY;IACpB,KAAK,EAAE,KAAK,CAAC;IACb,gBAAgB,EAAE,gBAAgB,CAAC;CACpC;AAED,sEAAsE;AACtE,UAAU,eAAe;IACvB,KAAK,EAAE,UAAU,CAAC;IAClB,eAAe,EAAE,SAAS,aAAa,EAAE,CAAC;IAC1C,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,CAAC,UAAU,EAAE,eAAe,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CACrD;AA+CD,wBAAgB,cAAc,CAAC,IAAI,EAAE,YAAY,GAAG,QAAQ,CAwH3D"}
1
+ {"version":3,"file":"reviewer.d.ts","sourceRoot":"","sources":["../../src/commands/reviewer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AAEH,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,EACL,KAAK,UAAU,EAIhB,MAAM,sBAAsB,CAAC;AAE9B,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG,CAAC,UAAU,EAAE;IAC1C,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB,KAAK,OAAO,CAAC,SAAS,kBAAkB,EAAE,CAAC,CAAC;AAE7C,UAAU,YAAY;IACpB,KAAK,EAAE,KAAK,CAAC;IACb,gBAAgB,EAAE,gBAAgB,CAAC;CACpC;AAED,sEAAsE;AACtE,UAAU,eAAe;IACvB,KAAK,EAAE,UAAU,CAAC;IAClB,eAAe,EAAE,SAAS,aAAa,EAAE,CAAC;IAC1C,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,CAAC,UAAU,EAAE,eAAe,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACzD;AA+CD,wBAAgB,cAAc,CAAC,IAAI,EAAE,YAAY,GAAG,QAAQ,CAwH3D"}