jackherd 1.13.0 → 1.14.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.
@@ -37,6 +37,7 @@ const EVENT_REFERENCES: Readonly<Record<string, Reference>> = {
37
37
  [WATCH_EVENT.taskParallel]: REFERENCE.events,
38
38
  [WATCH_EVENT.noTaskSignal]: REFERENCE.events,
39
39
  [WATCH_EVENT.unlinkedArtifact]: REFERENCE.events,
40
+ [WATCH_EVENT.planMissing]: REFERENCE.events,
40
41
  [UPDATE_AVAILABLE_EVENT]: REFERENCE.events,
41
42
  [WATCH_EVENT.laneModel]: REFERENCE.laneSession,
42
43
  }
@@ -28,6 +28,8 @@ export type WatchTexts = {
28
28
  parallelDuty: (phases: string) => string
29
29
  /** What a lane that works without a task signal asks of the coordinator. */
30
30
  noTaskSignalDuty: string
31
+ /** What a lane whose plan the base ref lacks asks of the coordinator. */
32
+ planMissingDuty: string
31
33
  /** What a lane's call waiting for approval asks of the coordinator, with the mark (request id and call hash) the question must carry. */
32
34
  approvalDuty: (id: string, hash: string) => string
33
35
  /** The standing duty while the main checkout is behind its base branch on the remote (`origin/main`), with the count of commits. */
@@ -70,6 +72,8 @@ export const watchTexts = (dialogLanguage: PlanLanguage): WatchTexts => ({
70
72
  parallelDuty: (phases) =>
71
73
  `${phases} can run in parallel: propose it to the user, check content and release files yourself (the table covers paths and markers only)`,
72
74
  noTaskSignalDuty: 'remind the lane to report its task with jh task start; do not stop it',
75
+ planMissingDuty:
76
+ "the herd shows the base ref's plan, not this one: run § 2 step 0 for it, copying from the lane's worktree",
73
77
  laneModelDuty:
74
78
  'switch it while idle: /exit, then claude --resume <session> --model <id> in the same pane (§ 2 step 6)',
75
79
  mainCheckoutBehind: (remoteBase, count) =>
@@ -16,6 +16,7 @@ import { isLaneModels, LANE_MODEL_EVENT, type LaneModelEvent, type LaneModels }
16
16
  import { isLaneParallels, LANE_PARALLEL_EVENT, type LaneParallelEvent, type LaneParallels } from './lane-parallel'
17
17
  import { isStallState, LANE_STALLED_EVENT, type LaneStalledEvent, type StallState } from './lane-stalled'
18
18
  import { type LaneSnapshot, type SeenSeqs, seenSeqsOf } from './lane-watch'
19
+ import { isPlanMissingState, PLAN_MISSING_EVENT, type PlanMissingEvent, type PlanMissingState } from './plan-missing'
19
20
  import { emptyPreviewState, isPreviewState, PREVIEW_EVENT, type PreviewEvent, type PreviewState } from './previews'
20
21
  import { PULL_REQUEST_STATE, type WatchedPullRequest } from './pull-requests'
21
22
  import { emptySourceState, isSourceState, SOURCE_EVENT, type SourceEvent, type SourceState } from './sources'
@@ -60,6 +61,7 @@ export const WATCH_EVENT = {
60
61
  noTaskSignal: NO_TASK_SIGNAL_EVENT,
61
62
  laneStalled: LANE_STALLED_EVENT,
62
63
  unlinkedArtifact: UNLINKED_ARTIFACT_EVENT,
64
+ planMissing: PLAN_MISSING_EVENT,
63
65
  approval: APPROVAL_EVENT,
64
66
  source: SOURCE_EVENT,
65
67
  due: APPOINTMENT_EVENT.due,
@@ -122,6 +124,8 @@ export type WatchState = {
122
124
  laneStalls: StallState
123
125
  /** Per open artifact send no plan or board links: in its grace or already reported as `unlinked artifact`. */
124
126
  unlinkedArtifacts: UnlinkedState
127
+ /** Per working lane whose board row links a plan the base ref lacks: the plan last reported as `plan missing on base`. */
128
+ planMissing: PlanMissingState
125
129
  /** The ids of the approval requests on disk when they were last read, each reported once while it is open. */
126
130
  approvals: string[]
127
131
  /** Per configured source of `watch.sources`: the entries seen (`[id, fingerprint]`) and the start of a failure streak. */
@@ -185,6 +189,7 @@ export type WatchEvent =
185
189
  | NoTaskSignalEvent
186
190
  | LaneStalledEvent
187
191
  | UnlinkedArtifactEvent
192
+ | PlanMissingEvent
188
193
  | ApprovalEvent
189
194
  | SourceEvent
190
195
  | AppointmentEvent
@@ -224,6 +229,7 @@ export const emptyWatchState = (): WatchState => ({
224
229
  noTaskSignals: {},
225
230
  laneStalls: {},
226
231
  unlinkedArtifacts: {},
232
+ planMissing: {},
227
233
  approvals: [],
228
234
  sources: emptySourceState(),
229
235
  reportMtimes: {},
@@ -277,6 +283,7 @@ const isWatchState = (value: Record<string, unknown>): value is WatchState =>
277
283
  isNoSignalState(value.noTaskSignals) &&
278
284
  isStallState(value.laneStalls) &&
279
285
  isUnlinkedState(value.unlinkedArtifacts) &&
286
+ isPlanMissingState(value.planMissing) &&
280
287
  Array.isArray(value.approvals) &&
281
288
  value.approvals.every((id) => typeof id === 'string') &&
282
289
  isSourceState(value.sources) &&