@anthusai/papyrus 1.3.0 → 1.4.0-next.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (141) hide show
  1. package/amplify/data/resource.js +134 -3
  2. package/amplify/data/resource.ts +145 -3
  3. package/amplify/data/schema.js +131 -0
  4. package/amplify/data/schema.ts +142 -0
  5. package/amplify/functions/category-action/handler.ts +272 -20
  6. package/amplify/functions/console-chat-responder/src/main.rs +1 -1
  7. package/amplify/functions/content-actions/handler.py +23 -0
  8. package/amplify/functions/content-actions/requirements.txt +1 -1
  9. package/amplify/functions/content-actions/resource.js +1 -1
  10. package/amplify/functions/content-actions/resource.ts +1 -1
  11. package/amplify/functions/model-attachment-upload/handler.ts +1 -0
  12. package/amplify/functions/renditions/requirements.txt +1 -1
  13. package/amplify/functions/shared/curation-delegation-flow.ts +147 -0
  14. package/amplify/functions/shared/curation-delegation.ts +115 -0
  15. package/amplify/functions/shared/python-bundle.js +3 -3
  16. package/amplify/functions/shared/python-bundle.ts +3 -3
  17. package/amplify/functions/shared/reference-curation-metadata.ts +51 -0
  18. package/amplify/functions/shared/slack-events.ts +2 -2
  19. package/amplify/publication/renditions-function.js +1 -1
  20. package/amplify/publication/renditions-function.ts +1 -1
  21. package/app/[year]/[month]/[day]/[articleSlug]/page.tsx +2 -1
  22. package/app/[year]/[month]/[day]/edition-route-page.tsx +2 -1
  23. package/app/[year]/[month]/[day]/page/[pageNumber]/page.tsx +2 -1
  24. package/app/[year]/[month]/[day]/page.tsx +3 -0
  25. package/app/[year]/[month]/[day]/section/[sectionKey]/page.tsx +2 -0
  26. package/app/api/archive/editions/route.ts +2 -0
  27. package/app/api/auth/prospectus/accounts/route.ts +9 -0
  28. package/app/api/local-preview/derive/route.ts +28 -0
  29. package/app/api/local-preview/media/route.ts +25 -0
  30. package/app/archive/page.tsx +3 -0
  31. package/app/globals.css +28 -28
  32. package/app/layout.tsx +8 -5
  33. package/app/newsroom/[section]/[...selection]/page.tsx +0 -1
  34. package/app/newsroom/[section]/page.tsx +8 -1
  35. package/app/newsroom/articles/page.tsx +10 -0
  36. package/app/newsroom/insights/[threadId]/page.tsx +5 -16
  37. package/app/newsroom/insights/page.tsx +4 -12
  38. package/app/newsroom/sections/[sectionId]/page.tsx +3 -0
  39. package/app/page.tsx +30 -1
  40. package/components/administration-delegations-panel.tsx +156 -0
  41. package/components/curation-delegation-client.ts +104 -0
  42. package/components/item-stream-view.tsx +63 -0
  43. package/components/markus-ir-preview.tsx +47 -2
  44. package/components/news-desk-client-provider.tsx +17 -2
  45. package/components/news-desk-page.tsx +0 -1
  46. package/components/news-desk-taxonomy-client.ts +154 -240
  47. package/components/newspaper.tsx +4 -4
  48. package/components/newsroom-article-editor.tsx +138 -40
  49. package/components/newsroom-assignment-detail.tsx +79 -0
  50. package/components/newsroom-assignments-view.tsx +1 -1
  51. package/components/newsroom-board-view.tsx +153 -43
  52. package/components/newsroom-client-shell.tsx +8 -1
  53. package/components/newsroom-newsprint-board.css +5 -46
  54. package/components/newsroom-ops-overview.tsx +27 -5
  55. package/components/newsroom-organizer-shell.tsx +36 -13
  56. package/components/newsroom-organizer.css +269 -13
  57. package/components/newsroom-references-view.tsx +118 -23
  58. package/components/newsroom-topics-view.tsx +1 -1
  59. package/components/priming-proof-page.tsx +17 -8
  60. package/components/prospectus-account-chooser.tsx +131 -0
  61. package/components/reference-cyclotron-priming.tsx +85 -0
  62. package/components/reference-cyclotron-status.tsx +5 -5
  63. package/components/reference-cyclotron-suggestion.tsx +143 -0
  64. package/components/reference-live-agreement.tsx +62 -0
  65. package/components/reference-section-suggestion.tsx +104 -0
  66. package/components/story-reporting-trail-client.ts +91 -0
  67. package/components/story-reporting-trail.tsx +115 -0
  68. package/components/topic-steering-workspace.tsx +280 -679
  69. package/components/ui/sheet.tsx +10 -4
  70. package/components/use-paged-content-item-versions.ts +151 -0
  71. package/components/wiki-concepts-view.tsx +153 -0
  72. package/components/wiki-index-client.ts +51 -0
  73. package/infra/amplify-app-shell.js +17 -3
  74. package/infra/amplify-app-shell.ts +18 -3
  75. package/infra/site-config.js +6 -1
  76. package/infra/site-config.ts +7 -1
  77. package/lib/approved-priming-proof-reports.ts +6 -3
  78. package/lib/approved-priming-proofs/papyrus.json +90 -0
  79. package/lib/article-flow.ts +95 -0
  80. package/lib/article-frame-solver.ts +519 -0
  81. package/lib/blog-feature-solver.ts +1 -109
  82. package/lib/category-repository.ts +1 -1
  83. package/lib/chrome-text.ts +504 -0
  84. package/lib/content-actions-client.ts +3 -0
  85. package/lib/content-actions-demo.ts +55 -0
  86. package/lib/content-item-lifecycle.ts +34 -5
  87. package/lib/content-source-context.ts +9 -1
  88. package/lib/curation-delegation-helpers.ts +69 -0
  89. package/lib/cyclotron-suggestions.ts +263 -0
  90. package/lib/front-matter-fields.ts +42 -0
  91. package/lib/front-mosaic.ts +1133 -0
  92. package/lib/furniture.ts +385 -0
  93. package/lib/graphql-content-repository.ts +42 -7
  94. package/lib/item-stream.ts +47 -0
  95. package/lib/labels.ts +37 -0
  96. package/lib/layout-config.ts +237 -0
  97. package/lib/layout-plan.ts +40 -11
  98. package/lib/layout-scenarios.ts +21 -11
  99. package/lib/layout-types.ts +457 -0
  100. package/lib/layout-warnings.ts +8 -0
  101. package/lib/local-preview-section-fixtures.ts +72 -0
  102. package/lib/local-preview-seed.ts +286 -0
  103. package/lib/local-preview-tenant.ts +27 -0
  104. package/lib/local-preview.ts +3 -0
  105. package/lib/newspaper-layout.ts +17 -3410
  106. package/lib/newsroom-articles.ts +2 -0
  107. package/lib/newsroom-assignments.ts +14 -1
  108. package/lib/newsroom-board.ts +99 -47
  109. package/lib/newsroom-demo-dashboard.ts +1 -1
  110. package/lib/newsroom-forum-routes.ts +2 -43
  111. package/lib/newsroom-index-filters.ts +3 -40
  112. package/lib/newsroom-media-backend.ts +5 -0
  113. package/lib/newsroom-nav.ts +1 -3
  114. package/lib/newsroom-organizer-nav.ts +9 -16
  115. package/lib/newsroom-section-routing.ts +1 -1
  116. package/lib/newsroom-sections.ts +12 -0
  117. package/lib/papyrus-web-locations.ts +3 -24
  118. package/lib/pretext-layout.ts +22 -0
  119. package/lib/priming-proof-report.ts +1 -0
  120. package/lib/prospectus-account-client.ts +90 -0
  121. package/lib/prospectus-sign-in-routes.ts +45 -0
  122. package/lib/prospectus-site-identity.ts +13 -1
  123. package/lib/prospectus-tenant.ts +22 -0
  124. package/lib/reader-static-params.ts +2 -1
  125. package/lib/relevance-decisions.ts +29 -0
  126. package/lib/relevance-status.ts +73 -0
  127. package/lib/reporting-story-budget.ts +2 -2
  128. package/lib/semantic-graph.ts +5 -5
  129. package/lib/site-brand.ts +28 -1
  130. package/lib/site-env.ts +5 -0
  131. package/lib/site-section-suggestions.ts +120 -0
  132. package/lib/site-stack.ts +1 -1
  133. package/lib/story-reporting-trail.ts +106 -0
  134. package/lib/tenant-keys.ts +32 -0
  135. package/lib/text-columns.ts +251 -0
  136. package/lib/wiki.ts +56 -0
  137. package/package.json +46 -3
  138. package/renderers/pretext/presentation-shell.tsx +1 -1
  139. package/renderers/pretext/rhythm-article-page.tsx +1 -18
  140. package/routes.manifest.json +34 -15
  141. package/app/newsroom/work/page.tsx +0 -10
@@ -1,5 +1,7 @@
1
1
  "use client";
2
2
 
3
+ import { AdministrationDelegationsPanel } from "./administration-delegations-panel";
4
+ import { WikiConceptsView } from "./wiki-concepts-view";
3
5
  import { Hub } from "aws-amplify/utils";
4
6
  import { fetchAuthSession } from "aws-amplify/auth";
5
7
  import { gsap } from "gsap";
