@knpkv/herdr-work 0.10.0 → 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;AAGnB,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;AAspB5D,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,YA8VH,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,16 +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";
5
- import { activityShownOf, finishedNotShown, goalsNotShown } from "./model.js";
6
+ import { activityShownOf } from "./model.js";
6
7
  import { decodeWorkBoardNavigationGoal, encodeWorkBoardNavigationGoal } from "./navigation.js";
7
8
  import { workRequestClockText, workRequestDecidability } from "./request-decision.js";
8
9
  import { displayStateOf, observedFor } from "./display-state.js";
9
- import { workTriage, workTriageGroups, workTriageGroupTitle, workTriageSentence } from "./work-triage.js";
10
+ import { workGoalFinishedAt, workTriage, workTriageGroups, workTriageGroupTitle, workTriageSentence } from "./work-triage.js";
10
11
  /** The request clock's wording ("52s", "4m 12s", "11m", "expiring"), shared with hosts that show the same clocks. */
11
12
  export { workRequestClockText } from "./request-decision.js";
12
13
  const windows = ["now", "day", "week", "month"];
13
- const stageOrder = ["local", "review", "pull_request", "merged", "deployed"];
14
14
  const initialVisibleGoalCount = 10;
15
15
  const selectedGoalFragment = "work-selected-goal";
16
16
  const goalStates = [
@@ -67,17 +67,30 @@ const agentStatusLabel = {
67
67
  gone: "gone"
68
68
  };
69
69
  const deliveryLabel = {
70
- local: "Local",
71
- review: "Review",
70
+ local: "In progress",
71
+ review: "In review",
72
72
  pull_request: "Pull request",
73
73
  merged: "Merged",
74
74
  deployed: "Deployed"
75
75
  };
76
76
  const reviewPresentation = {
77
- not_requested: { label: "Not requested", tone: "neutral" },
78
- requested: { label: "Requested", tone: "caution" },
79
- changes_requested: { label: "Changes requested", tone: "critical" },
80
- 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"
81
94
  };
82
95
  const requestPresentation = {
83
96
  open: { label: "Open", tone: "caution" },
@@ -89,6 +102,17 @@ const formatTimestamp = (timestamp) => new Intl.DateTimeFormat("en-GB", {
89
102
  dateStyle: "medium",
90
103
  timeStyle: "short"
91
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
+ };
92
116
  const formatSpend = (goal) => goal.spend === null
93
117
  ? "Not recorded"
94
118
  : new Intl.NumberFormat("en", {
@@ -102,12 +126,6 @@ const blockersFor = (goal) => {
102
126
  };
103
127
  const activityFor = (goal) => goal.activity ?? [];
104
128
  const requestsFor = (goal) => goal.requests ?? [];
105
- const hierarchyLabel = (goal) => {
106
- const hierarchy = goal.agentHierarchy;
107
- if (hierarchy === undefined || hierarchy === null)
108
- return "No agent hierarchy recorded";
109
- return `${hierarchy.agent.host} / ${hierarchy.agent.name}`;
110
- };
111
129
  /** Renders the persisted approval deep link after its origin is bound at ingress. */
112
130
  const exactLink = (target, label) => (_jsx("a", { className: "work-exact-link", href: target.url, children: label }));
113
131
  /** Renders the persisted review destination without rewriting its credential-free URL. */
@@ -118,24 +136,37 @@ const reviewLabel = (review) => {
118
136
  }
119
137
  return (_jsx(StateLabel, { label: reviewPresentation[review.state].label, size: "compact", tone: reviewPresentation[review.state].tone }));
120
138
  };
121
- /** Delivery as words: past stages are done, the current one is "now" in the goal's state ink. */
122
- const stagesFor = (goal, shown) => {
123
- const current = stageOrder.indexOf(goal.delivery);
124
- return stageOrder.map((stage, index) => ({
125
- id: `${goal.id}-${stage}`,
126
- name: deliveryLabel[stage],
127
- state: index < current ? "done" : index === current ? "now" : "not yet",
128
- tone: index === current ? displayPresentation[shown].tone : "neutral"
129
- }));
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) })] })) })] })] }));
130
169
  };
