@knpkv/herdr-work 0.9.5 → 0.11.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.
package/dist/view.d.ts CHANGED
@@ -4,7 +4,7 @@ import { type WorkRequestDecisions } from "./request-decision.js";
4
4
  export type { WorkRequestAnswer, WorkRequestDecision, WorkRequestDecisions } from "./request-decision.js";
5
5
  /** The request clock's wording ("52s", "4m 12s", "11m", "expiring"), shared with hosts that show the same clocks. */
6
6
  export { workRequestClockText } from "./request-decision.js";
7
- export declare const WorkBoard: ({ decisions, externalLinks, initialGoalId, initialWindow, navigation, snapshots }: {
7
+ export declare const WorkBoard: ({ decisions, externalLinks, host, initialGoalId, initialWindow, navigation, snapshots }: {
8
8
  readonly snapshots: WorkSnapshots;
9
9
  /**
10
10
  * Lets the reader decide approval requests in place, with their clock. Omit it (the LAN view, a
@@ -12,6 +12,8 @@ export declare const WorkBoard: ({ decisions, externalLinks, initialGoalId, init
12
12
  */
13
13
  readonly decisions?: WorkRequestDecisions;
14
14
  readonly externalLinks?: "disabled" | "enabled";
15
+ /** The application supplies its own host; goal owners may be on other hosts. Omit when unknown. */
16
+ readonly host?: string;
15
17
  readonly initialGoalId?: string | null;
16
18
  readonly initialWindow?: WorkSnapshotWindow;
17
19
  readonly navigation?: (selection: {
@@ -1 +1 @@
1
- {"version":3,"file":"view.d.ts","sourceRoot":"","sources":["../src/view.tsx"],"names":[],"mappings":"AAWA,OAAO,EAA+B,KAAK,YAAY,EAAE,MAAM,OAAO,CAAA;AACtE,OAAO,KAAK,EAaV,aAAa,EACb,kBAAkB,EAEnB,MAAM,YAAY,CAAA;AAEnB,OAAO,EAAiD,KAAK,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;AAWhH,YAAY,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;AACzG,qHAAqH;AACrH,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;AAwnB5D,eAAO,MAAM,SAAS,sFAOnB;IACD,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAA;IACjC;;;OAGG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,oBAAoB,CAAA;IACzC,QAAQ,CAAC,aAAa,CAAC,EAAE,UAAU,GAAG,SAAS,CAAA;IAC/C,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACtC,QAAQ,CAAC,aAAa,CAAC,EAAE,kBAAkB,CAAA;IAC3C,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,kBAAkB,CAAA;KAAE,KAAK,MAAM,CAAA;CACrH,KAAG,YAqWH,CAAA"}
1
+ {"version":3,"file":"view.d.ts","sourceRoot":"","sources":["../src/view.tsx"],"names":[],"mappings":"AAUA,OAAO,EAA+B,KAAK,YAAY,EAAE,MAAM,OAAO,CAAA;AACtE,OAAO,KAAK,EAaV,aAAa,EACb,kBAAkB,EAEnB,MAAM,YAAY,CAAA;AAGnB,OAAO,EAAiD,KAAK,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;AAYhH,YAAY,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;AACzG,qHAAqH;AACrH,OAAO,EAAE,oBAAoB,EAAE,MAAM,uBAAuB,CAAA;AAy5B5D,eAAO,MAAM,SAAS,4FAQnB;IACD,QAAQ,CAAC,SAAS,EAAE,aAAa,CAAA;IACjC;;;OAGG;IACH,QAAQ,CAAC,SAAS,CAAC,EAAE,oBAAoB,CAAA;IACzC,QAAQ,CAAC,aAAa,CAAC,EAAE,UAAU,GAAG,SAAS,CAAA;IAC/C,mGAAmG;IACnG,QAAQ,CAAC,IAAI,CAAC,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACtC,QAAQ,CAAC,aAAa,CAAC,EAAE,kBAAkB,CAAA;IAC3C,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,SAAS,EAAE;QAAE,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,QAAQ,CAAC,MAAM,EAAE,kBAAkB,CAAA;KAAE,KAAK,MAAM,CAAA;CACrH,KAAG,YAwgBH,CAAA"}
package/dist/view.js CHANGED
@@ -1,15 +1,16 @@
1
1
  import { Fragment as _Fragment, jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Button, StateLabel, Text } from "@knpkv/rly/primitives";
3
- import { DecisionBar, Hero, Region, StageRail, TimelineRow } from "@knpkv/rly/patterns";
3
+ import { Icon } from "@knpkv/rly/foundations";
4
+ import { DecisionBar, Hero, HeroWord, Region } from "@knpkv/rly/patterns";
4
5
  import { useEffect, useRef, useState } from "react";
6
+ import { activityShownOf } from "./model.js";
5
7
  import { decodeWorkBoardNavigationGoal, encodeWorkBoardNavigationGoal } from "./navigation.js";
6
8
  import { workRequestClockText, workRequestDecidability } from "./request-decision.js";
7
9
  import { displayStateOf, observedFor } from "./display-state.js";
8
- import { workTriage, workTriageGroups, workTriageGroupTitle, workTriageSentence } from "./work-triage.js";
10
+ import { workGoalFinishedAt, workTriage, workTriageGroups, workTriageGroupTitle, workTriageSentence } from "./work-triage.js";
9
11
  /** The request clock's wording ("52s", "4m 12s", "11m", "expiring"), shared with hosts that show the same clocks. */
10
12
  export { workRequestClockText } from "./request-decision.js";
11
13
  const windows = ["now", "day", "week", "month"];
12
- const stageOrder = ["local", "review", "pull_request", "merged", "deployed"];
13
14
  const initialVisibleGoalCount = 10;
14
15
  const selectedGoalFragment = "work-selected-goal";
15
16
  const goalStates = [
@@ -66,17 +67,30 @@ const agentStatusLabel = {
66
67
  gone: "gone"
67
68
  };
68
69
  const deliveryLabel = {
69
- local: "Local",
70
- review: "Review",
70
+ local: "In progress",
71
+ review: "In review",
71
72
  pull_request: "Pull request",
72
73
  merged: "Merged",
73
74
  deployed: "Deployed"
74
75
  };
75
76
  const reviewPresentation = {
76
- not_requested: { label: "Not requested", tone: "neutral" },
77
- requested: { label: "Requested", tone: "caution" },
78
- changes_requested: { label: "Changes requested", tone: "critical" },
79
- approved: { label: "Approved", tone: "positive" }
77
+ not_requested: { label: "Not requested", evidence: "no review yet", tone: "neutral" },
78
+ requested: { label: "Requested", evidence: "review requested", tone: "caution" },
79
+ changes_requested: { label: "Changes requested", evidence: "changes requested", tone: "critical" },
80
+ approved: { label: "Approved", evidence: "approved", tone: "positive" }
81
+ };
82
+ const checksPresentation = {
83
+ none: { label: "no checks", tone: "neutral" },
84
+ pending: { label: "checks running", tone: "progress" },
85
+ passing: { label: "checks passing", tone: "positive" },
86
+ failing: { label: "checks failing", tone: "critical" }
87
+ };
88
+ const stateIcon = {
89
+ neutral: "minus",
90
+ positive: "check",
91
+ critical: "alert",
92
+ caution: "clock",
93
+ progress: "loader"
80
94
  };
81
95
  const requestPresentation = {
82
96
  open: { label: "Open", tone: "caution" },
@@ -88,6 +102,17 @@ const formatTimestamp = (timestamp) => new Intl.DateTimeFormat("en-GB", {
88
102
  dateStyle: "medium",
89
103
  timeStyle: "short"
90
104
  }).format(timestamp);
105
+ /** The board's timestamp formatter also supplies compact clocks. */
106
+ const formatClock = (timestamp) => formatTimestamp(timestamp).split(", ").at(-1) ?? formatTimestamp(timestamp);
107
+ const ageOf = (timestamp, asOf) => {
108
+ const minutes = Math.max(0, Math.floor((asOf - timestamp) / 60_000));
109
+ if (minutes < 60)
110
+ return `${minutes}m ago`;
111
+ if (minutes < 1_440)
112
+ return `${Math.floor(minutes / 60)}h ago`;
113
+ const days = Math.floor(minutes / 1_440);
114
+ return `${days} ${days === 1 ? "day" : "days"} ago`;
115
+ };
91
116
  const formatSpend = (goal) => goal.spend === null
92
117
  ? "Not recorded"
93
118
  : new Intl.NumberFormat("en", {
@@ -101,12 +126,6 @@ const blockersFor = (goal) => {
101
126
  };
102
127
  const activityFor = (goal) => goal.activity ?? [];
103
128
  const requestsFor = (goal) => goal.requests ?? [];
104
- const hierarchyLabel = (goal) => {
105
- const hierarchy = goal.agentHierarchy;
106
- if (hierarchy === undefined || hierarchy === null)
107
- return "No agent hierarchy recorded";
108
- return `${hierarchy.agent.host} / ${hierarchy.agent.name}`;
109
- };
110
129
  /** Renders the persisted approval deep link after its origin is bound at ingress. */
111
130
  const exactLink = (target, label) => (_jsx("a", { className: "work-exact-link", href: target.url, children: label }));
112
131
  /** Renders the persisted review destination without rewriting its credential-free URL. */
@@ -117,24 +136,37 @@ const reviewLabel = (review) => {
117
136
  }
118
137
  return (_jsx(StateLabel, { label: reviewPresentation[review.state].label, size: "compact", tone: reviewPresentation[review.state].tone }));
119
138
  };
120
- /** Delivery as words: past stages are done, the current one is "now" in the goal's state ink. */
121
- const stagesFor = (goal, shown) => {
122
- const current = stageOrder.indexOf(goal.delivery);
123
- return stageOrder.map((stage, index) => ({
124
- id: `${goal.id}-${stage}`,
125
- name: deliveryLabel[stage],
126
- state: index < current ? "done" : index === current ? "now" : "not yet",
127
- tone: index === current ? displayPresentation[shown].tone : "neutral"
128
- }));
139
+ /** PR delivery includes review; local work includes it only when recorded. Abandoned work stops at its last step. */
140
+ const GoalProgress = ({ goal, observed }) => {
141
+ const shown = observed?.displayState ?? goal.state;
142
+ const merged = observed?.pullRequest?.fact.state === "merged" || goal.delivery === "merged";
143
+ const hasPullRequest = merged || observed?.pullRequest != null || goal.delivery === "pull_request";
144
+ const hasReview = hasPullRequest ||
145
+ goal.delivery === "review" ||
146
+ shown === "review" ||
147
+ (goal.review != null && goal.review.state !== "not_requested");
148
+ const finished = !hasPullRequest && (shown === "completed" || shown === "deployed");
149
+ const names = hasReview
150
+ ? ["Planned", "In progress", "In review", hasPullRequest ? "Merged" : "Done"]
151
+ : ["Planned", "In progress", "Done"];
152
+ const finalStep = names.length - 1;
153
+ const current = merged || finished ? finalStep : hasReview ? 2 : shown === "planned" ? 0 : 1;
154
+ const abandoned = shown === "abandoned";
155
+ return (_jsxs("section", { "aria-labelledby": "work-delivery-title", className: "work-delivery", children: [_jsx("h3", { className: "work-group-title", id: "work-delivery-title", children: "Delivery" }), _jsx("ol", { "aria-label": `Delivery of ${goal.title}`, className: "work-row-progress", "data-steps": names.length, role: "list", children: names.map((name, index) => (_jsxs("li", { "aria-current": !abandoned && index === current ? "step" : undefined, "aria-label": abandoned && index === current ? `${name}, abandoned here` : undefined, "data-reached": index < current || (!abandoned && index === current), "data-stopped": abandoned && index === current, "data-tone": displayPresentation[shown].tone, children: [_jsx("span", { "aria-hidden": "true", className: "work-step-number", children: index + 1 }), _jsx("span", { className: "work-step-name", children: name }), abandoned && index === current ? (_jsx("span", { className: "work-step-status", children: "Abandoned" })) : !abandoned && index === current && current !== finalStep ? (_jsx(StateLabel, { icon: stateIcon[displayPresentation[shown].tone], label: "Now", size: "compact", tone: displayPresentation[shown].tone })) : (_jsx("span", { className: "work-step-status", children: index < current || (!abandoned && index === current) ? "Done" : "Not yet" }))] }, name))) }), hasPullRequest ? null : (_jsx(Text, { tone: "secondary", variant: "meta", children: "No pull request" }))] }));
156
+ };
157
+ /** CI and review come only from an observed PR. Its confirmation time says when those facts were last read. */
158
+ const GoalEvidence = ({ goal, observed }) => {
159
+ const pullRequest = observed?.pullRequest;
160
+ if (pullRequest == null && goal.delivery !== "pull_request" && goal.delivery !== "merged")
161
+ return null;
162
+ const ci = pullRequest == null
163
+ ? { label: "checks unknown", tone: "neutral" }
164
+ : checksPresentation[pullRequest.fact.checks];
165
+ const review = pullRequest == null
166
+ ? { evidence: "review unknown", tone: "neutral" }
167
+ : reviewPresentation[pullRequest.fact.review];
168
+ return (_jsxs("div", { className: "work-row-evidence", children: [_jsxs("span", { className: "work-evidence-chip", children: [_jsx(StateLabel, { className: "work-ci-fact", icon: stateIcon[ci.tone], label: ci.label, size: "compact", tone: ci.tone }), _jsx("span", { className: "work-row-read", children: pullRequest == null ? (" · Not read") : (_jsxs(_Fragment, { children: [observed?.unknown?.source === "github" ? " · last read " : " · read ", _jsx("time", { dateTime: new Date(pullRequest.confirmedAt).toISOString(), title: formatTimestamp(pullRequest.confirmedAt), children: formatClock(pullRequest.confirmedAt) })] })) })] }), _jsxs("span", { className: "work-evidence-chip", children: [_jsx(StateLabel, { className: "work-review-fact", icon: stateIcon[review.tone], label: review.evidence, size: "compact", tone: review.tone }), _jsx("span", { className: "work-row-read", children: pullRequest == null ? (" · Not read") : (_jsxs(_Fragment, { children: [observed?.unknown?.source === "github" ? " · last read" : " · read", " ", _jsx("time", { dateTime: new Date(pullRequest.confirmedAt).toISOString(), title: formatTimestamp(pullRequest.confirmedAt), children: formatClock(pullRequest.confirmedAt) })] })) })] })] }));
129
169
  };
130
- /**
131
- * The repository as a row names it: its last path segment ("npm" for a checkout at
132
- * /home/…/knpkv.dev/npm, or for "knpkv/npm"). The detail keeps the full value.
133
- */
134
- const repositoryName = (repository) => repository
135
- .split("/")
136
- .filter((segment) => segment.length > 0)
137
- .at(-1) ?? repository;
138
170
  const rowCaption = (goal, observed, decisions) => {
139
171
  const open = requestsFor(goal).filter(({ state }) => state === "open");
140
172
  if (open.length > 0) {
@@ -150,16 +182,14 @@ const rowCaption = (goal, observed, decisions) => {
150
182
  : `, ${workRequestClockText(soonest.expiresAt, decisions.now)}${soonest.expiresAt > decisions.now ? " left" : ""}`;
151
183
  return {
152
184
  blocking: false,
153
- text: open.length === 1
154
- ? `Needs approval: ${open[0]?.summary ?? ""}${clock}`
155
- : `${open.length} requests need approval${clock}`
185
+ text: open.length === 1 ? `${open[0]?.summary ?? ""}${clock}` : `${open.length} requests need approval${clock}`
156
186
  };
157
187
  }
158
188
  const blockers = blockersFor(goal);
159
189
  if (blockers.length > 0)
160
190
  return { blocking: true, text: blockers[0]?.summary ?? "Blocked" };
161
191
  if (observed?.stale === true && observed.agent !== null) {
162
- return { blocking: false, text: `Owner gone since ${formatTimestamp(observed.agent.observedAt)}` };
192
+ return { blocking: false, text: `Owner gone since ${formatClock(observed.agent.observedAt)}` };
163
193
  }
164
194
  if (observed?.unknown != null) {
165
195
  return {
@@ -173,24 +203,29 @@ const rowCaption = (goal, observed, decisions) => {
173
203
  return { blocking: false, text: `${goal.owner.name}, ${deliveryLabel[goal.delivery].toLowerCase()}` };
174
204
  };
175
205
  /** The caption under the hero: the request waiting longest, or the latest change. */
176
- const summaryCaption = (summary, tense) => {
206
+ const summaryCaption = (summary, tense, snapshot) => {
177
207
  switch (summary._tag) {
178
208
  case "Attention":
179
209
  return summary.oldestRequest === null
180
210
  ? undefined
181
- : `${tense === "present" ? "Waiting longest" : "Waiting longest then"}: ${summary.oldestRequest.request.summary} on ${summary.oldestRequest.goal.title}, requested ${formatTimestamp(summary.oldestRequest.request.requestedAt)}.`;
182
- case "Clear":
183
- return summary.latest === null
184
- ? undefined
185
- : `${summary.moving} moving. Last change: ${summary.latest.title}, ${formatTimestamp(summary.latest.updatedAt)}.`;
211
+ : `${tense === "present" ? "Waiting longest" : "Waiting longest then"}: ${summary.oldestRequest.request.summary} on ${summary.oldestRequest.goal.title}${tense === "past" ? "." : `, requested ${ageOf(summary.oldestRequest.request.requestedAt, snapshot.asOf)}.`}`;
212
+ case "Clear": {
213
+ const rows = workTriage(snapshot).rows;
214
+ const planned = rows.filter((row) => row.group === "planned").length;
215
+ const finished = rows
216
+ .filter((row) => row.group === "done" || row.group === "earlier")
217
+ .map(({ goal }) => ({ goal, at: workGoalFinishedAt(goal, observedFor(snapshot, goal.id)) }))
218
+ .toSorted((left, right) => right.at - left.at)[0];
219
+ return `${summary.moving} ${summary.moving === 1 ? "goal" : "goals"} ${tense === "past" ? "were " : ""}moving, ${planned} planned.${finished === undefined ? "" : ` Last finished: ${finished.goal.title}, ${ageOf(finished.at, snapshot.asOf)}.`}`;
220
+ }
186
221
  case "Empty":
187
- return "Delegate work to an agent and its goal appears here once the hub admits it.";
222
+ return undefined;
188
223
  }
189
224
  };
190
225
  /**
191
226
  * What the timeline shows for one goal, newest first: the owner's recorded activity, plus what the
192
227
  * reconciler observed (a pull request, the owner's agent) and any source it could not read. Observed
193
- * events carry their provenance as a shape and words; an unreadable source is drawn hatched.
228
+ * events keep source detail and provenance in their accessible descriptions beside terse visible titles.
194
229
  */
195
230
  const timelineFor = (goal, observed) => {
196
231
  const recorded = activityFor(goal).map((entry) => ({
@@ -198,7 +233,7 @@ const timelineFor = (goal, observed) => {
198
233
  dateTime: new Date(entry.occurredAt).toISOString(),
199
234
  detail: activityKindLabel[entry.kind],
200
235
  id: entry.id,
201
- time: formatTimestamp(entry.occurredAt),
236
+ time: formatClock(entry.occurredAt),
202
237
  title: entry.summary
203
238
  }));
204
239
  const seen = [];
@@ -211,7 +246,7 @@ const timelineFor = (goal, observed) => {
211
246
  detail: `${checksLabel[fact.checks]}, last read ${formatTimestamp(confirmedAt)}`,
212
247
  id: `observed-pull-request-${fact.repository}-${fact.pullRequest}`,
213
248
  provenance: { kind: "auto", label: "Observed on GitHub" },
214
- time: formatTimestamp(at),
249
+ time: formatClock(at),
215
250
  title: `Pull request #${fact.pullRequest} ${pullRequestStateLabel[fact.state]}`
216
251
  });
217
252
  }
@@ -223,7 +258,7 @@ const timelineFor = (goal, observed) => {
223
258
  detail: `${fact.host}, last read ${formatTimestamp(confirmedAt)}`,
224
259
  id: `observed-agent-${fact.host}-${fact.agentId}`,
225
260
  provenance: { kind: "auto", label: "Observed in herdr" },
226
- time: formatTimestamp(observedAt),
261
+ time: formatClock(observedAt),
227
262
  title: `Agent ${agentStatusLabel[fact.status]}`
228
263
  });
229
264
  }
@@ -238,13 +273,20 @@ const timelineFor = (goal, observed) => {
238
273
  kind: "unknown",
239
274
  label: lastGoodAt === null ? "Never read" : `Last good read ${formatTimestamp(lastGoodAt)}`
240
275
  },
241
- time: formatTimestamp(since),
276
+ time: formatClock(since),
242
277
  title: `Couldn't read ${sourceLabel[source]}`
243
278
  });
244
279
  }
245
280
  return [...recorded, ...seen].toSorted((left, right) => Date.parse(right.dateTime) - Date.parse(left.dateTime));
246
281
  };
247
- const requestDecisionFor = (request, decisions) => {
282
+ const requestDecisionFor = (request, decisions, historical) => {
283
+ // A recorded open request is never a live approval target; no current queue, clock or handler reaches this bar.
284
+ if (historical && request.state === "open" && request.approvalTarget !== null) {
285
+ return {
286
+ _tag: "Bar",
287
+ bar: (_jsx(DecisionBar, { onApprove: () => undefined, onReject: () => undefined, state: { _tag: "off", reason: "Decisions are off in the past." }, target: request.summary }))
288
+ };
289
+ }
248
290
  const decidability = workRequestDecidability(request, decisions);
249
291
  if (decidability._tag === "Elsewhere" || decisions === undefined)
250
292
  return { _tag: "Elsewhere" };
@@ -283,17 +325,21 @@ const requestDecisionFor = (request, decisions) => {
283
325
  };
284
326
  };
285
327
  /** One request in a goal's detail: its title once, then the bar, the hub link or the outcome. */
286
- const RequestItem = ({ decisions, externalLinks, request }) => {
287
- const decision = requestDecisionFor(request, externalLinks === "enabled" ? decisions : undefined);
328
+ const RequestItem = ({ asOf, decisions, externalLinks, historical, request }) => {
329
+ const decision = requestDecisionFor(request, externalLinks === "enabled" ? decisions : undefined, historical && externalLinks === "enabled");
288
330
  const presentation = requestPresentation[request.state];
289
- return (_jsxs("li", { children: [decision._tag === "Bar" ? null : (_jsxs("span", { className: "work-request-heading", children: [_jsx(Text, { children: request.summary }), _jsx("span", { className: "work-row-state", "data-tone": presentation.tone, children: presentation.label })] })), decision._tag === "Bar" ? (decision.bar) : decision._tag === "Settled" ? (decision.refusal === null ? null : (_jsx(Text, { tone: "secondary", variant: "meta", children: decision.refusal }))) : request.state !== "open" ? null : request.approvalTarget === null ? (_jsx(Text, { tone: "secondary", variant: "meta", children: "No approval link recorded." })) : externalLinks === "disabled" ? (_jsxs(Text, { tone: "secondary", variant: "meta", children: ["Approve this on the hub (", request.approvalTarget.host, ")."] })) : (exactLink(request.approvalTarget, `Approve on ${request.approvalTarget.host}`)), decision._tag === "Elsewhere" ? null : (_jsx("p", { "aria-atomic": "true", className: "work-request-announcement", role: "status", children: decision._tag === "Settled" ? decision.announcement : "" }))] }));
331
+ return (_jsxs("li", { children: [decision._tag === "Bar" ? null : (_jsxs("span", { className: "work-request-heading", children: [_jsx(StateLabel, { className: "work-row-state", icon: request.state === "rejected" ? "close" : stateIcon[presentation.tone], label: presentation.label, size: "compact", tone: presentation.tone }), _jsx(Text, { as: "strong", variant: "meta", children: request.summary }), request.state === "open" ? null : (_jsxs(Text, { tone: "secondary", variant: "meta", children: ["requested ", ageOf(request.requestedAt, asOf)] }))] })), request.state === "open" ? (_jsxs("div", { className: "work-request-meta", children: [decision._tag === "Bar" ? _jsx(StateLabel, { icon: "clock", label: "Open", size: "compact", tone: "caution" }) : null, _jsxs(Text, { tone: "secondary", variant: "meta", children: ["requested ", ageOf(request.requestedAt, asOf)] })] })) : null, decision._tag === "Bar" ? (decision.bar) : decision._tag === "Settled" ? (decision.refusal === null ? null : (_jsx(Text, { tone: "secondary", variant: "meta", children: decision.refusal }))) : request.state !== "open" ? null : request.approvalTarget === null ? (_jsx(Text, { tone: "secondary", variant: "meta", children: "No approval link recorded." })) : externalLinks === "disabled" ? (_jsxs(Text, { tone: "secondary", variant: "meta", children: ["Approve this on the hub (", request.approvalTarget.host, ")."] })) : (exactLink(request.approvalTarget, `Approve on ${request.approvalTarget.host}`)), decision._tag === "Elsewhere" ? null : (_jsx("p", { "aria-atomic": "true", className: "work-request-announcement", role: "status", children: decision._tag === "Settled" ? decision.announcement : "" }))] }));
290
332
  };
291
333
  /** Everything about one goal, in reading order: what it is, where it is, what blocks it, what happened. */
292
- const GoalDetail = ({ decisions, externalLinks, goal, snapshot }) => {
334
+ const GoalDetail = ({ closeControl, decisions, externalLinks, goal, snapshot }) => {
293
335
  const family = familyForGoal(snapshot, goal.id);
294
336
  const observed = observedFor(snapshot, goal.id);
295
337
  const shown = observed?.displayState ?? goal.state;
296
338
  const activity = timelineFor(goal, observed);
339
+ const readDay = new Date(snapshot.observedAt).toDateString();
340
+ const agent = goal.agentHierarchy?.agent;
341
+ const agentName = agent?.name ?? goal.owner.name;
342
+ const agentIdentity = agent === undefined ? agentName : `${agent.host} / ${agentName}`;
297
343
  // Only the live window carries the overlay; there "nothing observed" is itself a reading.
298
344
  const overlay = snapshot.observed !== undefined;
299
345
  // A goal-level approval target (older checkpoints carry one without requests); skipped when a
@@ -303,16 +349,23 @@ const GoalDetail = ({ decisions, externalLinks, goal, snapshot }) => {
303
349
  requestsFor(goal).some(({ approvalTarget }) => approvalTarget?.url === goal.approvalTarget?.url)
304
350
  ? null
305
351
  : goal.approvalTarget;
306
- return (_jsxs("div", { className: "work-detail", children: [_jsx(Text, { tone: "secondary", children: goal.detail }), _jsx(StageRail, { heading: `Delivery of ${goal.title}`, size: "words", stages: stagesFor(goal, shown) }), _jsxs("dl", { className: "work-facts", children: [_jsxs("div", { children: [_jsx("dt", { children: "State" }), _jsxs("dd", { children: [displayPresentation[shown].label, shown === goal.state ? null : (_jsxs(Text, { as: "p", className: "work-fact-note", tone: "secondary", variant: "meta", children: ["Observed; recorded as ", statePresentation[goal.state].label.toLowerCase()] }))] })] }), overlay ? (_jsxs("div", { children: [_jsx("dt", { children: "Pull request" }), _jsx("dd", { children: observed?.pullRequest == null
307
- ? observed === null && snapshot.observedOmitted !== undefined
308
- ? "Not in this read: live state was trimmed to the most recently updated goals"
309
- : "None known"
310
- : `#${observed.pullRequest.fact.pullRequest} ${pullRequestStateLabel[observed.pullRequest.fact.state]}, ${checksLabel[observed.pullRequest.fact.checks]}` })] })) : null, _jsxs("div", { children: [_jsx("dt", { children: "Owner" }), _jsx("dd", { children: goal.owner.name })] }), _jsxs("div", { children: [_jsx("dt", { children: "Agent" }), _jsxs("dd", { children: [hierarchyLabel(goal), goal.agentHierarchy?.agent.relationship === undefined || goal.agentHierarchy === null
352
+ return (_jsxs("div", { className: "work-detail", children: [_jsxs("div", { className: "work-detail-heading", children: [_jsx(Text, { as: "h3", variant: "card-title", children: goal.title }), _jsx(StateLabel, { icon: stateIcon[displayPresentation[shown].tone], label: displayPresentation[shown].label, size: "compact", tone: displayPresentation[shown].tone }), shown === goal.state ? null : (_jsxs(Text, { className: "work-fact-note", tone: "secondary", variant: "meta", children: ["Observed; recorded as ", statePresentation[goal.state].label.toLowerCase()] }))] }), observed?.stale === true && observed.agent !== null ? (_jsxs("p", { className: "work-note", "data-tone": "held", children: [_jsx("b", { children: "Owner gone" }), " since ", formatClock(observed.agent.observedAt), ": this unfinished goal has no live agent."] })) : null, _jsx(GoalProgress, { goal: goal, observed: observed }), overlay ? _jsx(GoalEvidence, { goal: goal, observed: observed }) : null, _jsxs("dl", { className: "work-facts", children: [_jsxs("div", { children: [_jsx("dt", { children: "Pull request" }), _jsxs("dd", { children: [observed?.pullRequest == null
353
+ ? observed === null && snapshot.observedOmitted !== undefined
354
+ ? "Not in this read: live state was trimmed to the most recently updated goals"
355
+ : "None yet"
356
+ : `#${observed.pullRequest.fact.pullRequest} ${pullRequestStateLabel[observed.pullRequest.fact.state]}${observed.stale ? `, ${checksLabel[observed.pullRequest.fact.checks]}` : ""}`, observed?.pullRequest == null ? null : (_jsx(Text, { as: "p", className: "work-fact-note", tone: "secondary", variant: "meta", children: "Observed on GitHub" }))] })] }), goal.review == null ? null : (_jsxs("div", { children: [_jsx("dt", { children: "Review" }), _jsxs("dd", { children: [reviewLabel(goal.review), overlay ? null : ", as recorded", goal.review?.url === null || goal.review?.url === undefined ? null : externalLinks ===
357
+ "disabled" ? null : (_jsxs(_Fragment, { children: [" ", reviewLink(goal.review.url)] })), goal.review?.summary === null || goal.review?.summary === undefined ? null : (_jsx(Text, { tone: "secondary", variant: "meta", children: goal.review.summary }))] })] })), _jsxs("div", { children: [_jsx("dt", { children: "Repository" }), _jsx("dd", { children: goal.repository.repository })] }), _jsxs("div", { children: [_jsx("dt", { children: "Branch" }), _jsx("dd", { children: goal.repository.branch })] }), goal.owner.name === agentName ? null : (_jsxs("div", { children: [_jsx("dt", { children: "Owner" }), _jsx("dd", { children: goal.owner.name })] })), _jsxs("div", { children: [_jsx("dt", { children: "Agent" }), _jsxs("dd", { children: [observed?.agent == null
358
+ ? `${agentIdentity}, as recorded`
359
+ : `${agentIdentity}, ${agentStatusLabel[observed.agent.fact.status]}${observed.agent.fact.status === "gone" ? ` since ${formatClock(observed.agent.observedAt)}` : ""}`, observed?.agent == null ? null : (_jsx(Text, { as: "p", className: "work-fact-note", tone: "secondary", variant: "meta", children: "Observed in herdr" })), goal.agentHierarchy?.agent.relationship === undefined || goal.agentHierarchy === null
311
360
  ? null
312
- : `, ${goal.agentHierarchy.agent.relationship.relation} from ${goal.agentHierarchy.agent.relationship.parentAgentId}`] })] }), _jsxs("div", { children: [_jsx("dt", { children: "Repository" }), _jsxs("dd", { children: [goal.repository.repository, ", ", _jsx("code", { children: goal.repository.branch })] })] }), _jsxs("div", { children: [_jsx("dt", { children: "Review" }), _jsxs("dd", { children: [reviewLabel(goal.review), goal.review?.url === null || goal.review?.url === undefined ? null : externalLinks ===
313
- "disabled" ? null : (_jsxs(_Fragment, { children: [" ", reviewLink(goal.review.url)] })), goal.review?.summary === null || goal.review?.summary === undefined ? null : (_jsx(Text, { tone: "secondary", variant: "meta", children: goal.review.summary }))] })] }), _jsxs("div", { children: [_jsx("dt", { children: "Spend" }), _jsx("dd", { children: formatSpend(goal) })] })] }), observed?.stale === true && observed.agent !== null ? (_jsxs("p", { className: "work-note", "data-tone": "held", children: [_jsx("b", { children: "Owner gone" }), " since ", formatTimestamp(observed.agent.observedAt), ": this unfinished goal has no live agent."] })) : null, observed?.unknown == null ? null : (_jsxs("p", { className: "work-note", "data-tone": "unknown", children: [_jsxs("b", { children: ["Couldn't read ", sourceLabel[observed.unknown.source]] }), " since ", formatTimestamp(observed.unknown.since), ":", " ", observed.unknown.reason, ".", " ", observed.unknown.lastGoodAt === null
361
+ : `, ${goal.agentHierarchy.agent.relationship.relation} from ${goal.agentHierarchy.agent.relationship.parentAgentId}`] })] }), goal.spend === null ? null : (_jsxs("div", { children: [_jsx("dt", { children: "Spend" }), _jsx("dd", { children: formatSpend(goal) })] }))] }), goal.detail === goal.title ? null : _jsx(Text, { tone: "secondary", children: goal.detail }), observed?.unknown == null ? null : (_jsxs("p", { className: "work-note", "data-tone": "unknown", children: [_jsxs("b", { children: ["Couldn't read ", sourceLabel[observed.unknown.source]] }), " since ", formatTimestamp(observed.unknown.since), ":", " ", observed.unknown.reason, ".", " ", observed.unknown.lastGoodAt === null
314
362
  ? "It has never been read, so nothing here comes from it."
315
- : `Facts from it are as of ${formatTimestamp(observed.unknown.lastGoodAt)}.`] })), blockersFor(goal).map((blocker) => (_jsxs("p", { className: "work-note", "data-tone": "blocked", children: [_jsx("b", { children: "Blocked" }), " since ", formatTimestamp(blocker.since), ": ", blocker.summary] }, `${blocker.since}-${blocker.summary}`))), _jsxs("section", { "aria-labelledby": "work-requests-title", className: "work-detail-section", children: [_jsx("h3", { className: "work-group-title", id: "work-requests-title", children: "Requests" }), requestsFor(goal).length === 0 ? (_jsx(Text, { tone: "secondary", children: goal.requests === undefined ? "No requests recorded." : "No outstanding requests." })) : (_jsx("ul", { className: "work-detail-list", children: requestsFor(goal).map((request) => (_jsx(RequestItem, { decisions: decisions, externalLinks: externalLinks, request: request }, request.id))) }))] }), _jsxs("section", { "aria-labelledby": "work-activity-title", className: "work-detail-section", children: [_jsx("h3", { className: "work-group-title", id: "work-activity-title", children: "Activity" }), activity.length === 0 ? (_jsx(Text, { tone: "secondary", children: goal.activity === undefined ? "No activity recorded." : "Activity is clear." })) : (_jsx("ol", { "aria-label": `Activity on ${goal.title}`, className: "work-activity", children: activity.map((event, index) => (_jsx(TimelineRow, { continued: index < activity.length - 1, event: event }, event.id))) }))] }), family === null ? null : (_jsxs("section", { "aria-labelledby": "work-family-title", className: "work-detail-section", children: [_jsx("h3", { className: "work-group-title", id: "work-family-title", children: "Superseded history" }), _jsxs("details", { className: "work-family-history", children: [_jsxs("summary", { children: ["Show ", family.superseded.length, " superseded"] }), _jsx("ul", { className: "work-detail-list", children: family.superseded.map((entry) => (_jsxs("li", { children: [_jsxs("span", { className: "work-request-heading", children: [_jsx(Text, { children: entry.title }), _jsx("span", { className: "work-row-state", "data-tone": statePresentation[entry.state].tone, children: statePresentation[entry.state].label })] }), _jsxs(Text, { tone: "secondary", variant: "meta", children: [entry.summary, ", ", formatTimestamp(entry.updatedAt)] }), blockersFor(entry).map((blocker) => (_jsxs(Text, { tone: "secondary", variant: "meta", children: ["Blocked since ", formatTimestamp(blocker.since), ": ", blocker.summary] }, `${blocker.since}-${blocker.summary}`))), entry.review === undefined || entry.review === null ? null : (_jsxs(Text, { tone: "secondary", variant: "meta", children: ["Review ", reviewPresentation[entry.review.state].label.toLowerCase(), entry.review.summary === null ? "" : `: ${entry.review.summary}`] }))] }, entry.id))) })] })] })), _jsxs("div", { className: "work-links", children: [goal.connectTarget === null ? (_jsx(Text, { tone: "secondary", children: "No exact Connect target recorded." })) : externalLinks === "disabled" ? (_jsx(Text, { tone: "secondary", variant: "meta", children: "Connect target recorded." })) : (_jsx("a", { className: "work-connect-link", href: goal.connectTarget.url, children: "Open the agent in Connect" })), goalApproval === null ? null : externalLinks === "disabled" ? (_jsxs(Text, { tone: "secondary", variant: "meta", children: ["Approval target recorded on ", goalApproval.host, "."] })) : (exactLink(goalApproval, `Open ${goalApproval.host} approval`))] })] }));
363
+ : `Facts from it are as of ${formatTimestamp(observed.unknown.lastGoodAt)}.`] })), blockersFor(goal).map((blocker) => (_jsxs("p", { className: "work-note", "data-tone": "blocked", children: [_jsx("b", { children: "Blocked" }), " since ", formatTimestamp(blocker.since), ": ", blocker.summary] }, `${blocker.since}-${blocker.summary}`))), _jsxs("section", { "aria-labelledby": "work-requests-title", className: "work-detail-section", children: [_jsx("h3", { className: "work-group-title", id: "work-requests-title", children: "Requests" }), requestsFor(goal).length === 0 ? (_jsx(Text, { tone: "secondary", children: goal.requests === undefined ? "No requests recorded." : "No outstanding requests." })) : (_jsx("ul", { className: "work-detail-list", children: requestsFor(goal).map((request) => (_jsx(RequestItem, { asOf: snapshot.asOf, decisions: decisions, externalLinks: externalLinks, historical: snapshot.window !== "now", request: request }, request.id))) }))] }), _jsxs("section", { "aria-labelledby": "work-activity-title", className: "work-detail-section", children: [_jsx("h3", { className: "work-group-title", id: "work-activity-title", children: "Activity" }), activityOmittedFor(snapshot, goal) === 0 ? null : (_jsx(Text, { tone: "secondary", variant: "meta", children: activityShownOf(goal.activity?.length ?? 0, activityOmittedFor(snapshot, goal)) })), activity.length === 0 ? (_jsx(Text, { tone: "secondary", children: goal.activity === undefined ? "No activity recorded." : "Activity is clear." })) : (_jsx("ol", { "aria-label": `Activity on ${goal.title}`, className: "work-activity", children: activity.map((event) => {
364
+ const descriptionId = `work-activity-${goal.id}-${event.id}`;
365
+ const eventAt = Date.parse(event.dateTime);
366
+ const date = new Date(eventAt).toDateString() === readDay ? null : formatTimestamp(eventAt);
367
+ return (_jsxs("li", { "aria-describedby": descriptionId, className: "work-activity-row", "data-rly-timeline-event-id": event.id, children: [_jsx("time", { dateTime: event.dateTime, children: event.time }), _jsx("span", { children: event.title }), _jsxs("span", { className: "work-accessible-detail", "data-rly-timeline-provenance": event.provenance?.kind, id: descriptionId, children: [event.detail, event.provenance === undefined ? "" : `. ${event.provenance.label}`, date === null ? "" : `. ${date}`] })] }, event.id));
368
+ }) }))] }), family === null ? null : (_jsxs("section", { "aria-labelledby": "work-family-title", className: "work-detail-section", children: [_jsx("h3", { className: "work-group-title", id: "work-family-title", children: "Superseded history" }), _jsxs("details", { className: "work-family-history", children: [_jsxs("summary", { children: ["Show ", family.superseded.length, " superseded"] }), _jsx("ul", { className: "work-detail-list", children: family.superseded.map((entry) => (_jsxs("li", { children: [_jsxs("span", { className: "work-request-heading", children: [_jsx(Text, { children: entry.title }), _jsx("span", { className: "work-row-state", "data-tone": statePresentation[entry.state].tone, children: statePresentation[entry.state].label })] }), _jsxs(Text, { tone: "secondary", variant: "meta", children: [entry.summary, ", ", formatTimestamp(entry.updatedAt)] }), blockersFor(entry).map((blocker) => (_jsxs(Text, { tone: "secondary", variant: "meta", children: ["Blocked since ", formatTimestamp(blocker.since), ": ", blocker.summary] }, `${blocker.since}-${blocker.summary}`))), entry.review === undefined || entry.review === null ? null : (_jsxs(Text, { tone: "secondary", variant: "meta", children: ["Review ", reviewPresentation[entry.review.state].label.toLowerCase(), entry.review.summary === null ? "" : `: ${entry.review.summary}`] }))] }, entry.id))) })] })] })), _jsxs("div", { className: "work-links", children: [goal.connectTarget === null ? (_jsx(Text, { tone: "secondary", children: "No exact Connect target recorded." })) : externalLinks === "disabled" ? (_jsx(Text, { tone: "secondary", variant: "meta", children: "Connect target recorded." })) : (_jsxs("a", { className: "work-connect-link", href: `${goal.connectTarget.url}&open=stage`, children: [observed?.stale === true ? "Find" : "Open", " ", agentName, " in Connect", _jsx(Icon, { decorative: true, name: "arrow-right", size: "small" })] })), goalApproval === null ? null : externalLinks === "disabled" ? (_jsxs(Text, { tone: "secondary", variant: "meta", children: ["Approval target recorded on ", goalApproval.host, "."] })) : (exactLink(goalApproval, `Open ${goalApproval.host} approval`)), closeControl] })] }));
316
369
  };
317
370
  const activityKindLabel = {
318
371
  note: "Note",
@@ -323,6 +376,22 @@ const activityKindLabel = {
323
376
  shipment: "Shipment"
324
377
  };
325
378
  const snapshotFor = (snapshots, window) => snapshots[window];
379
+ /** How many older activities the snapshot left out of this goal's timeline. */
380
+ const activityOmittedFor = (snapshot, goal) => snapshot.activityOmitted?.[goal.id] ?? 0;
381
+ /** Header read time and live-read limitations. Omitted goal counts belong to the board. */
382
+ const headerSentences = (window, snapshot, host) => {
383
+ const when = `Durable goals${host === undefined ? "" : ` on ${host}`}, read ${formatClock(snapshot.observedAt)}.`;
384
+ const live = window !== "now"
385
+ ? []
386
+ : snapshot.observed === undefined
387
+ ? [
388
+ "Live state not available: this hub sends no observed facts, so owner and pull request lines show only what each goal recorded. Update herdr-work on the hub to see them."
389
+ ]
390
+ : snapshot.observedOmitted === undefined
391
+ ? []
392
+ : [`Live state shown for the most recently updated goals; ${String(snapshot.observedOmitted)} left out.`];
393
+ return [when, ...live];
394
+ };
326
395
  const familyForGoal = (snapshot, goalId) => (snapshot.families ?? []).find((group) => group.canonicalGoalId === goalId) ?? null;
327
396
  const familyLabelForGoal = (snapshot, goal) => {
328
397
  const group = familyForGoal(snapshot, goal.id);
@@ -335,7 +404,7 @@ const withFragment = (href, fragment) => {
335
404
  const fragmentIndex = href.indexOf("#");
336
405
  return `${fragmentIndex === -1 ? href : href.slice(0, fragmentIndex)}#${fragment}`;
337
406
  };
338
- export const WorkBoard = ({ decisions, externalLinks = "enabled", initialGoalId, initialWindow = "now", navigation, snapshots }) => {
407
+ export const WorkBoard = ({ decisions, externalLinks = "enabled", host, initialGoalId, initialWindow = "now", navigation, snapshots }) => {
339
408
  const [window, setWindow] = useState(initialWindow);
340
409
  const snapshot = snapshotFor(snapshots, window);
341
410
  const directInitialGoal = snapshot.goals.find(({ id }) => id === initialGoalId);
@@ -358,6 +427,9 @@ export const WorkBoard = ({ decisions, externalLinks = "enabled", initialGoalId,
358
427
  const selectedLinkRowRef = useRef(null);
359
428
  const selectedRowRef = useRef(null);
360
429
  const triage = workTriage(snapshot);
430
+ const finishedGoalCount = triage.rows.filter(({ displayState }) => displayState === "completed" || displayState === "deployed" || displayState === "abandoned").length;
431
+ const hasGoalOmissions = (snapshot.goalsOmitted ?? 0) > 0 || (snapshot.finishedOmitted ?? 0) > 0;
432
+ const finishedCount = finishedGoalCount > 0 || (snapshot.finishedOmitted ?? 0) > 0 ? (_jsxs(Text, { className: "work-finished-count", tone: "secondary", variant: "meta", children: [finishedGoalCount, " finished ", finishedGoalCount === 1 ? "goal" : "goals", (snapshot.finishedOmitted ?? 0) > 0 ? ` · ${snapshot.finishedOmitted} older not shown` : null] })) : null;
361
433
  // The overlay by goal, built once per snapshot: rows, captions and filters all read it.
362
434
  const overlay = new Map((snapshot.observed ?? []).map((entry) => [entry.goalId, entry]));
363
435
  const observedOf = (goal) => overlay.get(goal.id) ?? null;
@@ -365,9 +437,7 @@ export const WorkBoard = ({ decisions, externalLinks = "enabled", initialGoalId,
365
437
  // A historical window is the state as of its time; say so, never in the present tense.
366
438
  const tense = window === "now" ? "present" : "past";
367
439
  const sentence = workTriageSentence(triage.summary, tense);
368
- const heroFact = tense === "present"
369
- ? sentence
370
- : `As of ${formatTimestamp(snapshot.asOf)}, ${sentence.charAt(0).toLowerCase()}${sentence.slice(1)}`;
440
+ const heroFact = triage.summary._tag === "Attention" && triage.summary.blocked > 0 ? (_jsxs(_Fragment, { children: [sentence.slice(0, -`${triage.summary.blocked} blocked`.length), _jsx(HeroWord, { tone: "blocked", children: `${triage.summary.blocked} blocked` })] })) : tense === "past" && triage.summary._tag === "Attention" ? (_jsxs(_Fragment, { children: [sentence.split("needed you")[0], _jsx(HeroWord, { tone: "held", children: "needed you" })] })) : triage.summary._tag === "Empty" && tense === "past" ? ("Nothing needed you") : (sentence);
371
441
  const groupById = new Map(triage.rows.map((row) => [row.goal.id, row.group]));
372
442
  const ordered = triage.rows.map((row) => row.goal);
373
443
  // Filters match what each row shows: the observed state where there is one.
@@ -400,9 +470,25 @@ export const WorkBoard = ({ decisions, externalLinks = "enabled", initialGoalId,
400
470
  setVisibleGoalCount(initialVisibleGoalCount);
401
471
  }, [selectedFilteredGoalIndex, selectedId]);
402
472
  const goalRow = (goal) => {
403
- const caption = rowCaption(goal, observedOf(goal), externalLinks === "enabled" ? decisions : undefined);
404
- const content = (_jsxs(_Fragment, { children: [_jsxs("span", { className: "work-row-title", children: [_jsxs("span", { className: "work-row-meta", children: [repositoryName(goal.repository.repository), ", ", _jsx("code", { children: goal.repository.branch })] }), goal.title] }), _jsx("span", { className: "work-row-state", "data-tone": displayPresentation[shownOf(goal)].tone, children: displayPresentation[shownOf(goal)].label }), _jsxs("span", { className: "work-row-caption", "data-blocking": caption.blocking, children: [caption.text, familyLabelForGoal(snapshot, goal) === null ? null : `, ${familyLabelForGoal(snapshot, goal)}`] })] }));
405
- return navigation === undefined ? (_jsx("button", { "aria-controls": detailsOpen && selected?.id === goal.id ? "work-goal-details" : undefined, "aria-expanded": detailsOpen && selected?.id === goal.id, "aria-pressed": selected?.id === goal.id, className: "work-board-row", id: selected?.id === goal.id ? selectedGoalFragment : undefined, onClick: () => {
473
+ const observed = observedOf(goal);
474
+ const shown = shownOf(goal);
475
+ const caption = rowCaption(goal, observed, externalLinks === "enabled" && window === "now" ? decisions : undefined);
476
+ const open = requestsFor(goal).filter((request) => request.state === "open");
477
+ const host = observed?.agent?.fact.host ?? goal.connectTarget?.host ?? goal.agentHierarchy?.agent.host;
478
+ const agentName = goal.agentHierarchy?.agent.name ?? goal.owner.name;
479
+ const pr = observed?.pullRequest?.fact;
480
+ const terminal = shown === "completed" || shown === "deployed" || shown === "abandoned";
481
+ const stage = pr === undefined
482
+ ? `Stage: ${deliveryLabel[goal.delivery]}`
483
+ : `PR #${pr.pullRequest} ${pullRequestStateLabel[pr.state]}, ${checksLabel[pr.checks]}${pr.review === "not_requested" ? "" : ` · Review ${reviewPresentation[pr.review].label.toLowerCase()}`}`;
484
+ const metadata = goal.owner.name === "Unassigned"
485
+ ? `Unassigned · ${goal.repository.repository}`
486
+ : terminal
487
+ ? `${agentName} · ${shown === "abandoned" ? "" : goal.delivery === "merged" || pr?.state === "merged" ? "Merged · " : "Done · "}${displayPresentation[shown].label} ${ageOf(pr?.closedAt ?? goal.updatedAt, snapshot.asOf)}`
488
+ : `${agentName}${host === undefined ? "" : ` on ${host}`} · ${stage}${blockersFor(goal).length === 0 || open.length > 0 ? "" : ` · ${blockersFor(goal)[0]?.summary ?? "Blocked"}`}`;
489
+ const hasCaption = open.length > 0 || observed?.stale === true || observed?.unknown != null;
490
+ const content = (_jsxs(_Fragment, { children: [_jsx("span", { className: "work-row-title", children: goal.title }), _jsx(StateLabel, { className: "work-row-state", icon: stateIcon[displayPresentation[shown].tone], label: displayPresentation[shown].label, size: "compact", tone: displayPresentation[shown].tone }), _jsxs("span", { className: "work-row-meta", children: [metadata, familyLabelForGoal(snapshot, goal) === null ? null : ` · ${familyLabelForGoal(snapshot, goal)}`] }), hasCaption ? (_jsx("span", { className: "work-row-caption", "data-blocking": caption.blocking, children: open.length === 0 ? (caption.text) : (_jsxs(_Fragment, { children: [_jsxs("span", { className: "work-request-line", children: [_jsx(StateLabel, { className: "work-row-state", icon: "clock", label: "Open", size: "compact", tone: "caution" }), " ", _jsx("span", { className: "work-request-title", children: caption.text })] }), " ", _jsx("span", { className: "work-request-age", children: window === "now" ? ageOf(open[0]?.requestedAt ?? snapshot.asOf, snapshot.asOf) : "was open then" })] })) })) : null] }));
491
+ const control = navigation === undefined ? (_jsx("button", { "aria-controls": detailsOpen && selected?.id === goal.id ? "work-goal-details" : undefined, "aria-expanded": detailsOpen && selected?.id === goal.id, "aria-pressed": selected?.id === goal.id, className: "work-board-row", id: selected?.id === goal.id ? selectedGoalFragment : undefined, onClick: () => {
406
492
  setDetailsOpen(true);
407
493
  setSelectedId(goal.id);
408
494
  }, ref: selected?.id === goal.id ? selectedRowRef : undefined, type: "button", children: content })) : (_jsx("a", { "aria-current": selected?.id === goal.id ? "true" : undefined, className: "work-board-row", href: navigation({
@@ -416,6 +502,7 @@ export const WorkBoard = ({ decisions, externalLinks = "enabled", initialGoalId,
416
502
  }),
417
503
  window
418
504
  }), id: selected?.id === goal.id ? selectedGoalFragment : undefined, ref: selected?.id === goal.id ? selectedLinkRowRef : undefined, children: content }));
505
+ return control;
419
506
  };
420
507
  const timeTravel = (_jsx("div", { "aria-label": "Choose work snapshot", className: "work-time-controls", role: "group", children: windows.map((option) => {
421
508
  if (navigation === undefined) {
@@ -449,12 +536,12 @@ export const WorkBoard = ({ decisions, externalLinks = "enabled", initialGoalId,
449
536
  }) }));
450
537
  const statusFilterControls = (_jsx("div", { "aria-label": "Filter goals by status", className: "work-status-filters", role: "group", children: statusFilters.map((state) => {
451
538
  const label = state === "all" ? "All" : statePresentation[state].label;
452
- return navigation === undefined ? (_jsx("button", { "aria-pressed": statusFilter === state, className: "work-status-filter", onClick: () => {
539
+ return navigation === undefined ? (_jsx("button", { "aria-pressed": statusFilter === state, className: "work-status-filter", onFocus: ({ currentTarget }) => currentTarget.scrollIntoView({ block: "nearest", inline: "nearest" }), onClick: () => {
453
540
  setDetailsOpen(false);
454
541
  setSelectedId(null);
455
542
  setStatusFilter(state);
456
543
  setVisibleGoalCount(initialVisibleGoalCount);
457
- }, type: "button", children: label }, state)) : (_jsx("a", { "aria-current": statusFilter === state ? "page" : undefined, className: "work-status-filter", href: navigation({
544
+ }, type: "button", children: label }, state)) : (_jsx("a", { "aria-current": statusFilter === state ? "page" : undefined, className: "work-status-filter", onFocus: ({ currentTarget }) => currentTarget.scrollIntoView({ block: "nearest", inline: "nearest" }), href: navigation({
458
545
  goalId: encodeWorkBoardNavigationGoal({
459
546
  detailsOpen: false,
460
547
  goalId: null,
@@ -464,13 +551,35 @@ export const WorkBoard = ({ decisions, externalLinks = "enabled", initialGoalId,
464
551
  window
465
552
  }), children: label }, state));
466
553
  }) }));
467
- return (_jsxs("section", { className: "work-page", "aria-labelledby": "work-board-title", children: [_jsxs("header", { className: "work-page-intro", children: [_jsx(Text, { as: "h1", id: "work-board-title", variant: "card-title", children: "Work" }), _jsxs(Text, { tone: "secondary", variant: "meta", children: [window === "now" ? "Live" : windowLabel[window], ", as of ", formatTimestamp(snapshot.asOf), window !== "now"
468
- ? null
469
- : snapshot.observed === undefined
470
- ? ". Live state not available: this hub sends no observed facts (its herdr-work predates the reconciler, or the overlay did not fit the response), so states are as recorded. Update herdr-work on the hub to see them."
471
- : snapshot.observedOmitted === undefined
472
- ? null
473
- : `. Live state shown for the most recently updated goals; ${snapshot.observedOmitted} left out.`] })] }), _jsx(Hero, { caption: summaryCaption(triage.summary, tense), fact: heroFact, label: "Work summary" }), timeTravel, snapshot.goals.length === 0 ? (_jsx(Region, { title: "Goals", children: _jsx(Text, { tone: "secondary", children: window === "now" ? (_jsxs(_Fragment, { children: ["No goals yet. Delegate work to an agent with", " ", _jsx("code", { children: "fleetctl submit HOST agent.delegate work REPOSITORY PROMPT" }), " (HOST is a name from", " ", _jsx("code", { children: "fleetctl hosts" }), "); its goal appears here once the hub admits it."] })) : ("No goals at this checkpoint.") }) })) : (_jsxs("div", { className: "work-board-layout", "data-has-detail": selected !== null && detailsOpen, children: [_jsxs(Region, { actions: statusFilterControls, className: "work-board-list", count: filteredGoals.length, title: "Goals", children: [_jsxs(Text, { "aria-live": "polite", tone: "secondary", variant: "meta", children: ["Showing ", visibleGoals.length, " of ", filteredGoals.length, " goals"] }), filteredGoals.length === 0 ? _jsx(Text, { tone: "secondary", children: "No goals match this status." }) : null, visibleGroups.map(({ goals, group }) => (_jsxs("section", { "aria-labelledby": `work-group-${group}`, className: "work-group", children: [_jsxs("h3", { className: "work-group-title", id: `work-group-${group}`, children: [workTriageGroupTitle[group], " ", _jsxs("span", { className: "work-group-count", children: ["(", goals.length, ")"] })] }), _jsx("ul", { className: "work-rows", children: goals.map((goal) => (_jsx("li", { children: goalRow(goal) }, goal.id))) })] }, group))), visibleGoals.length < filteredGoals.length ? (navigation === undefined ? (_jsx(Button, { onClick: () => setVisibleGoalCount((count) => Math.min(count + initialVisibleGoalCount, filteredGoals.length)), children: "Load 10 more" })) : (_jsx("a", { className: "work-load-more-link", href: navigation({
554
+ const closeControl = (label) => navigation === undefined ? (_jsx(Button, { onClick: () => {
555
+ setDetailsOpen(false);
556
+ (selectedRowRef.current ?? selectedLinkRowRef.current)?.focus();
557
+ }, className: label === "Close" ? "work-close-heading" : undefined, variant: label === "Close" ? "quiet" : "secondary", children: label })) : (_jsx("a", { className: `work-exact-link work-close-control${label === "Close" ? " work-close-heading" : ""}`, href: withFragment(navigation({
558
+ goalId: encodeWorkBoardNavigationGoal({
559
+ detailsOpen: false,
560
+ goalId: selectedId,
561
+ statusFilter,
562
+ visibleGoalCount
563
+ }),
564
+ window
565
+ }), selectedGoalFragment), children: label }));
566
+ return (_jsxs("section", { className: "work-page", "aria-labelledby": "work-board-title", children: [_jsxs("header", { className: "work-page-intro", children: [_jsx(Text, { as: "h1", id: "work-board-title", variant: "section-title", children: "Work" }), _jsx(Text, { tone: "secondary", variant: "meta", children: headerSentences(window, snapshot, host)[0] })] }), headerSentences(window, snapshot, host)
567
+ .slice(1)
568
+ .map((sentence) => (_jsxs("p", { className: "work-note work-overlay-note", "data-tone": "held", children: [_jsx(Icon, { decorative: true, name: "clock", size: "small" }), _jsx("span", { children: sentence })] }, sentence))), externalLinks === "disabled" ? (_jsx("p", { className: "work-note", children: "Read-only on the local network. Requests are decided on the hub." })) : null, window === "now" ? null : (_jsxs("div", { className: "work-note work-history-note", children: [_jsxs("span", { children: ["Work as it was ", windowLabel[window].toLowerCase(), ", ", formatTimestamp(snapshot.asOf), ". Decisions are off in the past."] }), navigation === undefined ? (_jsx(Button, { onClick: () => {
569
+ setWindow("now");
570
+ setDetailsOpen(false);
571
+ setSelectedId(null);
572
+ setStatusFilter("all");
573
+ setVisibleGoalCount(initialVisibleGoalCount);
574
+ }, variant: "secondary", children: "Back to now" })) : (_jsx("a", { className: "work-exact-link", href: navigation({ goalId: null, window: "now" }), children: "Back to now" }))] })), snapshot.goals.length === 0 && (window === "now" || hasGoalOmissions) ? null : (_jsx(Hero, { caption: summaryCaption(triage.summary, tense, snapshot), fact: heroFact, label: "Work summary", size: "heading" })), _jsxs("div", { className: "work-board-controls", children: [timeTravel, statusFilterControls] }), snapshot.goals.length === 0 ? (_jsx(Region, { actions: (snapshot.goalsOmitted ?? 0) > 0 ? (_jsxs(Text, { "aria-live": "polite", tone: "secondary", variant: "meta", children: ["Showing 0 of ", snapshot.goalsOmitted, " goals"] })) : undefined, className: "work-empty-board", count: snapshot.goalsOmitted ?? 0, title: "Goals", children: _jsxs("div", { className: "work-empty-copy", children: [hasGoalOmissions ? (_jsx(Text, { as: "strong", children: "No goals in this read." })) : window === "now" ? (_jsx(Text, { as: "strong", children: "No goals yet." })) : null, hasGoalOmissions ? null : (_jsx(Text, { tone: "secondary", children: window === "now" ? (_jsxs(_Fragment, { children: ["Delegate work to an agent with", " ", _jsx("code", { children: "fleetctl submit HOST agent.delegate work REPOSITORY PROMPT" }), " (HOST is a name from", " ", _jsx("code", { children: "fleetctl hosts" }), "); its goal appears here once the hub admits it."] })) : ("No goals at this checkpoint.") })), finishedCount] }) })) : (_jsxs("div", { className: "work-board-layout", "data-has-detail": selected !== null && detailsOpen, children: [selected === null || !detailsOpen ? null : (_jsx(Region, { actions: closeControl("Close"), "aria-label": "Goal details", className: "work-inspector", headingId: "work-goal-details-title", id: "work-goal-details", ref: detailsRef, title: "Goal details", children: _jsx(GoalDetail, { closeControl: closeControl("Close details"), decisions: snapshot.window === "now" ? decisions : undefined, externalLinks: externalLinks, goal: selected, snapshot: snapshot }) })), _jsxs(Region, { actions: filteredGoals.length === 0 ? undefined : (_jsxs(Text, { "aria-live": "polite", tone: "secondary", variant: "meta", children: ["Showing ", visibleGoals.length, " of", " ", filteredGoals.length + (statusFilter === "all" ? (snapshot.goalsOmitted ?? 0) : 0), " goals"] })), className: "work-board-list", count: statusFilter === "all"
575
+ ? filteredGoals.length + (snapshot.goalsOmitted ?? 0)
576
+ : filteredGoals.length === 0
577
+ ? `0 of ${snapshot.goals.length + (snapshot.goalsOmitted ?? 0)}`
578
+ : filteredGoals.length, title: "Goals", children: [filteredGoals.length === 0 ? (_jsxs("div", { className: "work-filter-empty", children: [_jsx(Text, { tone: "secondary", children: "No goals match this status." }), navigation === undefined ? (_jsx(Button, { onClick: () => setStatusFilter("all"), variant: "secondary", children: "Show all" })) : (_jsx("a", { className: "work-exact-link", href: navigation({ goalId: null, window }), children: "Show all" }))] })) : null, visibleGroups.map(({ goals, group }) => (_jsxs("section", { "aria-labelledby": `work-group-${group}`, className: "work-group", children: [_jsxs("h3", { className: "work-group-title", id: `work-group-${group}`, children: [group === "planned"
579
+ ? "Planned"
580
+ : group === "needs-you" && tense === "past"
581
+ ? "Needed you"
582
+ : workTriageGroupTitle[group], " ", _jsxs("span", { className: "work-group-count", children: ["(", goals.length, ")"] })] }), _jsx("ul", { className: "work-rows", children: goals.map((goal) => (_jsx("li", { children: goalRow(goal) }, goal.id))) })] }, group))), filteredGoals.length > 0 ? finishedCount : null, visibleGoals.length < filteredGoals.length ? (navigation === undefined ? (_jsx(Button, { onClick: () => setVisibleGoalCount((count) => Math.min(count + initialVisibleGoalCount, filteredGoals.length)), children: "Load 10 more" })) : (_jsx("a", { className: "work-load-more-link", href: navigation({
474
583
  goalId: encodeWorkBoardNavigationGoal({
475
584
  detailsOpen,
476
585
  goalId: selectedId,
@@ -478,18 +587,6 @@ export const WorkBoard = ({ decisions, externalLinks = "enabled", initialGoalId,
478
587
  visibleGoalCount: Math.min(visibleGoalCount + initialVisibleGoalCount, filteredGoals.length)
479
588
  }),
480
589
  window
481
- }), children: "Load 10 more" }))) : null] }), selected === null || !detailsOpen ? null : (_jsx(Region, { actions: navigation === undefined ? (_jsx(Button, { onClick: () => {
482
- setDetailsOpen(false);
483
- const selectedRow = selectedRowRef.current ?? selectedLinkRowRef.current;
484
- selectedRow?.focus();
485
- }, variant: "secondary", children: "Close details" })) : (_jsx("a", { className: "work-exact-link", href: withFragment(navigation({
486
- goalId: encodeWorkBoardNavigationGoal({
487
- detailsOpen: false,
488
- goalId: selectedId,
489
- statusFilter,
490
- visibleGoalCount
491
- }),
492
- window
493
- }), selectedGoalFragment), children: "Close details" })), "aria-label": "Goal details", className: "work-inspector", headingId: "work-goal-details-title", id: "work-goal-details", ref: detailsRef, title: selected.title, children: _jsx(GoalDetail, { decisions: decisions, externalLinks: externalLinks, goal: selected, snapshot: snapshot }) }))] }))] }));
590
+ }), children: "Load 10 more" }))) : null] })] }))] }));
494
591
  };
495
592
  //# sourceMappingURL=view.js.map