@@ -34,17 +36,13 @@ import {
34
36
  loadEditorProcedureData,
35
37
  loadEditorSemanticRelationsData,
36
38
  loadEditionForumThreads,
37
- loadInsightForumThreadById,
38
- loadInsightForumThreads,
39
39
  loadNewsroomAssignmentPage,
40
40
  loadNewsroomMessagePage,
41
41
  loadNewsroomReferencePage,
42
42
  loadNewsroomSemanticNodePage,
43
43
  appendForumThreadMessageRecord,
44
- appendInsightThreadReplyRecord,
45
44
  createSectionForumThreadRecord,
46
45
  deleteForumThreadMessageRecord,
47
- deleteInsightThreadMessageRecord,
48
46
  ensureEditionForumThreadRecord,
49
47
  publishProcedureVersionRecord,
50
48
  runNewsroomKnowledgeQuery,
@@ -57,6 +55,10 @@ import {
57
55
  type KnowledgeQueryResponse,
58
56
  type NewsroomRecordPage,
59
57
  loadCyclotronStatus,
58
+ loadCyclotronShadowAgreement,
59
+ loadSiteSectionAgreement,
60
+ requestCyclotronPriming,
61
+ loadCyclotronPrimingRequests,
60
62
  requestCyclotronReviewRate,
61
63
  } from "./news-desk-taxonomy-client";
62
64
  import { listConsoleThreads } from "../lib/console-chat-client";
@@ -66,11 +68,8 @@ import {
66
68
  effectiveReferencesIndexFilters,
67
69
  normalizeReferenceIndexOrder,
68
70
  readAssignmentsIndexFilters,
69
- readInsightsIndexFilters,
70
71
  readMessagesIndexFilters,
71
72
  readReferencesIndexFilters,
72
- effectiveInsightsIndexFilters,
73
- parseInsightThreadIdFromNewsroomPathname,
74
73
  referencesStatusFromUrl,
75
74
  referencesStatusToUrl,
76
75
  type ReferenceIndexOrder,
@@ -160,8 +159,19 @@ import {
160
159
  REFERENCE_REJECTION_REASON_CODES,
161
160
  referenceCurationStatusForAction,
162
161
  } from "../lib/reference-policy";
162
+ import { isLocalPreviewEnabled } from "../lib/local-preview";
163
163
  import { currentRelevanceDecisions } from "../lib/relevance-decisions";
164
- import { buildReviewRateRequest, DEMO_CYCLOTRON_STATUS } from "../lib/relevance-status";
164
+ import { delegatedCurationsByReference, referencesWithHumanLatestCuration, type ShadowAgreement } from "../lib/cyclotron-suggestions";
165
+ import { createLocalPreviewCyclotronFixtures } from "../lib/local-preview-seed";
166
+ import { createLocalPreviewSectionFixtures } from "../lib/local-preview-section-fixtures";
167
+ import { currentSectionSuggestions, siteCyclotronIdOf, type SiteSectionAgreement } from "../lib/site-section-suggestions";
168
+ import {
169
+ buildPrimingRequest,
170
+ buildReviewRateRequest,
171
+ DEFAULT_RELEVANCE_CYCLOTRON,
172
+ DEMO_CYCLOTRON_STATUS,
173
+ type PrimingRequestView,
174
+ } from "../lib/relevance-status";
165
175
  import type { CyclotronStatus } from "cyclotron/cyclotron-status";
166
176
  import {
167
177
  referenceDisplaySummary,
@@ -193,12 +203,10 @@ import {
193
203
  } from "../lib/reporting-story-budget";
194
204
  import {
195
205
  buildForumThreadUrl,
196
- buildInsightThreadUrl,
197
206
  getForumMessageAnchorId,
198
207
  isForumThreadId,
199
208
  parseForumMessageAnchorFromHash,
200
209
  pushForumThreadUrl,
201
- pushInsightThreadUrl,
202
210
  readCurrentForumRoute,
203
211
  } from "../lib/newsroom-forum-routes";
204
212
 
@@ -238,8 +246,8 @@ type AssignmentAction = "claim" | "release" | "complete" | "cancel" | "reopen" |
238
246
  type ReportingPacketReviewDecision = "select" | "merge" | "brief" | "hold" | "kill";
239
247
  type AssignmentDeskViewMode = "queue" | "budget";
240
248
  type UserRoleAction = "grant" | "revoke";
241
- type AdministrationPanel = "users" | "policies" | "sections" | "procedures" | "signups";
242
- export type NewsDeskTab = "overview" | "desks" | "topics" | "concepts" | "references" | "insights" | "messages" | "assignments" | "administration" | "search";
249
+ type AdministrationPanel = "users" | "policies" | "sections" | "procedures" | "delegations" | "signups";
250
+ export type NewsDeskTab = "overview" | "desks" | "topics" | "concepts" | "references" | "messages" | "assignments" | "administration" | "search";
243
251
  type LexicalRuleScope = "publication" | "corpus" | "classifier" | "category";
244
252
  type AnalysisReindexMode = "online-update" | "classifier-retrain" | "scoped-topic-rebuild" | "entity-graph-rebuild" | "generated-analysis-rebuild";
245
253
  type ReferenceProcessingStatus = "created" | "processable" | "processed" | "blocked";
@@ -316,7 +324,7 @@ type NewsroomDetailAction = {
316
324
  onSelect: () => void;
317
325
  };
318
326
 
319
- type InsightTarget = {
327
+ type NoteTarget = {
320
328
  kind: "assignment" | "category" | "item" | "newsroomSection" | "reference" | "semanticNode";
321
329
  id: string;
322
330
  lineageId: string;
@@ -325,7 +333,7 @@ type InsightTarget = {
325
333
  versionNumber?: number | null;
326
334
  };
327
335
 
328
- type InsightComposerControl = {
336
+ type NoteComposerControl = {
329
337
  action: NewsroomDetailAction;
330
338
  dialog: ReactNode;
331
339
  error: string | null;
@@ -452,7 +460,7 @@ type ReferenceQualityResponse = {
452
460
  errors?: Array<{ message?: string | null } | string | null> | null;
453
461
  };
454
462
 
455
- type ReferenceInsightResponse = {
463
+ type ReferenceNoteResponse = {
456
464
  data?: {
457
465
  ok?: boolean | null;
458
466
  referenceId?: string | null;
@@ -784,11 +792,43 @@ function NewsDeskDashboard({
784
792
  const [referenceAttachments, setReferenceAttachments] = useState(dashboard.referenceAttachments);
785
793
  const [messages, setMessages] = useState(dashboard.messages);
786
794
  const [semanticRelations, setSemanticRelations] = useState(dashboard.semanticRelations);
787
- const relevanceDecisions = useMemo(() => currentRelevanceDecisions(semanticRelations), [semanticRelations]);
795
+ const cyclotronPreviewFixtures = useMemo(
796
+ () => (isLocalPreviewEnabled() ? createLocalPreviewCyclotronFixtures(references) : null),
797
+ [references],
798
+ );
799
+ const relevanceDecisions = useMemo(
800
+ () => currentRelevanceDecisions(cyclotronPreviewFixtures ? [...semanticRelations, ...cyclotronPreviewFixtures.relations] : semanticRelations),
801
+ [cyclotronPreviewFixtures, semanticRelations],
802
+ );
803
+ const delegatedCurations = useMemo(
804
+ () => delegatedCurationsByReference(cyclotronPreviewFixtures ? [...messages, ...cyclotronPreviewFixtures.messages] : messages),
805
+ [cyclotronPreviewFixtures, messages],
806
+ );
807
+ const humanCuratedReferenceLineageIds = useMemo(
808
+ () => referencesWithHumanLatestCuration(cyclotronPreviewFixtures ? [...messages, ...cyclotronPreviewFixtures.messages] : messages),
809
+ [cyclotronPreviewFixtures, messages],
810
+ );
788
811
  const relevanceCyclotronId = useMemo(
789
812
  () => [...relevanceDecisions.values()].find((decision) => decision.cyclotronId)?.cyclotronId ?? null,
790
813
  [relevanceDecisions],
791
814
  );
815
+ const primingCyclotronId = relevanceCyclotronId ?? DEFAULT_RELEVANCE_CYCLOTRON.id;
816
+ const primingLabels = useMemo(
817
+ () => [...relevanceDecisions.values()].find((decision) => decision.classes.length)?.classes
818
+ ?? DEFAULT_RELEVANCE_CYCLOTRON.labels,
819
+ [relevanceDecisions],
820
+ );
821
+ const [primingRequests, setPrimingRequests] = useState<PrimingRequestView[]>([]);
822
+ useEffect(() => {
823
+ if (dashboard.isDemo) return;
824
+ let cancelled = false;
825
+ const refresh = () => loadCyclotronPrimingRequests(primingCyclotronId)
826
+ .then((views) => { if (!cancelled) setPrimingRequests(views); })
827
+ .catch(() => { if (!cancelled) setPrimingRequests([]); });
828
+ void refresh();
829
+ const timer = window.setInterval(() => { void refresh(); }, 30_000);
830
+ return () => { cancelled = true; window.clearInterval(timer); };
831
+ }, [dashboard.isDemo, primingCyclotronId]);
792
832
  const [cyclotronStatus, setCyclotronStatus] = useState<CyclotronStatus | null>(null);
793
833
  useEffect(() => {
794
834
  if (!relevanceCyclotronId) return;
@@ -802,6 +842,19 @@ function NewsDeskDashboard({
802
842
  .catch(() => { if (!cancelled) setCyclotronStatus(null); });
803
843
  return () => { cancelled = true; };
804
844
  }, [dashboard.isDemo, relevanceCyclotronId]);
845
+ const [shadowAgreement, setShadowAgreement] = useState<ShadowAgreement | null>(null);
846
+ useEffect(() => {
847
+ if (cyclotronPreviewFixtures) {
848
+ setShadowAgreement(cyclotronPreviewFixtures.shadowAgreement);
849
+ return;
850
+ }
851
+ if (!relevanceCyclotronId || dashboard.isDemo) return;
852
+ let cancelled = false;
853
+ loadCyclotronShadowAgreement(relevanceCyclotronId)
854
+ .then((loaded) => { if (!cancelled) setShadowAgreement(loaded); })
855
+ .catch(() => { if (!cancelled) setShadowAgreement(null); });
856
+ return () => { cancelled = true; };
857
+ }, [cyclotronPreviewFixtures, dashboard.isDemo, relevanceCyclotronId]);
805
858
  const [semanticNodes, setSemanticNodes] = useState(dashboard.semanticNodes);
806
859
  const [assignments, setAssignments] = useState(dashboard.assignments);
807
860
  const [assignmentEvents, setAssignmentEvents] = useState(dashboard.assignmentEvents);
@@ -812,6 +865,35 @@ function NewsDeskDashboard({
812
865
  [dashboard.newsroomSections],
813
866
  );
814
867
  const [newsroomSections, setNewsroomSections] = useState(fallbackNewsroomSections);
868
+ const sectionChoices = useMemo(
869
+ () => newsroomSections
870
+ .filter((section) => section.enabled !== false && section.enabledStatus !== "disabled")
871
+ .sort((left, right) => left.sortOrder - right.sortOrder)
872
+ .map((section) => ({ id: section.id, title: section.title })),
873
+ [newsroomSections],
874
+ );
875
+ const sectionPreviewFixtures = useMemo(
876
+ () => (isLocalPreviewEnabled() ? createLocalPreviewSectionFixtures(references, sectionChoices.map((section) => section.id)) : null),
877
+ [references, sectionChoices],
878
+ );
879
+ const sectionSuggestions = useMemo(
880
+ () => currentSectionSuggestions(sectionPreviewFixtures ? [...semanticRelations, ...sectionPreviewFixtures.relations] : semanticRelations),
881
+ [sectionPreviewFixtures, semanticRelations],
882
+ );
883
+ const siteCyclotronId = useMemo(() => siteCyclotronIdOf(sectionSuggestions), [sectionSuggestions]);
884
+ const [siteSectionAgreement, setSiteSectionAgreement] = useState<SiteSectionAgreement | null>(null);
885
+ useEffect(() => {
886
+ if (sectionPreviewFixtures) {
887
+ setSiteSectionAgreement(sectionPreviewFixtures.agreement);
888
+ return;
889
+ }
890
+ if (!siteCyclotronId || dashboard.isDemo) return;
891
+ let cancelled = false;
892
+ loadSiteSectionAgreement(siteCyclotronId)
893
+ .then((loaded) => { if (!cancelled) setSiteSectionAgreement(loaded); })
894
+ .catch(() => { if (!cancelled) setSiteSectionAgreement(null); });
895
+ return () => { cancelled = true; };
896
+ }, [dashboard.isDemo, sectionPreviewFixtures, siteCyclotronId]);
815
897
  const [procedureDefinitions, setProcedureDefinitions] = useState(dashboard.procedureDefinitions ?? []);
816
898
  const [procedureVersions, setProcedureVersions] = useState(dashboard.procedureVersions ?? []);
817
899
  const [procedureRuns, setProcedureRuns] = useState(dashboard.procedureRuns ?? []);
@@ -902,9 +984,6 @@ function NewsDeskDashboard({
902
984
  overview: 0,
903
985
  desks: summaryCountFromRecord(summary, "categories"),
904
986
  messages: summaryCountFromRecord(summary, "messages"),
905
- insights: summary?.facets?.messages?.byKind?.insight
906
- ?? summary?.messageKindCounts?.insight
907
- ?? null,
908
987
  assignments: summaryCountFromRecord(summary, "assignments"),
909
988
  references: summaryCountFromRecord(summary, "references")
910
989
  ?? (hasHydratedReferences ? references.length : null),
@@ -1539,6 +1618,7 @@ function NewsDeskDashboard({
1539
1618
  note?: string,
1540
1619
  reasonCode?: ReferenceRejectionReasonCode | null,
1541
1620
  relevance?: { decisionRelationId: string; shareable: boolean },
1621
+ sectionId?: string | null,
1542
1622
  ) {
1543
1623
  const nextStatus = referenceCurationStatusForAction(action);
1544
1624
  setActionState({ id: reference.id, message: `${action} pending`, tone: "pending" });
@@ -1568,6 +1648,7 @@ function NewsDeskDashboard({
1568
1648
  curationStatus: nextStatus,
1569
1649
  reasonCode: action === "reject" ? reasonCode ?? null : null,
1570
1650
  ...(relevance ? { decisionRelationId: relevance.decisionRelationId, shareable: relevance.shareable } : {}),
1651
+ ...(action === "accept" && sectionId ? { sectionId } : {}),
1571
1652
  },
1572
1653
  }, ...current]);
1573
1654
  setSemanticRelations((current) => [{
@@ -1610,6 +1691,7 @@ function NewsDeskDashboard({
1610
1691
  reasonCode: action === "reject" ? reasonCode ?? undefined : undefined,
1611
1692
  decisionRelationId: relevance?.decisionRelationId,
1612
1693
  shareable: relevance ? relevance.shareable : undefined,
1694
+ sectionId: action === "accept" ? sectionId ?? undefined : undefined,
1613
1695
  },
1614
1696
  { authMode: USER_POOL_AUTH_MODE },
1615
1697
  );
@@ -1870,11 +1952,11 @@ function NewsDeskDashboard({
1870
1952
  });
1871
1953
  }
1872
1954
 
1873
- async function createInsight(target: InsightTarget, summary: string, body: string): Promise<void> {
1955
+ async function createNote(target: NoteTarget, summary: string, body: string): Promise<void> {
1874
1956
  const cleanSummary = summary.trim();
1875
1957
  const cleanBody = body.trim();
1876
- if (!cleanSummary) throw new Error("Insight summary is required.");
1877
- if (!cleanBody) throw new Error("Insight body is required.");
1958
+ if (!cleanSummary) throw new Error("Note summary is required.");
1959
+ if (!cleanBody) throw new Error("Note body is required.");
1878
1960
  const now = new Date().toISOString();
1879
1961
  const subjectSeed = [
1880
1962
  target.kind,
@@ -1883,10 +1965,10 @@ function NewsDeskDashboard({
1883
1965
  cleanBody,
1884
1966
  now,
1885
1967
  ];
1886
- const messageId = `message-insight-${safeUiId(target.kind)}-${safeUiId(target.lineageId)}-${hashUiKey(subjectSeed)}`;
1968
+ const messageId = `message-note-${safeUiId(target.kind)}-${safeUiId(target.lineageId)}-${hashUiKey(subjectSeed)}`;
1887
1969
  const message: MessageRecord = {
1888
1970
  id: messageId,
1889
- messageKind: "insight",
1971
+ messageKind: "note",
1890
1972
  messageDomain: "knowledge",
1891
1973
  status: "active",
1892
1974
  summary: cleanSummary,
@@ -1904,18 +1986,18 @@ function NewsDeskDashboard({
1904
1986
  targetLineageId: target.lineageId,
1905
1987
  },
1906
1988
  };
1907
- const relation = buildUiInsightRelation(message, target, now);
1908
- setActionState({ id: messageId, message: "insight pending", tone: "pending" });
1989
+ const relation = buildUiNoteRelation(message, target, now);
1990
+ setActionState({ id: messageId, message: "note pending", tone: "pending" });
1909
1991
 
1910
1992
  if (dashboard.isDemo) {
1911
1993
  setMessages((current) => [message, ...current.filter((entry) => entry.id !== message.id)]);
1912
1994
  setSemanticRelations((current) => [relation, ...current.filter((entry) => entry.id !== relation.id)]);
1913
- setActionState({ id: messageId, message: "insight saved", tone: "ok" });
1995
+ setActionState({ id: messageId, message: "note saved", tone: "ok" });
1914
1996
  return;
1915
1997
  }
1916
1998
 
1917
1999
  if (target.kind === "reference") {
1918
- const response = await dataClient.mutations.createReferenceInsight(
2000
+ const response = await dataClient.mutations.createReferenceNote(
1919
2001
  {
1920
2002
  referenceId: target.id,
1921
2003
  summary: cleanSummary,
@@ -1924,7 +2006,7 @@ function NewsDeskDashboard({
1924
2006
  },
1925
2007
  { authMode: USER_POOL_AUTH_MODE },
1926
2008
  );
1927
- const created = assertReferenceInsightMutationSucceeded(response as ReferenceInsightResponse, target.id);
2009
+ const created = assertReferenceNoteMutationSucceeded(response as ReferenceNoteResponse, target.id);
1928
2010
  const persistedMessageId = created.messageId ?? messageId;
1929
2011
  const persistedRelationId = created.relationId ?? relation.id;
1930
2012
  const persistedMessage: MessageRecord = {
@@ -1941,7 +2023,7 @@ function NewsDeskDashboard({
1941
2023
  };
1942
2024
  setMessages((current) => [persistedMessage, ...current.filter((entry) => entry.id !== persistedMessage.id)]);
1943
2025
  setSemanticRelations((current) => [persistedRelation, ...current.filter((entry) => entry.id !== persistedRelation.id)]);
1944
- setActionState({ id: persistedMessageId, message: "insight saved", tone: "ok" });
2026
+ setActionState({ id: persistedMessageId, message: "note saved", tone: "ok" });
1945
2027
  return;
1946
2028
  }
1947
2029
 
@@ -1979,7 +2061,7 @@ function NewsDeskDashboard({
1979
2061
  assertNoGraphQLErrors(relationResponse.errors);
1980
2062
  setMessages((current) => [message, ...current.filter((entry) => entry.id !== message.id)]);
1981
2063
  setSemanticRelations((current) => [relation, ...current.filter((entry) => entry.id !== relation.id)]);
1982
- setActionState({ id: messageId, message: "insight saved", tone: "ok" });
2064
+ setActionState({ id: messageId, message: "note saved", tone: "ok" });
1983
2065
  }
1984
2066
 
1985
2067
  function createLexicalSteeringRule(draft: LexicalRuleDraft) {
@@ -3048,7 +3130,7 @@ function NewsDeskDashboard({
3048
3130
 
3049
3131
  const shellTabCounts = useMemo(() => {
3050
3132
  const entries: Partial<Record<string, NewsroomNavCount>> = {};
3051
- for (const tab of ["overview", "messages", "insights", "assignments", "references", "topics", "concepts", "administration"] as NewsDeskTab[]) {
3133
+ for (const tab of ["overview", "messages", "assignments", "references", "topics", "concepts", "administration"] as NewsDeskTab[]) {
3052
3134
  if (tab === "administration") continue;
3053
3135
  entries[tab] = {
3054
3136
  count: tabCounts[tab],
@@ -3062,7 +3144,9 @@ function NewsDeskDashboard({
3062
3144
  ? mastheadTitle
3063
3145
  : activeTab === "overview"
3064
3146
  ? "Overview"
3065
- : formatDeskSectionHeadline(activeTab);
3147
+ : activeTab === "administration" && administrationPanel === "sections"
3148
+ ? "Sections"
3149
+ : formatDeskSectionHeadline(activeTab);
3066
3150
 
3067
3151
  return (
3068
3152
  <>
@@ -3099,8 +3183,8 @@ function NewsDeskDashboard({
3099
3183
  >
3100
3184
  {activeTab !== "overview" && activeTab !== "assignments" && activeTab !== "messages" && activeTab !== "references" && activeTab !== "topics" && activeTab !== "concepts" && activeTab !== "search" ? (
3101
3185
  <NewsroomOpsSectionIntro
3102
- description={formatDeskSectionLede(activeTab)}
3103
- title={formatDeskSectionHeadline(activeTab)}
3186
+ description={activeTab === "administration" && administrationPanel === "sections" ? "Organize the publication's sections and the ontology that classifies its coverage." : formatDeskSectionLede(activeTab)}
3187
+ title={activeTab === "administration" && administrationPanel === "sections" ? "Sections" : formatDeskSectionHeadline(activeTab)}
3104
3188
  />
3105
3189
  ) : null}
3106
3190
 
@@ -3120,7 +3204,7 @@ function NewsDeskDashboard({
3120
3204
  <DeepNewsroomSectionView
3121
3205
  demo={Boolean(dashboard.isDemo)}
3122
3206
  disabled={controlsDisabled}
3123
- onCreateInsight={createInsight}
3207
+ onCreateNote={createNote}
3124
3208
  section={activeNewsroomSection}
3125
3209
  sectionId={sectionPageId ?? ""}
3126
3210
  sectionsLoaded={loadedSections.sections}
@@ -3146,6 +3230,12 @@ function NewsDeskDashboard({
3146
3230
  semanticNodes={semanticNodes}
3147
3231
  />
3148
3232
  ) : null}
3233
+ {!isSectionPage && (activeTab === "desks" || activeTab === "topics") ? (
3234
+ <nav aria-label="Taxonomy views" className="taxonomy-view-switch" data-taxonomy-view-switch>
3235
+ <Link aria-current={activeTab === "desks" ? "page" : undefined} href={getNewsroomNavHref("/newsroom/desks", Boolean(dashboard.isDemo))}>Categories</Link>
3236
+ <Link aria-current={activeTab === "topics" ? "page" : undefined} href={getNewsroomNavHref("/newsroom/topics", Boolean(dashboard.isDemo))}>Topic proposals</Link>
3237
+ </nav>
3238
+ ) : null}
3149
3239
  {!isSectionPage && activeTab === "desks" ? (
3150
3240
  <DesksDeskView
3151
3241
  assignments={assignments}
@@ -3173,14 +3263,10 @@ function NewsDeskDashboard({
3173
3263
  ) : null}
3174
3264
  {!isSectionPage && activeTab === "concepts" ? (
3175
3265
  <ConceptsDeskView
3176
- categories={mergeCategoryRecords(categorys, activeCategoryTreeNodes)}
3177
- disabled={controlsDisabled}
3178
3266
  graph={graph}
3179
- initialCategoryLineageId={initialSelection.category}
3180
3267
  initialNodeLineageId={initialSelection.node}
3181
- onCreateInsight={createInsight}
3268
+ isDemo={Boolean(dashboard.isDemo)}
3182
3269
  semanticNodes={semanticNodes}
3183
- summary={summary}
3184
3270
  />
3185
3271
  ) : null}
3186
3272
  {!isSectionPage && activeTab === "references" ? (
@@ -3189,7 +3275,19 @@ function NewsDeskDashboard({
3189
3275
  disabled={controlsDisabled}
3190
3276
  initialReferenceLineageId={initialSelection.reference ?? pathnameReferenceLineageId}
3191
3277
  cyclotronStatus={cyclotronStatus}
3278
+ cyclotronPriming={{
3279
+ labels: primingLabels,
3280
+ requests: primingRequests,
3281
+ onRequest: async (input) => {
3282
+ const request = buildPrimingRequest(input, authState.label, primingLabels);
3283
+ if (!dashboard.isDemo) await requestCyclotronPriming(primingCyclotronId, request);
3284
+ setPrimingRequests((current) => [{ request, outcome: null }, ...current]);
3285
+ },
3286
+ }}
3192
3287
  decisions={relevanceDecisions}
3288
+ humanCuratedReferenceLineageIds={humanCuratedReferenceLineageIds}
3289
+ delegatedCurations={delegatedCurations}
3290
+ shadowAgreement={shadowAgreement}
3193
3291
  onRequestReviewRate={relevanceCyclotronId ? async (choice) => {
3194
3292
  const request = buildReviewRateRequest(choice, authState.label);
3195
3293
  if (dashboard.isDemo) return;
@@ -3202,20 +3300,14 @@ function NewsDeskDashboard({
3202
3300
  normalizeReferenceRejectionReasonCode(curation.reasonCode),
3203
3301
  { decisionRelationId: curation.decisionRelationId, shareable: curation.shareable },
3204
3302
  )}
3205
- onReview={(reference, action) => runReferenceCurationAction(reference, action)}
3303
+ sections={sectionChoices}
3304
+ sectionSuggestions={sectionSuggestions}
3305
+ sectionAgreement={siteSectionAgreement}
3306
+ onReview={(reference, action, options) => runReferenceCurationAction(reference, action, undefined, undefined, undefined, options?.sectionId)}
3206
3307
  referenceAttachments={referenceAttachments}
3207
3308
  references={references}
3208
3309
  />
3209
3310
  ) : null}
3210
- {!isSectionPage && activeTab === "insights" ? (
3211
- <InsightsDeskView
3212
- graph={graph}
3213
- initialThreadId={initialSelection.forumThread}
3214
- isDemo={Boolean(dashboard.isDemo)}
3215
- messages={messages}
3216
- summary={summary}
3217
- />
3218
- ) : null}
3219
3311
  {!isSectionPage && activeTab === "messages" ? (
3220
3312
  <MessagesDeskView
3221
3313
  assignments={assignments}
@@ -4159,20 +4251,20 @@ function RotatingSectionTriangleIcon({ expanded }: { expanded: boolean }) {
4159
4251
  function DeepNewsroomSectionView({
4160
4252
  demo,
4161
4253
  disabled,
4162
- onCreateInsight,
4254
+ onCreateNote,
4163
4255
  section,
4164
4256
  sectionId,
4165
4257
  sectionsLoaded,
4166
4258
  }: {
4167
4259
  demo: boolean;
4168
4260
  disabled: boolean;
4169
- onCreateInsight: (target: InsightTarget, summary: string, body: string) => Promise<void>;
4261
+ onCreateNote: (target: NoteTarget, summary: string, body: string) => Promise<void>;
4170
4262
  section: NewsroomSectionRecord | null;
4171
4263
  sectionId: string;
4172
4264
  sectionsLoaded: boolean;
4173
4265
  }) {
4174
- const insightTarget = section ? insightTargetForNewsroomSection(section) : null;
4175
- const insightComposer = useNewsroomInsightComposer(insightTarget, disabled, onCreateInsight);
4266
+ const noteTarget = section ? noteTargetForNewsroomSection(section) : null;
4267
+ const noteComposer = useNewsroomNoteComposer(noteTarget, disabled, onCreateNote);
4176
4268
 
4177
4269
  if (!section) {
4178
4270
  return (
@@ -4201,13 +4293,13 @@ function DeepNewsroomSectionView({
4201
4293
  <h2 data-newsroom-deep-section-title>{section.title}</h2>
4202
4294
  <p>{displayNewsroomSectionShortTitle(section)}</p>
4203
4295
  <div className="newsroom-deep-section__header-actions">
4204
- <button type="button" disabled={insightComposer.action.disabled} onClick={insightComposer.action.onSelect}>
4205
- {insightComposer.loading ? "Saving Insight" : "Add Insight"}
4296
+ <button type="button" disabled={noteComposer.action.disabled} onClick={noteComposer.action.onSelect}>
4297
+ {noteComposer.loading ? "Saving Note" : "Add Note"}
4206
4298
  </button>
4207
4299
  <Link href={getNewsDeskTabHref("/newsroom", demo)}>Back to Newsroom</Link>
4208
4300
  </div>
4209
4301
  </header>
4210
- {insightComposer.error ? <div className="category-steering-alert" role="status">{insightComposer.error}</div> : null}
4302
+ {noteComposer.error ? <div className="category-steering-alert" role="status">{noteComposer.error}</div> : null}
4211
4303
  <div className="newsroom-deep-section__body">
4212
4304
  <article className="newsroom-deep-section__field newsroom-deep-section__field--lead">
4213
4305
  <p className="story-label">Mission</p>
@@ -4224,7 +4316,7 @@ function DeepNewsroomSectionView({
4224
4316
  </article>
4225
4317
  ))}
4226
4318
  </div>
4227
- {insightComposer.dialog}
4319
+ {noteComposer.dialog}
4228
4320
  </section>
4229
4321
  );
4230
4322
  }
@@ -4415,9 +4507,9 @@ function AdministrationDeskView({
4415
4507
  <div className="news-desk-columns news-desk-columns--administration" data-news-desk-section="administration">
4416
4508
  <div className="news-desk-main-column">
4417
4509
  <section className="category-steering-section category-steering-section--lead" aria-labelledby="newsroom-administration-title">
4418
- <SectionHeader title="Administration" detail="Settings for users, policies, sections, and procedures" />
4419
- <div className="news-desk-settings-shell">
4420
- <nav className="news-desk-settings-nav" aria-label="Administration settings">
4510
+ <SectionHeader title={administrationPanel === "sections" ? "Sections" : "Administration"} detail={administrationPanel === "sections" ? "The publication's sections and ontology" : "Settings for users, policies, and procedures"} />
4511
+ <div className="news-desk-settings-shell" data-settings-nav={administrationPanel === "sections" ? "hidden" : undefined}>
4512
+ {administrationPanel === "sections" ? null : <nav className="news-desk-settings-nav" aria-label="Administration settings">
4421
4513
  <Link
4422
4514
  href={administrationPanelHref("users", isDemo)}
4423
4515
  data-news-desk-admin-nav="users"
@@ -4436,15 +4528,6 @@ function AdministrationDeskView({
4436
4528
  <strong>Policies</strong>
4437
4529
  <span>Publication and category doctrine</span>
4438
4530
  </Link>
4439
- <Link
4440
- href={administrationPanelHref("sections", isDemo)}
4441
- data-news-desk-admin-nav="sections"
4442
- data-active={administrationPanel === "sections" || undefined}
4443
- onClick={() => onPanelChange("sections")}
4444
- >
4445
- <strong>Sections</strong>
4446
- <span>Canonical and floating newspaper sections</span>
4447
- </Link>
4448
4531
  <Link
4449
4532
  href={administrationPanelHref("procedures", isDemo)}
4450
4533
  data-news-desk-admin-nav="procedures"
@@ -4454,6 +4537,15 @@ function AdministrationDeskView({
4454
4537
  <strong>Procedures</strong>
4455
4538
  <span>Procedure registry and versioned Tactus code</span>
4456
4539
  </Link>
4540
+ <Link
4541
+ href={administrationPanelHref("delegations", isDemo)}
4542
+ data-news-desk-admin-nav="delegations"
4543
+ data-active={administrationPanel === "delegations" || undefined}
4544
+ onClick={() => onPanelChange("delegations")}
4545
+ >
4546
+ <strong>Agent delegations</strong>
4547
+ <span>Let an agent curate References for you</span>
4548
+ </Link>
4457
4549
  {signupPluginEnabled() ? (
4458
4550
  <Link
4459
4551
  href={administrationPanelHref("signups", isDemo)}
@@ -4465,9 +4557,10 @@ function AdministrationDeskView({
4465
4557
  <span>Addresses from the site&apos;s signup forms</span>
4466
4558
  </Link>
4467
4559
  ) : null}
4468
- </nav>
4560
+ </nav>}
4469
4561
  <div className="news-desk-settings-panel">
4470
4562
  {administrationPanel === "signups" ? <SignupEntriesPanel /> : null}
4563
+ {administrationPanel === "delegations" ? <AdministrationDelegationsPanel disabled={disabled} /> : null}
4471
4564
  {administrationPanel === "users" ? (
4472
4565
  <AdministrationUsersPanel
4473
4566
  canManageUsers={canManageUsers}
@@ -4953,7 +5046,7 @@ function AdministrationSectionsPanel({
4953
5046
 
4954
5047
  return (
4955
5048
  <div data-news-desk-admin-panel="sections">
4956
- <SectionHeader title="Newspaper Sections" detail={`${sortedSections.length} configured sections`} />
5049
+ <SectionHeader title="Sections" detail={`${sortedSections.length} configured sections`} />
4957
5050
  <div className="news-desk-administration-category-layout">
4958
5051
  <div className="news-desk-administration-category-list" data-news-desk-section-list>
4959
5052
  {sortedSections.length ? (
@@ -5790,7 +5883,7 @@ function DesksDeskView({
5790
5883
  }, [initialRootKey, roots, selectedRoot, selectedRootKey]);
5791
5884
 
5792
5885
  return (
5793
- <div className="news-desk-columns" data-news-desk-section="desks">
5886
+ <div className="news-desk-columns news-desk-columns--administration news-desk-columns--taxonomy" data-news-desk-section="desks">
5794
5887
  <div className="news-desk-main-column">
5795
5888
  <section className="category-steering-section category-steering-section--lead" aria-labelledby="newsroom-desks-title">
5796
5889
  <SectionHeader title="News Desks" detail={`${roots.length} root topic desks`} />
@@ -6340,148 +6433,27 @@ function TopicProposalEditModal({
6340
6433
  }
6341
6434
 
6342
6435
  function ConceptsDeskView({
6343
- categories,
6344
- disabled,
6345
6436
  graph,
6346
- initialCategoryLineageId,
6347
6437
  initialNodeLineageId,
6348
- onCreateInsight,
6438
+ isDemo,
6349
6439
  semanticNodes,
6350
- summary,
6351
6440
  }: {
6352
- categories: CategorySteeringCategory[];
6353
- disabled: boolean;
6354
6441
  graph: SemanticGraph;
6355
- initialCategoryLineageId?: string | null;
6356
6442
  initialNodeLineageId?: string | null;
6357
- onCreateInsight: (target: InsightTarget, summary: string, body: string) => Promise<void>;
6443
+ isDemo: boolean;
6358
6444
  semanticNodes: SemanticNodeRecord[];
6359
- summary?: NewsroomSummaryRecord | null;
6360
6445
  }) {
6361
- const [nodeKindFilter, setNodeKindFilter] = useState("");
6362
- const [nodeStatusFilter, setNodeStatusFilter] = useState("");
6363
- const [selectedNodeLineageId, setSelectedNodeLineageId] = useState(initialNodeLineageId ?? "");
6364
- const [isConceptDetailOpen, setIsConceptDetailOpen] = useState(Boolean(initialNodeLineageId));
6365
- const categoryContext = useMemo(() => buildCategoryDrilldownContext(categories, initialCategoryLineageId), [categories, initialCategoryLineageId]);
6366
6446
  const feed = useNewsroomPagedRows({
6367
6447
  initialItems: semanticNodes,
6368
- enabled: !categoryContext?.primary,
6369
- resetKey: `semanticNodes:${nodeKindFilter}:${nodeStatusFilter}`,
6370
- loadPage: (nextToken) => loadNewsroomSemanticNodePage({
6371
- nodeKind: nodeKindFilter,
6372
- status: nodeStatusFilter,
6373
- nextToken,
6374
- }),
6448
+ enabled: true,
6449
+ resetKey: "semanticNodes:wiki",
6450
+ loadPage: (nextToken) => loadNewsroomSemanticNodePage({ nodeKind: "", status: "", nextToken }),
6375
6451
  });
6376
- const categoryFilter = categoryContext.primary ? graph.resolve("category", categoryLineageId(categoryContext.primary)) : null;
6377
- const categoryConceptLineages = useMemo(() => (
6378
- categoryContext.primary
6379
- ? new Set(semanticNodesForCategoryContext(graph, categoryContext).map((node) => node.lineageId))
6380
- : null
6381
- ), [categoryContext, graph]);
6382
- const feedNodes = useMemo(() => (
6383
- feed.items.length
6384
- ? feed.items
6385
- : (feed.isLoading || Boolean(feed.error))
6386
- ? semanticNodes
6387
- : feed.items
6388
- ), [feed.error, feed.isLoading, feed.items, semanticNodes]);
6389
- const visibleNodes = useMemo(() => (
6390
- categoryConceptLineages
6391
- ? semanticNodes.filter((node) => categoryConceptLineages.has(node.lineageId ?? node.id))
6392
- : feedNodes
6393
- ), [categoryConceptLineages, feedNodes, semanticNodes]);
6394
- const fallbackConceptCategories = useMemo(() => (
6395
- visibleNodes.length
6396
- ? []
6397
- : categories.filter((category) => category.status !== "deprecated" && category.status !== "archived")
6398
- ), [categories, visibleNodes.length]);
6399
- const requestedNodeLineageId = selectedNodeLineageId || initialNodeLineageId || "";
6400
- const selected = selectSemanticNodeSummary(graph, visibleNodes, requestedNodeLineageId)
6401
- ?? (requestedNodeLineageId ? selectSemanticNodeSummary(graph, semanticNodes, requestedNodeLineageId) : null)
6402
- ?? (requestedNodeLineageId ? selectCategorySummary(graph, categories, requestedNodeLineageId) : null)
6403
- ?? categoryFilter
6404
- ?? selectSemanticNodeSummary(graph, visibleNodes)
6405
- ?? selectCategorySummary(graph, fallbackConceptCategories);
6406
- const conceptKnowledgeQuery = useNewsroomKnowledgeContext(selected ? {
6407
- anchor: {
6408
- kind: selected.kind === "category" ? "category" : "semanticNode",
6409
- id: selected.id ?? selected.lineageId,
6410
- lineageId: selected.lineageId,
6411
- },
6412
- title: selected.label,
6413
- subtitle: selected.subtitle,
6414
- } : null);
6415
- const conceptInsight = useNewsroomInsightComposer(
6416
- selected && (selected.kind === "category" || selected.kind === "semanticNode") ? insightTargetForSemanticObject(selected) : null,
6417
- disabled,
6418
- onCreateInsight,
6419
- );
6420
- const detail = categoryFilter
6421
- ? `${visibleNodes.length} graph nodes associated with ${categoryFilter.label}`
6422
- : visibleNodes.length
6423
- ? `${summaryCountFromRecord(summary, "semanticNodes") || visibleNodes.length} graph nodes`
6424
- : `${fallbackConceptCategories.length} taxonomy concepts`;
6425
- const nodeKindCounts = categoryFilter ? countSemanticNodesByKind(visibleNodes) : summary?.facets?.semanticNodes?.byNodeKind ?? countSemanticNodesByKind(visibleNodes);
6426
- const nodeStatusCounts = categoryFilter ? countSemanticNodesByStatus(visibleNodes) : summary?.facets?.semanticNodes?.byStatus ?? countSemanticNodesByStatus(visibleNodes);
6427
- const nodeKinds = sortedCountOptions(nodeKindCounts).slice(0, 8);
6428
- const cards = visibleNodes.length
6429
- ? visibleNodes.map((node, index) => semanticNodeToNewsroomCard(node, index))
6430
- : fallbackConceptCategories.map((category, index) => categoryToConceptNewsroomCard(category, index));
6431
- const cardIds = useMemo(() => new Set(cards.map((card) => card.id)), [cards]);
6432
- const selectedLineageId = selected && cardIds.has(selected.lineageId) ? selected.lineageId : null;
6433
- const selectNode = (lineageId: string) => {
6434
- setSelectedNodeLineageId(lineageId);
6435
- setIsConceptDetailOpen(true);
6436
- pushNewsroomDetailUrl("concepts", lineageId, false);
6437
- };
6438
- return (
6439
- <>
6440
- <NewsroomListDetailShell
6441
- animatedDetail
6442
- sectionKey="concepts"
6443
- canExpandDetail={Boolean(selected)}
6444
- detailOpen={isConceptDetailOpen}
6445
- selectionScrollKey={selectedLineageId}
6446
- actions={[conceptInsight.action]}
6447
- utilityActions={[conceptKnowledgeQuery.action]}
6448
- lede={(
6449
- <NewsroomDeskSectionLede headingId="semantic-concepts-title" section="concepts" />
6450
- )}
6451
- list={(
6452
- <section className="category-steering-section category-steering-section--lead" aria-label={detail}>
6453
- <NewsroomCardGrid
6454
- cards={cards}
6455
- emptyLabel={feed.isLoading ? "Loading semantic concepts" : feed.error ?? "No semantic nodes imported"}
6456
- filterLabel="Concept kind"
6457
- filterOptions={[
6458
- { key: "", label: "All concept kinds", count: summaryCountFromRecord(summary, "semanticNodes") || visibleNodes.length },
6459
- ...nodeKinds.map((option) => ({ key: option.key, label: formatAssignmentTypeLabel(option.key), count: option.count })),
6460
- ]}
6461
- filterValue={nodeKindFilter}
6462
- footerLabel={feed.error ?? undefined}
6463
- hasMore={!categoryFilter && feed.hasMore}
6464
- isLoadingMore={feed.isLoadingMore}
6465
- metricValue={nodeStatusFilter}
6466
- metrics={[
6467
- { key: "", label: "All", count: summaryCountFromRecord(summary, "semanticNodes") || visibleNodes.length },
6468
- ...sortedCountOptions(nodeStatusCounts).map((option) => ({ key: option.key, label: option.key, count: option.count })),
6469
- ]}
6470
- onFilterChange={setNodeKindFilter}
6471
- onLoadMore={feed.loadMore}
6472
- onMetricChange={setNodeStatusFilter}
6473
- onSelect={selectNode}
6474
- selectedId={selectedLineageId}
6475
- />
6476
- </section>
6477
- )}
6478
- onCloseDetail={() => setIsConceptDetailOpen(false)}
6479
- detail={<SemanticDetailPanel graph={graph} selected={selected} knowledgeQuery={conceptKnowledgeQuery} />}
6480
- />
6481
- {conceptKnowledgeQuery.dialog}
6482
- {conceptInsight.dialog}
6483
- </>
6484
- );
6452
+ useEffect(() => {
6453
+ if (feed.hasMore && !feed.isLoadingMore) feed.loadMore();
6454
+ }, [feed]);
6455
+ const nodes = feed.items.length ? feed.items : semanticNodes;
6456
+ return <WikiConceptsView demo={isDemo} graph={graph} initialNodeLineageId={initialNodeLineageId} nodes={nodes} />;
6485
6457
  }
6486
6458
 
6487
6459
  function ReferencesDeskView({
@@ -6495,7 +6467,7 @@ function ReferencesDeskView({
6495
6467
  initialReferenceLineageId,
6496
6468
  isDemo,
6497
6469
  deepLinkFetchEnabled = true,
6498
- onCreateInsight,
6470
+ onCreateNote,
6499
6471
  onHydrateReference,
6500
6472
  onMoveCorpus,
6501
6473
  onReview,
@@ -6520,7 +6492,7 @@ function ReferencesDeskView({
6520
6492
  initialReferenceLineageId?: string | null;
6521
6493
  isDemo?: boolean;
6522
6494
  deepLinkFetchEnabled?: boolean;
6523
- onCreateInsight: (target: InsightTarget, summary: string, body: string) => Promise<void>;
6495
+ onCreateNote: (target: NoteTarget, summary: string, body: string) => Promise<void>;
6524
6496
  onHydrateReference?: (reference: ReferenceRecord) => void;
6525
6497
  onMoveCorpus: (reference: ReferenceRecord, corpusId: string) => void;
6526
6498
  onReview: (reference: ReferenceRecord, action: ReferenceCurationAction, note?: string, reasonCode?: ReferenceRejectionReasonCode | null) => void;
@@ -6924,7 +6896,7 @@ function ReferencesDeskView({
6924
6896
  corpora={corpora}
6925
6897
  disabled={disabled}
6926
6898
  graph={graph}
6927
- onCreateInsight={onCreateInsight}
6899
+ onCreateNote={onCreateNote}
6928
6900
  onMoveCorpus={onMoveCorpus}
6929
6901
  onReview={runReferenceAction}
6930
6902
  onReviewTopicLabel={onReviewTopicLabel}
@@ -6976,365 +6948,6 @@ function NewsroomDeskSectionLede({
6976
6948
  );
6977
6949
  }
6978
6950
 
6979
- function InsightsDeskView({
6980
- graph,
6981
- initialThreadId = null,
6982
- isDemo = false,
6983
- messages,
6984
- summary,
6985
- }: {
6986
- graph: SemanticGraph;
6987
- initialThreadId?: string | null;
6988
- isDemo?: boolean;
6989
- messages: MessageRecord[];
6990
- summary?: NewsroomSummaryRecord | null;
6991
- }) {
6992
- const pathname = usePathname();
6993
- const pathnameThreadId = useMemo(
6994
- () => parseInsightThreadIdFromNewsroomPathname(pathname),
6995
- [pathname],
6996
- );
6997
- const resolvedInitialThreadId = initialThreadId ?? pathnameThreadId ?? "";
6998
- const forumMessageAnchorId = useForumMessageAnchorId();
6999
- const [domainFilter, setDomainFilter] = useState(() => {
7000
- if (typeof window === "undefined") return "";
7001
- return readInsightsIndexFilters(new URLSearchParams(window.location.search)).domain;
7002
- });
7003
- const [threads, setThreads] = useState<ForumThreadWithMessages[]>([]);
7004
- const [threadsLoading, setThreadsLoading] = useState(false);
7005
- const [threadsError, setThreadsError] = useState<string | null>(null);
7006
- const [nextToken, setNextToken] = useState<string | null>(null);
7007
- const [selectedThreadId, setSelectedThreadId] = useState(resolvedInitialThreadId);
7008
- const [forumView, setForumView] = useState<ForumViewState>(
7009
- resolvedInitialThreadId ? { mode: "thread", threadId: resolvedInitialThreadId } : { mode: "index" },
7010
- );
7011
- const [replyParentId, setReplyParentId] = useState("");
7012
- const [composeSummary, setComposeSummary] = useState("");
7013
- const [composeContent, setComposeContent] = useState("");
7014
- const [composeError, setComposeError] = useState<string | null>(null);
7015
- const [deletingMessageId, setDeletingMessageId] = useState("");
7016
-
7017
- const syncInsightsIndexUrl = useCallback((nextDomain: string, replace = true) => {
7018
- if (isDemo || forumView.mode === "thread") return;
7019
- syncBrowserNewsroomIndexUrl("insights", effectiveInsightsIndexFilters({ domain: nextDomain }), { replace });
7020
- }, [forumView.mode, isDemo]);
7021
-
7022
- useEffect(() => {
7023
- if (isDemo || forumView.mode === "thread") return;
7024
- syncInsightsIndexUrl(domainFilter, true);
7025
- }, [domainFilter, forumView.mode, isDemo, syncInsightsIndexUrl]);
7026
-
7027
- const refreshThreads = useCallback(async (options?: { append?: boolean; token?: string | null }) => {
7028
- if (isDemo) {
7029
- const demoThreads = messages
7030
- .filter((message) => message.messageKind === "insight")
7031
- .filter((message) => !domainFilter || message.messageDomain === domainFilter)
7032
- .map((message) => buildDemoInsightForumThread(message, messages));
7033
- setThreads(demoThreads);
7034
- setThreadsError(null);
7035
- setNextToken(null);
7036
- return;
7037
- }
7038
- setThreadsLoading(true);
7039
- try {
7040
- const result = await loadInsightForumThreads({
7041
- domain: domainFilter,
7042
- nextToken: options?.token ?? null,
7043
- includeMessages: true,
7044
- });
7045
- setThreads((current) => (options?.append ? [...current, ...result.threads] : result.threads));
7046
- setNextToken(result.nextToken);
7047
- setThreadsError(null);
7048
- setSelectedThreadId((current) => (current && result.threads.some((thread) => thread.id === current) ? current : current));
7049
- } catch (error) {
7050
- setThreads([]);
7051
- setThreadsError(error instanceof Error ? error.message : "Could not load insight threads.");
7052
- setNextToken(null);
7053
- } finally {
7054
- setThreadsLoading(false);
7055
- }
7056
- }, [domainFilter, isDemo, messages]);
7057
-
7058
- useEffect(() => {
7059
- void refreshThreads();
7060
- }, [refreshThreads]);
7061
-
7062
- useEffect(() => {
7063
- if (!resolvedInitialThreadId || threadsLoading) return;
7064
- if (!threads.some((thread) => thread.id === resolvedInitialThreadId)) return;
7065
- if (selectedThreadId === resolvedInitialThreadId && forumView.mode === "thread") return;
7066
- setSelectedThreadId(resolvedInitialThreadId);
7067
- setForumView({ mode: "thread", threadId: resolvedInitialThreadId });
7068
- }, [forumView.mode, resolvedInitialThreadId, selectedThreadId, threads, threadsLoading]);
7069
-
7070
- useEffect(() => {
7071
- const handlePopState = () => {
7072
- const route = readCurrentForumRoute();
7073
- if (route.surface === "insight_forum" && route.threadId) {
7074
- setSelectedThreadId(route.threadId);
7075
- setForumView({ mode: "thread", threadId: route.threadId });
7076
- return;
7077
- }
7078
- setSelectedThreadId("");
7079
- setForumView({ mode: "index" });
7080
- };
7081
- window.addEventListener("popstate", handlePopState);
7082
- return () => window.removeEventListener("popstate", handlePopState);
7083
- }, []);
7084
-
7085
- const selectedThread = threads.find((thread) => thread.id === selectedThreadId) ?? null;
7086
- const domainOptions = useMemo(() => {
7087
- const domains = new Set<string>();
7088
- for (const message of messages) {
7089
- if (message.messageKind === "insight" && message.messageDomain) domains.add(message.messageDomain);
7090
- }
7091
- for (const thread of threads) {
7092
- if (thread.sectionKey) domains.add(thread.sectionKey);
7093
- }
7094
- return sortedCountOptions(
7095
- Object.fromEntries([...domains].map((domain) => [domain, threads.filter((thread) => thread.sectionKey === domain).length])),
7096
- );
7097
- }, [messages, threads]);
7098
-
7099
- const openThread = useCallback((threadId: string, options?: { messageId?: string | null; replace?: boolean }) => {
7100
- setSelectedThreadId(threadId);
7101
- setForumView({ mode: "thread", threadId });
7102
- pushInsightThreadUrl(threadId, {
7103
- demo: isDemo,
7104
- messageId: options?.messageId,
7105
- replace: options?.replace,
7106
- });
7107
- }, [isDemo]);
7108
-
7109
- const closeThread = useCallback(() => {
7110
- setSelectedThreadId("");
7111
- setForumView({ mode: "index" });
7112
- pushInsightThreadUrl(null, { demo: isDemo, replace: true });
7113
- }, [isDemo]);
7114
-
7115
- const postThreadMessage = useCallback(async () => {
7116
- if (!selectedThread) return;
7117
- const content = composeContent.trim();
7118
- if (!content) {
7119
- setComposeError("Message content is required.");
7120
- return;
7121
- }
7122
- const summary = composeSummary.trim() || content.slice(0, 120);
7123
- setComposeError(null);
7124
- try {
7125
- await appendInsightThreadReplyRecord({
7126
- threadId: selectedThread.id,
7127
- summary,
7128
- content,
7129
- role: "human",
7130
- authorLabel: "human-editor",
7131
- parentMessageId: replyParentId || undefined,
7132
- });
7133
- setComposeSummary("");
7134
- setComposeContent("");
7135
- setReplyParentId("");
7136
- await refreshThreads();
7137
- const reloaded = await loadInsightForumThreadById(selectedThread.id, { includeMessages: true });
7138
- if (reloaded) {
7139
- setThreads((current) => current.map((thread) => (thread.id === reloaded.id ? reloaded : thread)));
7140
- }
7141
- setForumView({ mode: "thread", threadId: selectedThread.id });
7142
- } catch (error) {
7143
- setComposeError(error instanceof Error ? error.message : "Could not post thread message.");
7144
- }
7145
- }, [composeContent, composeSummary, refreshThreads, replyParentId, selectedThread]);
7146
-
7147
- const deleteThreadMessage = useCallback(async (threadId: string, messageId: string) => {
7148
- setDeletingMessageId(messageId);
7149
- try {
7150
- await deleteInsightThreadMessageRecord({ threadId, messageId });
7151
- if (replyParentId === messageId) setReplyParentId("");
7152
- await refreshThreads();
7153
- const reloaded = await loadInsightForumThreadById(threadId, { includeMessages: true });
7154
- if (reloaded) {
7155
- setThreads((current) => current.map((thread) => (thread.id === reloaded.id ? reloaded : thread)));
7156
- }
7157
- setComposeError(null);
7158
- } catch (error) {
7159
- setComposeError(error instanceof Error ? error.message : "Could not delete message.");
7160
- } finally {
7161
- setDeletingMessageId("");
7162
- }
7163
- }, [refreshThreads, replyParentId]);
7164
-
7165
- const insightKnowledgeQuery = useNewsroomKnowledgeContext(selectedThread ? {
7166
- anchor: { kind: "message", id: selectedThread.id, lineageId: selectedThread.id },
7167
- title: selectedThread.title ?? "Insight thread",
7168
- subtitle: selectedThread.sectionKey ?? "insight",
7169
- } : null);
7170
-
7171
- return (
7172
- <>
7173
- <NewsroomListDetailShell
7174
- sectionKey="insights"
7175
- canExpandDetail={forumView.mode === "thread" && Boolean(forumView.threadId)}
7176
- detailOpen={forumView.mode === "thread" && Boolean(forumView.threadId)}
7177
- selectionScrollKey={forumView.mode === "thread" ? forumView.threadId : null}
7178
- utilityActions={[insightKnowledgeQuery.action]}
7179
- lede={(
7180
- <NewsroomDeskSectionLede
7181
- headingId="insights-forum-title"
7182
- section="insights"
7183
- headline="Insights"
7184
- lede="Recent research insights in reverse chronological order. Each insight opens a forum thread for discussion and refinement."
7185
- controls={(
7186
- <div className="news-desk-reference-sort-controls" role="group" aria-label="Insight domain filter">
7187
- <span className="news-desk-reference-sort-controls__label">Domain</span>
7188
- <button
7189
- type="button"
7190
- data-active={!domainFilter || undefined}
7191
- onClick={() => {
7192
- setDomainFilter("");
7193
- syncInsightsIndexUrl("", true);
7194
- }}
7195
- >
7196
- All
7197
- </button>
7198
- <button
7199
- type="button"
7200
- data-active={domainFilter === "knowledge" || undefined}
7201
- onClick={() => {
7202
- setDomainFilter("knowledge");
7203
- syncInsightsIndexUrl("knowledge", true);
7204
- }}
7205
- >
7206
- Knowledge
7207
- </button>
7208
- <button
7209
- type="button"
7210
- data-active={domainFilter === "assignment_work" || undefined}
7211
- onClick={() => {
7212
- setDomainFilter("assignment_work");
7213
- syncInsightsIndexUrl("assignment_work", true);
7214
- }}
7215
- >
7216
- Assignment work
7217
- </button>
7218
- </div>
7219
- )}
7220
- />
7221
- )}
7222
- list={(
7223
- <section className="category-steering-section category-steering-section--lead" aria-label="Insight forum threads">
7224
- <ForumThreadIndex
7225
- threads={threads}
7226
- sections={[]}
7227
- isLoading={threadsLoading}
7228
- error={threadsError}
7229
- emptyLabel="No insight threads match this filter."
7230
- variant="insight"
7231
- toolbar={domainOptions.length > 2 ? (
7232
- <div className="news-desk-forum-toolbar">
7233
- <select
7234
- aria-label="Insight domain"
7235
- value={domainFilter}
7236
- onChange={(event) => {
7237
- setDomainFilter(event.target.value);
7238
- syncInsightsIndexUrl(event.target.value, true);
7239
- }}
7240
- >
7241
- <option value="">All domains ({threads.length})</option>
7242
- {domainOptions.map((option) => (
7243
- <option key={option.key} value={option.key}>{option.key} ({option.count})</option>
7244
- ))}
7245
- </select>
7246
- {nextToken ? (
7247
- <button type="button" disabled={threadsLoading} onClick={() => void refreshThreads({ append: true, token: nextToken })}>
7248
- Load more
7249
- </button>
7250
- ) : null}
7251
- </div>
7252
- ) : nextToken ? (
7253
- <div className="news-desk-forum-toolbar">
7254
- <button type="button" disabled={threadsLoading} onClick={() => void refreshThreads({ append: true, token: nextToken })}>
7255
- Load more
7256
- </button>
7257
- </div>
7258
- ) : null}
7259
- onOpenThread={(threadId) => openThread(threadId)}
7260
- />
7261
- </section>
7262
- )}
7263
- detail={forumView.mode === "thread" && selectedThread ? (
7264
- <ForumThreadView
7265
- thread={selectedThread}
7266
- sections={[]}
7267
- replyParentId={replyParentId}
7268
- composeSummary={composeSummary}
7269
- composeContent={composeContent}
7270
- composeError={composeError}
7271
- focusMessageId={forumMessageAnchorId}
7272
- isDemo={isDemo}
7273
- variant="insight"
7274
- onBack={closeThread}
7275
- onReplyTarget={(messageId) => {
7276
- setReplyParentId(messageId);
7277
- pushInsightThreadUrl(selectedThread.id, { demo: isDemo, messageId, replace: true });
7278
- }}
7279
- onClearReplyTarget={() => {
7280
- setReplyParentId("");
7281
- pushInsightThreadUrl(selectedThread.id, { demo: isDemo, replace: true });
7282
- }}
7283
- onSummaryChange={setComposeSummary}
7284
- onContentChange={setComposeContent}
7285
- onSubmit={() => void postThreadMessage()}
7286
- onDeleteMessage={(messageId) => {
7287
- if (!selectedThread || deletingMessageId === messageId) return;
7288
- void deleteThreadMessage(selectedThread.id, messageId);
7289
- }}
7290
- deletingMessageId={deletingMessageId}
7291
- />
7292
- ) : forumView.mode === "thread" ? (
7293
- <section className="category-steering-section">
7294
- <SectionHeader title="Thread Unavailable" detail="Insights" />
7295
- <EmptyRow label="That insight thread is not available. Return to the index or adjust filters." />
7296
- </section>
7297
- ) : null}
7298
- />
7299
- {insightKnowledgeQuery.dialog}
7300
- </>
7301
- );
7302
- }
7303
-
7304
- function buildDemoInsightForumThread(root: MessageRecord, allMessages: MessageRecord[]): ForumThreadWithMessages {
7305
- const replies = allMessages.filter((message) => (
7306
- message.threadId === root.id
7307
- && message.id !== root.id
7308
- && String(message.status || "active") === "active"
7309
- ));
7310
- const messages = [root, ...replies].sort(
7311
- (left, right) => Number(left.sequenceNumber ?? 0) - Number(right.sequenceNumber ?? 0),
7312
- );
7313
- const lastMessage = messages.at(-1) ?? root;
7314
- return {
7315
- id: root.id,
7316
- threadKind: "insight_forum",
7317
- status: "active",
7318
- title: root.summary ?? "Insight",
7319
- summary: root.messageDomain ?? null,
7320
- primaryAnchorKind: "message",
7321
- primaryAnchorId: root.id,
7322
- primaryAnchorLineageId: root.id,
7323
- primaryAnchorKey: `message#${root.id}`,
7324
- createdByLabel: root.authorLabel ?? null,
7325
- messageCount: messages.length,
7326
- lastMessageId: lastMessage.id,
7327
- lastMessageAt: lastMessage.updatedAt ?? lastMessage.createdAt,
7328
- metadata: metadataRecord(root.metadata),
7329
- createdAt: root.createdAt,
7330
- updatedAt: lastMessage.updatedAt ?? lastMessage.createdAt,
7331
- newsroomFeedKey: "messages",
7332
- messages,
7333
- scope: "insight",
7334
- sectionKey: root.messageDomain ?? null,
7335
- };
7336
- }
7337
-
7338
6951
  function MessagesDeskView({
7339
6952
  assignments,
7340
6953
  graph,
@@ -7941,7 +7554,6 @@ function ForumThreadIndex({
7941
7554
  emptyLabel,
7942
7555
  toolbar,
7943
7556
  composer,
7944
- variant = "edition",
7945
7557
  onOpenThread,
7946
7558
  }: {
7947
7559
  threads: ForumThreadWithMessages[];
@@ -7951,7 +7563,6 @@ function ForumThreadIndex({
7951
7563
  emptyLabel: string;
7952
7564
  toolbar?: ReactNode;
7953
7565
  composer?: ReactNode;
7954
- variant?: "edition" | "insight";
7955
7566
  onOpenThread: (threadId: string) => void;
7956
7567
  }) {
7957
7568
  return (
@@ -7963,7 +7574,7 @@ function ForumThreadIndex({
7963
7574
  <thead>
7964
7575
  <tr>
7965
7576
  <th>Topic</th>
7966
- <th>{variant === "insight" ? "Domain" : "Scope"}</th>
7577
+ <th>Scope</th>
7967
7578
  <th>Replies</th>
7968
7579
  <th>Last Post</th>
7969
7580
  </tr>
@@ -7976,11 +7587,9 @@ function ForumThreadIndex({
7976
7587
  );
7977
7588
  const lastMessage = sortedMessages.at(-1) ?? null;
7978
7589
  const replies = Math.max(activeMessages.length - 1, 0);
7979
- const scopeLabel = thread.scope === "insight"
7980
- ? (thread.sectionKey ?? thread.summary ?? "Insight")
7981
- : thread.scope === "edition"
7982
- ? "Edition"
7983
- : `Section: ${normalizeForumThreadSectionLabel(thread, sections)}`;
7590
+ const scopeLabel = thread.scope === "edition"
7591
+ ? "Edition"
7592
+ : `Section: ${normalizeForumThreadSectionLabel(thread, sections)}`;
7984
7593
  return (
7985
7594
  <tr key={thread.id}>
7986
7595
  <td>
@@ -8094,7 +7703,6 @@ function ForumThreadView({
8094
7703
  deletingMessageId,
8095
7704
  focusMessageId = null,
8096
7705
  isDemo = false,
8097
- variant = "edition",
8098
7706
  onBack,
8099
7707
  onReplyTarget,
8100
7708
  onDeleteMessage,
@@ -8112,7 +7720,6 @@ function ForumThreadView({
8112
7720
  deletingMessageId?: string;
8113
7721
  focusMessageId?: string | null;
8114
7722
  isDemo?: boolean;
8115
- variant?: "edition" | "insight";
8116
7723
  onBack: () => void;
8117
7724
  onReplyTarget: (messageId: string) => void;
8118
7725
  onDeleteMessage: (messageId: string) => void;
@@ -8125,9 +7732,7 @@ function ForumThreadView({
8125
7732
  (left, right) => Number(left.sequenceNumber ?? 0) - Number(right.sequenceNumber ?? 0),
8126
7733
  );
8127
7734
  const visibleCount = messages.length;
8128
- const threadShareUrl = variant === "insight"
8129
- ? buildInsightThreadUrl(thread.id, { demo: isDemo })
8130
- : buildForumThreadUrl(thread.id, { demo: isDemo });
7735
+ const threadShareUrl = buildForumThreadUrl(thread.id, { demo: isDemo });
8131
7736
 
8132
7737
  useEffect(() => {
8133
7738
  if (!focusMessageId || typeof window === "undefined") return;
@@ -8156,11 +7761,9 @@ function ForumThreadView({
8156
7761
  Back to Threads
8157
7762
  </button>
8158
7763
  <p className="story-label">
8159
- {thread.scope === "insight"
8160
- ? `Insight · ${thread.sectionKey ?? "discussion"}`
8161
- : thread.scope === "edition"
8162
- ? "Edition Thread"
8163
- : `Section Thread: ${normalizeForumThreadSectionLabel(thread, sections)}`}
7764
+ {thread.scope === "edition"
7765
+ ? "Edition Thread"
7766
+ : `Section Thread: ${normalizeForumThreadSectionLabel(thread, sections)}`}
8164
7767
  </p>
8165
7768
  <h3>{thread.title}</h3>
8166
7769
  </div>
@@ -8200,9 +7803,7 @@ function ForumThreadView({
8200
7803
  </DropdownMenuItem>
8201
7804
  <DropdownMenuItem
8202
7805
  onSelect={() => {
8203
- const url = variant === "insight"
8204
- ? buildInsightThreadUrl(thread.id, { messageId: message.id, demo: isDemo })
8205
- : buildForumThreadUrl(thread.id, { messageId: message.id, demo: isDemo });
7806
+ const url = buildForumThreadUrl(thread.id, { messageId: message.id, demo: isDemo });
8206
7807
  void copyShareLink(url);
8207
7808
  }}
8208
7809
  >
@@ -8505,7 +8106,7 @@ function NewsroomListDetailShell({
8505
8106
  list: ReactNode;
8506
8107
  onCloseDetail?: () => void;
8507
8108
  selectionScrollKey?: string | null;
8508
- sectionKey: "assignments" | "concepts" | "insights" | "messages" | "references" | "topics";
8109
+ sectionKey: "assignments" | "concepts" | "messages" | "references" | "topics";
8509
8110
  utilityActions?: NewsroomDetailAction[];
8510
8111
  }) {
8511
8112
  const [detailMode, setDetailMode] = useState<"split" | "full">("split");
@@ -9017,7 +8618,7 @@ function LibraryBigIcon() {
9017
8618
  );
9018
8619
  }
9019
8620
 
9020
- function InsightIcon() {
8621
+ function NoteIcon() {
9021
8622
  return (
9022
8623
  <svg
9023
8624
  aria-hidden="true"
@@ -9478,7 +9079,7 @@ function SemanticDetailPanel({
9478
9079
  }
9479
9080
 
9480
9081
  const messages = graph.messagesFor(selected.kind, selected.lineageId);
9481
- const insights = graph.insightsFor(selected.kind, selected.lineageId);
9082
+ const notes = graph.notesFor(selected.kind, selected.lineageId);
9482
9083
  const attachments = selected.kind === "reference" ? graph.attachmentsForReference(selected.lineageId) : [];
9483
9084
  const neighborGroups = graph.neighbors(selected.kind, selected.lineageId);
9484
9085
  const selectedReference = selected.kind === "reference" ? selected.record as ReferenceRecord : null;
@@ -9552,8 +9153,8 @@ function SemanticDetailPanel({
9552
9153
  ) : null}
9553
9154
  </div>
9554
9155
  ) : null}
9555
- {insights.length ? (
9556
- <InsightMessageBlock insights={insights} />
9156
+ {notes.length ? (
9157
+ <NoteMessageBlock notes={notes} />
9557
9158
  ) : null}
9558
9159
  {messages.length ? (
9559
9160
  <div className="news-desk-detail-block">
@@ -9757,11 +9358,11 @@ function useNewsroomKnowledgeContext(target: KnowledgeQueryTarget | null) {
9757
9358
  };
9758
9359
  }
9759
9360
 
9760
- function useNewsroomInsightComposer(
9761
- target: InsightTarget | null,
9361
+ function useNewsroomNoteComposer(
9362
+ target: NoteTarget | null,
9762
9363
  disabled: boolean,
9763
- onCreate: (target: InsightTarget, summary: string, body: string) => Promise<void>,
9764
- ): InsightComposerControl {
9364
+ onCreate: (target: NoteTarget, summary: string, body: string) => Promise<void>,
9365
+ ): NoteComposerControl {
9765
9366
  const [dialogOpen, setDialogOpen] = useState(false);
9766
9367
  const [summary, setSummary] = useState("");
9767
9368
  const [body, setBody] = useState("");
@@ -9770,7 +9371,7 @@ function useNewsroomInsightComposer(
9770
9371
  loading: false,
9771
9372
  targetKey: null,
9772
9373
  });
9773
- const targetKey = target ? insightTargetKey(target) : null;
9374
+ const targetKey = target ? noteTargetKey(target) : null;
9774
9375
 
9775
9376
  useEffect(() => {
9776
9377
  setDialogOpen(false);
@@ -9784,7 +9385,7 @@ function useNewsroomInsightComposer(
9784
9385
  const cleanSummary = summary.trim();
9785
9386
  const cleanBody = body.trim();
9786
9387
  if (!cleanSummary || !cleanBody) {
9787
- setState({ error: "Insight summary and body are required.", loading: false, targetKey });
9388
+ setState({ error: "Note summary and body are required.", loading: false, targetKey });
9788
9389
  return;
9789
9390
  }
9790
9391
  setState({ error: null, loading: true, targetKey });
@@ -9796,7 +9397,7 @@ function useNewsroomInsightComposer(
9796
9397
  setBody("");
9797
9398
  } catch (error) {
9798
9399
  setState({
9799
- error: error instanceof Error ? error.message : "Could not save insight.",
9400
+ error: error instanceof Error ? error.message : "Could not save note.",
9800
9401
  loading: false,
9801
9402
  targetKey,
9802
9403
  });
@@ -9808,15 +9409,15 @@ function useNewsroomInsightComposer(
9808
9409
 
9809
9410
  return {
9810
9411
  action: {
9811
- ariaLabel: "Add insight",
9412
+ ariaLabel: "Add note",
9812
9413
  disabled: disabled || !target || loading,
9813
- icon: <InsightIcon />,
9814
- key: "add-insight",
9815
- label: loading ? "Saving Insight" : "Add Insight",
9414
+ icon: <NoteIcon />,
9415
+ key: "add-note",
9416
+ label: loading ? "Saving Note" : "Add Note",
9816
9417
  onSelect: () => setDialogOpen(true),
9817
9418
  } satisfies NewsroomDetailAction,
9818
9419
  dialog: target ? (
9819
- <InsightComposerDialog
9420
+ <NoteComposerDialog
9820
9421
  body={body}
9821
9422
  disabled={disabled || loading}
9822
9423
  error={error}
@@ -10155,7 +9756,7 @@ function KnowledgeQueryDialog({
10155
9756
  );
10156
9757
  }
10157
9758
 
10158
- function InsightComposerDialog({
9759
+ function NoteComposerDialog({
10159
9760
  body,
10160
9761
  disabled,
10161
9762
  error,
@@ -10176,7 +9777,7 @@ function InsightComposerDialog({
10176
9777
  onSummaryChange: (value: string) => void;
10177
9778
  open: boolean;
10178
9779
  summary: string;
10179
- target: InsightTarget;
9780
+ target: NoteTarget;
10180
9781
  }) {
10181
9782
  useEffect(() => {
10182
9783
  if (!open) return;
@@ -10193,16 +9794,16 @@ function InsightComposerDialog({
10193
9794
  return (
10194
9795
  <div
10195
9796
  className="news-desk-modal"
10196
- data-news-desk-insight-modal
9797
+ data-news-desk-note-modal
10197
9798
  onMouseDown={(event) => {
10198
9799
  if (event.target === event.currentTarget && !disabled) onClose();
10199
9800
  }}
10200
9801
  >
10201
- <div className="news-desk-modal__dialog news-desk-modal__dialog--knowledge-query" role="dialog" aria-modal="true" aria-labelledby="insight-composer-title">
9802
+ <div className="news-desk-modal__dialog news-desk-modal__dialog--knowledge-query" role="dialog" aria-modal="true" aria-labelledby="note-composer-title">
10202
9803
  <header className="news-desk-modal__header">
10203
9804
  <div>
10204
- <p className="story-label">Insight</p>
10205
- <h3 id="insight-composer-title">{target.title}</h3>
9805
+ <p className="story-label">Note</p>
9806
+ <h3 id="note-composer-title">{target.title}</h3>
10206
9807
  <span>{target.kind} / {target.lineageId}</span>
10207
9808
  </div>
10208
9809
  <button type="button" disabled={disabled} onClick={onClose}>Close</button>
@@ -10217,7 +9818,7 @@ function InsightComposerDialog({
10217
9818
  />
10218
9819
  </label>
10219
9820
  <label>
10220
- <span>Insight Body</span>
9821
+ <span>Note Body</span>
10221
9822
  <textarea
10222
9823
  disabled={disabled}
10223
9824
  rows={8}
@@ -10230,7 +9831,7 @@ function InsightComposerDialog({
10230
9831
  <footer className="news-desk-modal__actions">
10231
9832
  <button type="button" disabled={disabled} onClick={onClose}>Cancel</button>
10232
9833
  <button type="button" className="news-desk-assignment-create-button" disabled={disabled || !summary.trim() || !body.trim()} onClick={onRun}>
10233
- {disabled ? "Saving" : "Save Insight"}
9834
+ {disabled ? "Saving" : "Save Note"}
10234
9835
  </button>
10235
9836
  </footer>
10236
9837
  </div>
@@ -10977,14 +10578,14 @@ function PayloadDetailBlock({
10977
10578
  );
10978
10579
  }
10979
10580
 
10980
- function InsightMessageBlock({ insights }: { insights: MessageRecord[] }) {
10581
+ function NoteMessageBlock({ notes }: { notes: MessageRecord[] }) {
10981
10582
  return (
10982
10583
  <div className="news-desk-detail-block">
10983
- <p className="story-label">Insights</p>
10984
- {insights.slice(0, 6).map((message) => (
10584
+ <p className="story-label">Notes</p>
10585
+ {notes.slice(0, 6).map((message) => (
10985
10586
  <div className="news-desk-detail-line" key={message.id}>
10986
10587
  <span>{message.authorLabel ?? "knowledge"}</span>
10987
- <strong>{message.summary ?? "Stored insight"}</strong>
10588
+ <strong>{message.summary ?? "Stored note"}</strong>
10988
10589
  </div>
10989
10590
  ))}
10990
10591
  </div>
@@ -10998,7 +10599,7 @@ function ReferenceDetailPanel({
10998
10599
  curation,
10999
10600
  disabled,
11000
10601
  graph,
11001
- onCreateInsight,
10602
+ onCreateNote,
11002
10603
  onMoveCorpus,
11003
10604
  onReview,
11004
10605
  onReviewTopicLabel,
@@ -11016,7 +10617,7 @@ function ReferenceDetailPanel({
11016
10617
  curation: ReferenceCurationDisplayState | null;
11017
10618
  disabled: boolean;
11018
10619
  graph: SemanticGraph;
11019
- onCreateInsight: (target: InsightTarget, summary: string, body: string) => Promise<void>;
10620
+ onCreateNote: (target: NoteTarget, summary: string, body: string) => Promise<void>;
11020
10621
  onMoveCorpus: (reference: ReferenceRecord, corpusId: string) => void;
11021
10622
  onReview: (action: ReferenceCurationAction) => void;
11022
10623
  onReviewTopicLabel: (input: { action: TopicLabelAction; category: CategorySteeringCategory; note?: string | null; reference: ReferenceRecord; sourceRelationId?: string | null }) => void;
@@ -11295,7 +10896,7 @@ function ReferenceDetailPanel({
11295
10896
 
11296
10897
  const lineageId = referenceLineageId;
11297
10898
  const messages = graph.messagesFor("reference", lineageId);
11298
- const insights = graph.insightsFor("reference", lineageId);
10899
+ const notes = graph.notesFor("reference", lineageId);
11299
10900
  const neighborGroups = graph.neighbors("reference", lineageId);
11300
10901
  const fallbackOutgoingCitationGroup = neighborGroups.find((group) => group.direction === "outgoing" && group.predicate === "cites") ?? null;
11301
10902
  const fallbackIncomingCitationGroup = neighborGroups.find((group) => group.direction === "incoming" && group.predicate === "cites") ?? null;
@@ -11414,10 +11015,10 @@ function ReferenceDetailPanel({
11414
11015
  reference={reference}
11415
11016
  semanticRelations={semanticRelations}
11416
11017
  />
11417
- <ReferenceInsightPanel
11018
+ <ReferenceNotePanel
11418
11019
  disabled={disabled}
11419
- insights={insights}
11420
- onCreateInsight={onCreateInsight}
11020
+ notes={notes}
11021
+ onCreateNote={onCreateNote}
11421
11022
  reference={reference}
11422
11023
  />
11423
11024
  <div data-news-desk-reference-metadata-expander>
@@ -12307,49 +11908,49 @@ function ReferenceCurationStatusPanel({ status }: { status: ReferenceCurationRun
12307
11908
  );
12308
11909
  }
12309
11910
 
12310
- function ReferenceInsightPanel({
11911
+ function ReferenceNotePanel({
12311
11912
  disabled,
12312
- insights,
12313
- onCreateInsight,
11913
+ notes,
11914
+ onCreateNote,
12314
11915
  reference,
12315
11916
  }: {
12316
11917
  disabled: boolean;
12317
- insights: MessageRecord[];
12318
- onCreateInsight: (target: InsightTarget, summary: string, body: string) => Promise<void>;
11918
+ notes: MessageRecord[];
11919
+ onCreateNote: (target: NoteTarget, summary: string, body: string) => Promise<void>;
12319
11920
  reference: ReferenceRecord;
12320
11921
  }) {
12321
11922
  const [composerOpen, setComposerOpen] = useState(false);
12322
11923
  const [body, setBody] = useState("");
12323
11924
  const [error, setError] = useState<string | null>(null);
12324
11925
  const [saving, setSaving] = useState(false);
12325
- const target = useMemo(() => insightTargetForReference(reference), [reference]);
12326
- const hasInsights = insights.length > 0;
11926
+ const target = useMemo(() => noteTargetForReference(reference), [reference]);
11927
+ const hasNotes = notes.length > 0;
12327
11928
 
12328
11929
  return (
12329
- <section className="news-desk-reference-workflow" data-reference-insight-workflow>
11930
+ <section className="news-desk-reference-workflow" data-reference-note-workflow>
12330
11931
  <header className="news-desk-reference-workflow__header">
12331
- <p className="story-label">Insights</p>
12332
- {!hasInsights && !composerOpen ? (
11932
+ <p className="story-label">Notes</p>
11933
+ {!hasNotes && !composerOpen ? (
12333
11934
  <button
12334
11935
  type="button"
12335
- data-news-desk-reference-insight-trigger
11936
+ data-news-desk-reference-note-trigger
12336
11937
  disabled={disabled || saving}
12337
11938
  onClick={() => {
12338
11939
  setComposerOpen(true);
12339
11940
  setError(null);
12340
11941
  }}
12341
11942
  >
12342
- Add insight
11943
+ Add note
12343
11944
  </button>
12344
11945
  ) : null}
12345
11946
  </header>
12346
- <div className="news-desk-reference-insight-shell" data-reference-insight-input>
12347
- {hasInsights ? (
12348
- <div className="news-desk-reference-insight-list" data-reference-insight-state>
12349
- {insights.map((message) => (
11947
+ <div className="news-desk-reference-note-shell" data-reference-note-input>
11948
+ {hasNotes ? (
11949
+ <div className="news-desk-reference-note-list" data-reference-note-state>
11950
+ {notes.map((message) => (
12350
11951
  <article className="news-desk-reference-topic-state-row" key={message.id}>
12351
11952
  <div className="news-desk-reference-topic-state-row__headline">
12352
- <strong>{message.summary ?? "Stored insight"}</strong>
11953
+ <strong>{message.summary ?? "Stored note"}</strong>
12353
11954
  <span>{message.authorLabel ?? message.source ?? "knowledge"}</span>
12354
11955
  </div>
12355
11956
  <div className="news-desk-reference-topic-state-row__meta">
@@ -12359,35 +11960,35 @@ function ReferenceInsightPanel({
12359
11960
  ))}
12360
11961
  </div>
12361
11962
  ) : null}
12362
- {hasInsights && !composerOpen ? (
11963
+ {hasNotes && !composerOpen ? (
12363
11964
  <button
12364
11965
  type="button"
12365
- data-news-desk-reference-insight-trigger
11966
+ data-news-desk-reference-note-trigger
12366
11967
  disabled={disabled || saving}
12367
11968
  onClick={() => {
12368
11969
  setComposerOpen(true);
12369
11970
  setError(null);
12370
11971
  }}
12371
11972
  >
12372
- Add insight
11973
+ Add note
12373
11974
  </button>
12374
11975
  ) : null}
12375
11976
  {composerOpen ? (
12376
11977
  <form
12377
- className="news-desk-reference-insight-form"
12378
- data-news-desk-reference-insight-form
11978
+ className="news-desk-reference-note-form"
11979
+ data-news-desk-reference-note-form
12379
11980
  onSubmit={(event) => {
12380
11981
  event.preventDefault();
12381
11982
  if (disabled || saving) return;
12382
11983
  const cleanBody = body.trim();
12383
11984
  if (!cleanBody) {
12384
- setError("Insight text is required.");
11985
+ setError("Note text is required.");
12385
11986
  return;
12386
11987
  }
12387
- const cleanSummary = summarizeInsightForStorage(cleanBody);
11988
+ const cleanSummary = summarizeNoteForStorage(cleanBody);
12388
11989
  setSaving(true);
12389
11990
  setError(null);
12390
- void onCreateInsight(target, cleanSummary, cleanBody)
11991
+ void onCreateNote(target, cleanSummary, cleanBody)
12391
11992
  .then(() => {
12392
11993
  setSaving(false);
12393
11994
  setBody("");
@@ -12395,12 +11996,12 @@ function ReferenceInsightPanel({
12395
11996
  })
12396
11997
  .catch((submitError) => {
12397
11998
  setSaving(false);
12398
- setError(submitError instanceof Error ? submitError.message : "Could not save insight.");
11999
+ setError(submitError instanceof Error ? submitError.message : "Could not save note.");
12399
12000
  });
12400
12001
  }}
12401
12002
  >
12402
12003
  <label className="news-desk-reference-curation-note">
12403
- <span>Insight</span>
12004
+ <span>Note</span>
12404
12005
  <textarea
12405
12006
  disabled={disabled || saving}
12406
12007
  value={body}
@@ -12408,9 +12009,9 @@ function ReferenceInsightPanel({
12408
12009
  />
12409
12010
  </label>
12410
12011
  {error ? <p className="news-desk-detail-copy">{error}</p> : null}
12411
- <div className="news-desk-reference-insight-form__actions">
12012
+ <div className="news-desk-reference-note-form__actions">
12412
12013
  <button type="submit" disabled={disabled || saving || !body.trim()}>
12413
- {saving ? "Saving" : "Save insight"}
12014
+ {saving ? "Saving" : "Save note"}
12414
12015
  </button>
12415
12016
  <button
12416
12017
  type="button"
@@ -12431,9 +12032,9 @@ function ReferenceInsightPanel({
12431
12032
  );
12432
12033
  }
12433
12034
 
12434
- function summarizeInsightForStorage(body: string): string {
12035
+ function summarizeNoteForStorage(body: string): string {
12435
12036
  const normalized = body.trim().replace(/\s+/g, " ");
12436
- if (!normalized) return "Insight";
12037
+ if (!normalized) return "Note";
12437
12038
  const firstSentenceMatch = normalized.match(/(.+?[.!?])(?:\s|$)/);
12438
12039
  const candidate = (firstSentenceMatch?.[1] ?? normalized).trim();
12439
12040
  if (candidate.length <= 120) return candidate;
@@ -12767,7 +12368,7 @@ function ReportingStoryBudgetBoard({
12767
12368
  section={section}
12768
12369
  selectedAssignmentId={selectedAssignmentId}
12769
12370
  />
12770
- )) : <EmptyRow label="No reporting edition-candidate assignments found" />}
12371
+ )) : <EmptyRow label="No reporting candidate assignments found" />}
12771
12372
  </div>
12772
12373
  );
12773
12374
  }
@@ -13263,7 +12864,7 @@ function buildUiAnalysisReindexAssignmentPlan({
13263
12864
  createdBy: actorLabel,
13264
12865
  createdAt: now,
13265
12866
  updatedAt: now,
13266
- newsroomFeedKey: "assignments",
12867
+ newsroomFeedKey: "operations",
13267
12868
  metadata,
13268
12869
  };
13269
12870
  const event: AssignmentEventRecord = {
@@ -13298,7 +12899,7 @@ function buildUiAnalysisReindexAssignmentPlan({
13298
12899
  return { assignment, event, relation, metadata };
13299
12900
  }
13300
12901
 
13301
- function insightTargetForReference(reference: ReferenceRecord): InsightTarget {
12902
+ function noteTargetForReference(reference: ReferenceRecord): NoteTarget {
13302
12903
  return {
13303
12904
  kind: "reference",
13304
12905
  id: reference.id,
@@ -13309,7 +12910,7 @@ function insightTargetForReference(reference: ReferenceRecord): InsightTarget {
13309
12910
  };
13310
12911
  }
13311
12912
 
13312
- function insightTargetForNewsroomSection(section: NewsroomSectionRecord): InsightTarget {
12913
+ function noteTargetForNewsroomSection(section: NewsroomSectionRecord): NoteTarget {
13313
12914
  return {
13314
12915
  kind: "newsroomSection",
13315
12916
  id: section.id,
@@ -13320,7 +12921,7 @@ function insightTargetForNewsroomSection(section: NewsroomSectionRecord): Insigh
13320
12921
  };
13321
12922
  }
13322
12923
 
13323
- function insightTargetForSemanticObject(object: SemanticObjectSummary): InsightTarget | null {
12924
+ function noteTargetForSemanticObject(object: SemanticObjectSummary): NoteTarget | null {
13324
12925
  if (object.kind !== "category" && object.kind !== "semanticNode") return null;
13325
12926
  return {
13326
12927
  kind: object.kind,
@@ -13332,21 +12933,21 @@ function insightTargetForSemanticObject(object: SemanticObjectSummary): InsightT
13332
12933
  };
13333
12934
  }
13334
12935
 
13335
- function insightTargetKey(target: InsightTarget): string {
12936
+ function noteTargetKey(target: NoteTarget): string {
13336
12937
  return `${target.kind}#${target.lineageId}`;
13337
12938
  }
13338
12939
 
13339
- function buildUiInsightRelation(message: MessageRecord, target: InsightTarget, now: string): SemanticRelationRecord {
12940
+ function buildUiNoteRelation(message: MessageRecord, target: NoteTarget, now: string): SemanticRelationRecord {
13340
12941
  const subjectStateKey = semanticStateKey("message", message.id);
13341
12942
  const objectStateKey = semanticStateKey(target.kind, target.lineageId);
13342
12943
  const subjectVersionKey = semanticVersionKey("message", message.id);
13343
12944
  const objectVersionKey = semanticVersionKey(target.kind, target.id);
13344
12945
  return {
13345
- id: `semantic-relation-${hashUiKey([subjectVersionKey, "insight_about", objectVersionKey])}`,
12946
+ id: `semantic-relation-${hashUiKey([subjectVersionKey, "note_about", objectVersionKey])}`,
13346
12947
  relationState: "current",
13347
- predicate: "insight_about",
13348
- relationTypeId: "semantic-relation-type-insight-about",
13349
- relationTypeKey: "insight_about",
12948
+ predicate: "note_about",
12949
+ relationTypeId: "semantic-relation-type-note-about",
12950
+ relationTypeKey: "note_about",
13350
12951
  relationDomain: "knowledge",
13351
12952
  subjectKind: "message",
13352
12953
  subjectId: message.id,
@@ -13359,7 +12960,7 @@ function buildUiInsightRelation(message: MessageRecord, target: InsightTarget, n
13359
12960
  subjectStateKey,
13360
12961
  objectStateKey,
13361
12962
  objectSubjectStateKey: `${objectStateKey}#message`,
13362
- predicateObjectStateKey: `insight_about#${objectStateKey}`,
12963
+ predicateObjectStateKey: `note_about#${objectStateKey}`,
13363
12964
  subjectVersionKey,
13364
12965
  objectVersionKey,
13365
12966
  score: null,
@@ -13664,7 +13265,7 @@ function signupPluginEnabled(): boolean {
13664
13265
  }
13665
13266
 
13666
13267
  function normalizeAdministrationPanel(value: string | null | undefined): AdministrationPanel {
13667
- if (value === "policies" || value === "sections" || value === "procedures") return value;
13268
+ if (value === "policies" || value === "sections" || value === "procedures" || value === "delegations") return value;
13668
13269
  if (value === "signups" && signupPluginEnabled()) return value;
13669
13270
  return "users";
13670
13271
  }
@@ -13856,10 +13457,10 @@ function compareDoctrineCategories(left: DoctrineCategory, right: DoctrineCatego
13856
13457
 
13857
13458
  function formatDeskSectionHeadline(section: NewsDeskTab): string {
13858
13459
  if (section === "search") return "Search";
13859
- if (section === "topics") return "Topics";
13460
+ if (section === "desks") return "Taxonomy";
13461
+ if (section === "topics") return "Topic proposals";
13860
13462
  if (section === "concepts") return "Concepts";
13861
13463
  if (section === "references") return "References";
13862
- if (section === "insights") return "Insights";
13863
13464
  if (section === "messages") return "Messages";
13864
13465
  if (section === "assignments") return "Assignments";
13865
13466
  if (section === "administration") return "Administration";
@@ -13868,13 +13469,13 @@ function formatDeskSectionHeadline(section: NewsDeskTab): string {
13868
13469
 
13869
13470
  function formatDeskSectionLede(section: NewsDeskTab): string {
13870
13471
  if (section === "search") return "Search the newsroom knowledge base with semantic and ontology context.";
13871
- if (section === "topics") return "Review and shape the subject areas the newsroom covers.";
13472
+ if (section === "desks") return "The desks and topic categories that organize coverage, with each desk's mission and policies.";
13473
+ if (section === "topics") return "Review suggested changes to the subject areas the newsroom covers.";
13872
13474
  if (section === "concepts") return "Browse people, organizations, places, and ideas found in the source material.";
13873
13475
  if (section === "references") return "Review source materials before they become usable evidence.";
13874
- if (section === "insights") return "Browse recent research insights and discuss each one in a forum thread to refine the synthesis.";
13875
13476
  if (section === "messages") return "Read notes, rationales, forum threads, curation decisions, and other work products from people and agents.";
13876
13477
  if (section === "assignments") return "Create, filter, claim, and complete newsroom work.";
13877
- if (section === "administration") return "Manage users, roles, doctrine, configurable newspaper sections, and newsroom procedures.";
13478
+ if (section === "administration") return "Manage users, roles, doctrine, and newsroom procedures.";
13878
13479
  return "";
13879
13480
  }
13880
13481
 
@@ -14967,7 +14568,7 @@ function buildUiReportingPacketReviewPlan({
14967
14568
  event: AssignmentEventRecord;
14968
14569
  relations: SemanticRelationRecord[];
14969
14570
  } {
14970
- if (assignment.assignmentTypeKey !== "reporting.edition-candidate") throw new Error("Only reporting edition-candidate assignments can review reporting packets.");
14571
+ if (assignment.assignmentTypeKey !== "reporting.candidate") throw new Error("Only reporting candidate assignments can review reporting packets.");
14971
14572
  if (message.messageKind !== "reporting_context_packet") throw new Error("Only reporting context packets can be reviewed.");
14972
14573
  if (decision === "merge" && !targetItem?.id && !targetItemId.trim()) throw new Error("Merge Packet requires a target Item ID.");
14973
14574
  const resolvedTargetItem = targetItem ?? (targetItemId.trim() ? { id: targetItemId.trim(), lineageId: targetItemId.trim(), versionNumber: null } : null);
@@ -16503,21 +16104,21 @@ function assertReferenceQualityMutationSucceeded(
16503
16104
  return response.data;
16504
16105
  }
16505
16106
 
16506
- function assertReferenceInsightMutationSucceeded(
16507
- response: ReferenceInsightResponse,
16107
+ function assertReferenceNoteMutationSucceeded(
16108
+ response: ReferenceNoteResponse,
16508
16109
  referenceId: string,
16509
- ): NonNullable<ReferenceInsightResponse["data"]> {
16110
+ ): NonNullable<ReferenceNoteResponse["data"]> {
16510
16111
  if (response.errors?.length) {
16511
16112
  throw new Error(response.errors.map(formatGraphQLError).join("; "));
16512
16113
  }
16513
16114
  if (!response.data?.ok) {
16514
- throw new Error(`Reference insight was not saved for ${referenceId}.`);
16115
+ throw new Error(`Reference note was not saved for ${referenceId}.`);
16515
16116
  }
16516
16117
  if (response.data.referenceId && response.data.referenceId !== referenceId) {
16517
- throw new Error(`Reference insight response did not match ${referenceId}.`);
16118
+ throw new Error(`Reference note response did not match ${referenceId}.`);
16518
16119
  }
16519
16120
  if (!response.data.messageId || !response.data.relationId) {
16520
- throw new Error(`Reference insight saved without required audit relations for ${referenceId}.`);
16121
+ throw new Error(`Reference note saved without required audit relations for ${referenceId}.`);
16521
16122
  }
16522
16123
  return response.data;
16523
16124
  }