131
- /**
132
- * The repository as a row names it: its last path segment ("npm" for a checkout at
133
- * /home/…/knpkv.dev/npm, or for "knpkv/npm"). The detail keeps the full value.
134
- */
135
- const repositoryName = (repository) => repository
136
- .split("/")
137
- .filter((segment) => segment.length > 0)
138
- .at(-1) ?? repository;
139
170
  const rowCaption = (goal, observed, decisions) => {
140
171
  const open = requestsFor(goal).filter(({ state }) => state === "open");
141
172
  if (open.length > 0) {
@@ -151,16 +182,14 @@ const rowCaption = (goal, observed, decisions) => {
151
182
  : `, ${workRequestClockText(soonest.expiresAt, decisions.now)}${soonest.expiresAt > decisions.now ? " left" : ""}`;
152
183
  return {
153
184
  blocking: false,
154
- text: open.length === 1
155
- ? `Needs approval: ${open[0]?.summary ?? ""}${clock}`
156
- : `${open.length} requests need approval${clock}`
185
+ text: open.length === 1 ? `${open[0]?.summary ?? ""}${clock}` : `${open.length} requests need approval${clock}`
157
186
  };
158
187
  }
159
188
  const blockers = blockersFor(goal);
160
189
  if (blockers.length > 0)
161
190
  return { blocking: true, text: blockers[0]?.summary ?? "Blocked" };
162
191
  if (observed?.stale === true && observed.agent !== null) {
163
- return { blocking: false, text: `Owner gone since ${formatTimestamp(observed.agent.observedAt)}` };
192
+ return { blocking: false, text: `Owner gone since ${formatClock(observed.agent.observedAt)}` };
164
193
  }
165
194
  if (observed?.unknown != null) {
166
195
  return {
@@ -174,24 +203,29 @@ const rowCaption = (goal, observed, decisions) => {
174
203
  return { blocking: false, text: `${goal.owner.name}, ${deliveryLabel[goal.delivery].toLowerCase()}` };
175
204
  };
176
205
  /** The caption under the hero: the request waiting longest, or the latest change. */
177
- const summaryCaption = (summary, tense) => {
206
+ const summaryCaption = (summary, tense, snapshot) => {
178
207
  switch (summary._tag) {
179
208
  case "Attention":
180
209
  return summary.oldestRequest === null
181
210
  ? undefined
182
- : `${tense === "present" ? "Waiting longest" : "Waiting longest then"}: ${summary.oldestRequest.request.summary} on ${summary.oldestRequest.goal.title}, requested ${formatTimestamp(summary.oldestRequest.request.requestedAt)}.`;
183
- case "Clear":
184
- return summary.latest === null
185
- ? undefined
186
- : `${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
+ }
187
221
  case "Empty":
188
- return "Delegate work to an agent and its goal appears here once the hub admits it.";
222
+ return undefined;
189
223
  }
190
224
  };
191
225
  /**
192
226
  * What the timeline shows for one goal, newest first: the owner's recorded activity, plus what the
193
227
  * reconciler observed (a pull request, the owner's agent) and any source it could not read. Observed
194
- * 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.
195
229
  */
196
230
  const timelineFor = (goal, observed) => {
197
231
  const recorded = activityFor(goal).map((entry) => ({
@@ -199,7 +233,7 @@ const timelineFor = (goal, observed) => {
199
233
  dateTime: new Date(entry.occurredAt).toISOString(),
200
234
  detail: activityKindLabel[entry.kind],
201
235
  id: entry.id,
202
- time: formatTimestamp(entry.occurredAt),
236
+ time: formatClock(entry.occurredAt),
203
237
  title: entry.summary
204
238
  }));
205
239
  const seen = [];
@@ -212,7 +246,7 @@ const timelineFor = (goal, observed) => {
212
246
  detail: `${checksLabel[fact.checks]}, last read ${formatTimestamp(confirmedAt)}`,
213
247
  id: `observed-pull-request-${fact.repository}-${fact.pullRequest}`,
214
248
  provenance: { kind: "auto", label: "Observed on GitHub" },
215
- time: formatTimestamp(at),
249
+ time: formatClock(at),
216
250
  title: `Pull request #${fact.pullRequest} ${pullRequestStateLabel[fact.state]}`
217
251
  });
218
252
  }
@@ -224,7 +258,7 @@ const timelineFor = (goal, observed) => {
224
258
  detail: `${fact.host}, last read ${formatTimestamp(confirmedAt)}`,
225
259
  id: `observed-agent-${fact.host}-${fact.agentId}`,
226
260
  provenance: { kind: "auto", label: "Observed in herdr" },
227
- time: formatTimestamp(observedAt),
261
+ time: formatClock(observedAt),
228
262
  title: `Agent ${agentStatusLabel[fact.status]}`
229
263
  });
230
264
  }
@@ -239,13 +273,20 @@ const timelineFor = (goal, observed) => {
239
273
  kind: "unknown",
240
274
  label: lastGoodAt === null ? "Never read" : `Last good read ${formatTimestamp(lastGoodAt)}`
241
275
  },
242
- time: formatTimestamp(since),
276
+ time: formatClock(since),
243
277
  title: `Couldn't read ${sourceLabel[source]}`
244
278
  });
245
279
  }
246
280
  return [...recorded, ...seen].toSorted((left, right) => Date.parse(right.dateTime) - Date.parse(left.dateTime));
247
281
  };
248
- 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
+ }
249
290
  const decidability = workRequestDecidability(request, decisions);
250
291
  if (decidability._tag === "Elsewhere" || decisions === undefined)
251
292
  return { _tag: "Elsewhere" };
@@ -284,17 +325,21 @@ const requestDecisionFor = (request, decisions) => {
284
325
  };
285
326
  };
286
327
  /** One request in a goal's detail: its title once, then the bar, the hub link or the outcome. */
287
- const RequestItem = ({ decisions, externalLinks, request }) => {
288
- 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");
289
330
  const presentation = requestPresentation[request.state];
290
- 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 : "" }))] }));
291
332
  };
292
333
  /** Everything about one goal, in reading order: what it is, where it is, what blocks it, what happened. */
293
- const GoalDetail = ({ decisions, externalLinks, goal, snapshot }) => {
334
+ const GoalDetail = ({ closeControl, decisions, externalLinks, goal, snapshot }) => {
294
335
  const family = familyForGoal(snapshot, goal.id);
295
336
  const observed = observedFor(snapshot, goal.id);
296
337
  const shown = observed?.displayState ?? goal.state;
297
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}`;
298
343
  // Only the live window carries the overlay; there "nothing observed" is itself a reading.
299
344
  const overlay = snapshot.observed !== undefined;
300
345
  // A goal-level approval target (older checkpoints carry one without requests); skipped when a
@@ -304,16 +349,23 @@ const GoalDetail = ({ decisions, externalLinks, goal, snapshot }) => {
304
349
  requestsFor(goal).some(({ approvalTarget }) => approvalTarget?.url === goal.approvalTarget?.url)
305
350
  ? null
306
351
  : goal.approvalTarget;
307
- 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
308
- ? observed === null && snapshot.observedOmitted !== undefined
309
- ? "Not in this read: live state was trimmed to the most recently updated goals"
310
- : "None known"
311
- : `#${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
312
360
  ? null
313
- : `, ${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 ===
314
- "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
315
362
  ? "It has never been read, so nothing here comes from it."
316
- : `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" }), 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, 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] })] }));
317
369
  };
318
370
  const activityKindLabel = {
319
371
  note: "Note",
@@ -326,25 +378,19 @@ const activityKindLabel = {
326
378
  const snapshotFor = (snapshots, window) => snapshots[window];
327
379
  /** How many older activities the snapshot left out of this goal's timeline. */
328
380
  const activityOmittedFor = (snapshot, goal) => snapshot.activityOmitted?.[goal.id] ?? 0;
329
- /**
330
- * The board header's line, as whole sentences joined by a space, so each piece ends in exactly one period:
331
- * the window and its time, then what the live window could not show, then any finished or older goals it left out.
332
- */
333
- const headerSentences = (window, snapshot) => {
334
- const when = `${window === "now" ? "Live" : windowLabel[window]}, as of ${formatTimestamp(snapshot.asOf)}.`;
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)}.`;
335
384
  const live = window !== "now"
336
385
  ? []
337
386
  : snapshot.observed === undefined
338
387
  ? [
339
- "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.",
340
- "Update herdr-work on the hub to see them."
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."
341
389
  ]
342
390
  : snapshot.observedOmitted === undefined
343
391
  ? []
344
392
  : [`Live state shown for the most recently updated goals; ${String(snapshot.observedOmitted)} left out.`];
345
- const finished = snapshot.finishedOmitted === undefined ? [] : [`${finishedNotShown(snapshot.finishedOmitted)}.`];
346
- const cut = snapshot.goalsOmitted === undefined ? [] : [`${goalsNotShown(snapshot.goalsOmitted)}.`];
347
- return [when, ...live, ...finished, ...cut];
393
+ return [when, ...live];
348
394
  };
349
395
  const familyForGoal = (snapshot, goalId) => (snapshot.families ?? []).find((group) => group.canonicalGoalId === goalId) ?? null;
350
396
  const familyLabelForGoal = (snapshot, goal) => {
@@ -358,7 +404,7 @@ const withFragment = (href, fragment) => {
358
404
  const fragmentIndex = href.indexOf("#");
359
405
  return `${fragmentIndex === -1 ? href : href.slice(0, fragmentIndex)}#${fragment}`;
360
406
  };
361
- export const WorkBoard = ({ decisions, externalLinks = "enabled", initialGoalId, initialWindow = "now", navigation, snapshots }) => {
407
+ export const WorkBoard = ({ decisions, externalLinks = "enabled", host, initialGoalId, initialWindow = "now", navigation, snapshots }) => {
362
408
  const [window, setWindow] = useState(initialWindow);
363
409
  const snapshot = snapshotFor(snapshots, window);
364
410
  const directInitialGoal = snapshot.goals.find(({ id }) => id === initialGoalId);
@@ -381,6 +427,9 @@ export const WorkBoard = ({ decisions, externalLinks = "enabled", initialGoalId,
381
427
  const selectedLinkRowRef = useRef(null);
382
428
  const selectedRowRef = useRef(null);
383
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;
384
433
  // The overlay by goal, built once per snapshot: rows, captions and filters all read it.
385
434
  const overlay = new Map((snapshot.observed ?? []).map((entry) => [entry.goalId, entry]));
386
435
  const observedOf = (goal) => overlay.get(goal.id) ?? null;
@@ -388,9 +437,7 @@ export const WorkBoard = ({ decisions, externalLinks = "enabled", initialGoalId,
388
437
  // A historical window is the state as of its time; say so, never in the present tense.
389
438
  const tense = window === "now" ? "present" : "past";
390
439
  const sentence = workTriageSentence(triage.summary, tense);
391
- const heroFact = tense === "present"
392
- ? sentence
393
- : `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);
394
441
  const groupById = new Map(triage.rows.map((row) => [row.goal.id, row.group]));
395
442
  const ordered = triage.rows.map((row) => row.goal);
396
443
  // Filters match what each row shows: the observed state where there is one.
@@ -423,9 +470,25 @@ export const WorkBoard = ({ decisions, externalLinks = "enabled", initialGoalId,
423
470
  setVisibleGoalCount(initialVisibleGoalCount);
424
471
  }, [selectedFilteredGoalIndex, selectedId]);
425
472
  const goalRow = (goal) => {
426
- const caption = rowCaption(goal, observedOf(goal), externalLinks === "enabled" ? decisions : undefined);
427
- 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)}`] })] }));
428
- 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: () => {
429
492
  setDetailsOpen(true);
430
493
  setSelectedId(goal.id);
431
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({
@@ -439,6 +502,7 @@ export const WorkBoard = ({ decisions, externalLinks = "enabled", initialGoalId,
439
502
  }),
440
503
  window
441
504
  }), id: selected?.id === goal.id ? selectedGoalFragment : undefined, ref: selected?.id === goal.id ? selectedLinkRowRef : undefined, children: content }));
505
+ return control;
442
506
  };
443
507
  const timeTravel = (_jsx("div", { "aria-label": "Choose work snapshot", className: "work-time-controls", role: "group", children: windows.map((option) => {
444
508
  if (navigation === undefined) {
@@ -472,12 +536,12 @@ export const WorkBoard = ({ decisions, externalLinks = "enabled", initialGoalId,
472
536
  }) }));
473
537
  const statusFilterControls = (_jsx("div", { "aria-label": "Filter goals by status", className: "work-status-filters", role: "group", children: statusFilters.map((state) => {
474
538
  const label = state === "all" ? "All" : statePresentation[state].label;
475
- 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: () => {
476
540
  setDetailsOpen(false);
477
541
  setSelectedId(null);
478
542
  setStatusFilter(state);
479
543
  setVisibleGoalCount(initialVisibleGoalCount);
480
- }, 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({
481
545
  goalId: encodeWorkBoardNavigationGoal({
482
546
  detailsOpen: false,
483
547
  goalId: null,
@@ -487,7 +551,35 @@ export const WorkBoard = ({ decisions, externalLinks = "enabled", initialGoalId,
487
551
  window
488
552
  }), children: label }, state));
489
553
  }) }));
490
- 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" }), _jsx(Text, { tone: "secondary", variant: "meta", children: headerSentences(window, snapshot).join(" ") })] }), _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({
491
583
  goalId: encodeWorkBoardNavigationGoal({
492
584
  detailsOpen,
493
585
  goalId: selectedId,
@@ -495,18 +587,6 @@ export const WorkBoard = ({ decisions, externalLinks = "enabled", initialGoalId,
495
587
  visibleGoalCount: Math.min(visibleGoalCount + initialVisibleGoalCount, filteredGoals.length)
496
588
  }),
497
589
  window
498
- }), children: "Load 10 more" }))) : null] }), selected === null || !detailsOpen ? null : (_jsx(Region, { actions: navigation === undefined ? (_jsx(Button, { onClick: () => {
499
- setDetailsOpen(false);
500
- const selectedRow = selectedRowRef.current ?? selectedLinkRowRef.current;
501
- selectedRow?.focus();
502
- }, variant: "secondary", children: "Close details" })) : (_jsx("a", { className: "work-exact-link", href: withFragment(navigation({
503
- goalId: encodeWorkBoardNavigationGoal({
504
- detailsOpen: false,
505
- goalId: selectedId,
506
- statusFilter,
507
- visibleGoalCount
508
- }),
509
- window
510
- }), 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] })] }))] }));
511
591
  };
512
592
  //# sourceMappingURL=view.js.map