@anthusai/papyrus 1.10.0 → 1.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.
Files changed (61) hide show
  1. package/amplify/data/resource.js +3 -3
  2. package/amplify/data/resource.ts +3 -3
  3. package/amplify/functions/category-action/handler.ts +18 -18
  4. package/amplify/functions/console-chat-responder/src/main.rs +1 -1
  5. package/amplify/functions/content-actions/handler.py +23 -0
  6. package/amplify/functions/content-actions/requirements.txt +1 -1
  7. package/amplify/functions/content-actions/resource.js +1 -1
  8. package/amplify/functions/content-actions/resource.ts +1 -1
  9. package/amplify/functions/renditions/requirements.txt +1 -1
  10. package/amplify/functions/shared/slack-events.ts +2 -2
  11. package/amplify/publication/renditions-function.js +1 -1
  12. package/amplify/publication/renditions-function.ts +1 -1
  13. package/app/api/local-preview/derive/route.ts +28 -0
  14. package/app/api/local-preview/media/route.ts +25 -0
  15. package/app/globals.css +28 -28
  16. package/app/layout.tsx +8 -5
  17. package/app/newsroom/[section]/[...selection]/page.tsx +0 -1
  18. package/app/newsroom/[section]/page.tsx +0 -1
  19. package/app/newsroom/insights/[threadId]/page.tsx +5 -16
  20. package/app/newsroom/insights/page.tsx +4 -12
  21. package/components/board-card-excerpts.tsx +45 -0
  22. package/components/markus-ir-preview.tsx +47 -2
  23. package/components/news-desk-page.tsx +0 -1
  24. package/components/news-desk-taxonomy-client.ts +25 -220
  25. package/components/newsroom-article-editor.tsx +135 -39
  26. package/components/newsroom-assignment-detail.tsx +35 -1
  27. package/components/newsroom-board-view.tsx +29 -7
  28. package/components/newsroom-newsprint-board.css +9 -2
  29. package/components/newsroom-ops-overview.tsx +27 -5
  30. package/components/newsroom-organizer-shell.tsx +25 -7
  31. package/components/newsroom-organizer.css +196 -4
  32. package/components/priming-proof-page.tsx +17 -8
  33. package/components/prospectus-account-chooser.tsx +103 -0
  34. package/components/story-reporting-trail-client.ts +91 -0
  35. package/components/story-reporting-trail.tsx +115 -0
  36. package/components/topic-steering-workspace.tsx +154 -671
  37. package/components/ui/sheet.tsx +1 -0
  38. package/components/wiki-concepts-view.tsx +153 -0
  39. package/components/wiki-index-client.ts +51 -0
  40. package/lib/content-actions-client.ts +3 -0
  41. package/lib/content-actions-demo.ts +55 -3
  42. package/lib/content-item-lifecycle.ts +4 -2
  43. package/lib/front-matter-fields.ts +42 -0
  44. package/lib/local-preview-seed.ts +33 -0
  45. package/lib/newsroom-articles.ts +2 -0
  46. package/lib/newsroom-board.ts +4 -0
  47. package/lib/newsroom-forum-routes.ts +2 -43
  48. package/lib/newsroom-index-filters.ts +3 -40
  49. package/lib/newsroom-media-backend.ts +5 -0
  50. package/lib/newsroom-nav.ts +1 -3
  51. package/lib/newsroom-organizer-nav.ts +5 -12
  52. package/lib/newsroom-section-routing.ts +1 -1
  53. package/lib/newsroom-sections.ts +12 -0
  54. package/lib/papyrus-web-locations.ts +3 -24
  55. package/lib/priming-proof-report.ts +1 -0
  56. package/lib/semantic-graph.ts +5 -5
  57. package/lib/site-brand.ts +9 -0
  58. package/lib/story-reporting-trail.ts +106 -0
  59. package/lib/wiki.ts +56 -0
  60. package/package.json +12 -1
  61. package/routes.manifest.json +23 -7
@@ -1,5 +1,6 @@
1
1
  "use client";
2
2
 
3
+ import { WikiConceptsView } from "./wiki-concepts-view";
3
4
  import { Hub } from "aws-amplify/utils";
4
5
  import { fetchAuthSession } from "aws-amplify/auth";
5
6
  import { gsap } from "gsap";
@@ -34,17 +35,13 @@ import {
34
35
  loadEditorProcedureData,
35
36
  loadEditorSemanticRelationsData,
36
37
  loadEditionForumThreads,
37
- loadInsightForumThreadById,
38
- loadInsightForumThreads,
39
38
  loadNewsroomAssignmentPage,
40
39
  loadNewsroomMessagePage,
41
40
  loadNewsroomReferencePage,
42
41
  loadNewsroomSemanticNodePage,
43
42
  appendForumThreadMessageRecord,
44
- appendInsightThreadReplyRecord,
45
43
  createSectionForumThreadRecord,
46
44
  deleteForumThreadMessageRecord,
47
- deleteInsightThreadMessageRecord,
48
45
  ensureEditionForumThreadRecord,
49
46
  publishProcedureVersionRecord,
50
47
  runNewsroomKnowledgeQuery,
@@ -68,11 +65,8 @@ import {
68
65
  effectiveReferencesIndexFilters,
69
66
  normalizeReferenceIndexOrder,
70
67
  readAssignmentsIndexFilters,
71
- readInsightsIndexFilters,
72
68
  readMessagesIndexFilters,
73
69
  readReferencesIndexFilters,
74
- effectiveInsightsIndexFilters,
75
- parseInsightThreadIdFromNewsroomPathname,
76
70
  referencesStatusFromUrl,
77
71
  referencesStatusToUrl,
78
72
  type ReferenceIndexOrder,
@@ -201,12 +195,10 @@ import {
201
195
  } from "../lib/reporting-story-budget";
202
196
  import {
203
197
  buildForumThreadUrl,
204
- buildInsightThreadUrl,
205
198
  getForumMessageAnchorId,
206
199
  isForumThreadId,
207
200
  parseForumMessageAnchorFromHash,
208
201
  pushForumThreadUrl,
209
- pushInsightThreadUrl,
210
202
  readCurrentForumRoute,
211
203
  } from "../lib/newsroom-forum-routes";
212
204
 
@@ -247,7 +239,7 @@ type ReportingPacketReviewDecision = "select" | "merge" | "brief" | "hold" | "ki
247
239
  type AssignmentDeskViewMode = "queue" | "budget";
248
240
  type UserRoleAction = "grant" | "revoke";
249
241
  type AdministrationPanel = "users" | "policies" | "sections" | "procedures" | "signups";
250
- export type NewsDeskTab = "overview" | "desks" | "topics" | "concepts" | "references" | "insights" | "messages" | "assignments" | "administration" | "search";
242
+ export type NewsDeskTab = "overview" | "desks" | "topics" | "concepts" | "references" | "messages" | "assignments" | "administration" | "search";
251
243
  type LexicalRuleScope = "publication" | "corpus" | "classifier" | "category";
252
244
  type AnalysisReindexMode = "online-update" | "classifier-retrain" | "scoped-topic-rebuild" | "entity-graph-rebuild" | "generated-analysis-rebuild";
253
245
  type ReferenceProcessingStatus = "created" | "processable" | "processed" | "blocked";
@@ -324,7 +316,7 @@ type NewsroomDetailAction = {
324
316
  onSelect: () => void;
325
317
  };
326
318
 
327
- type InsightTarget = {
319
+ type NoteTarget = {
328
320
  kind: "assignment" | "category" | "item" | "newsroomSection" | "reference" | "semanticNode";
329
321
  id: string;
330
322
  lineageId: string;
@@ -333,7 +325,7 @@ type InsightTarget = {
333
325
  versionNumber?: number | null;
334
326
  };
335
327
 
336
- type InsightComposerControl = {
328
+ type NoteComposerControl = {
337
329
  action: NewsroomDetailAction;
338
330
  dialog: ReactNode;
339
331
  error: string | null;
@@ -460,7 +452,7 @@ type ReferenceQualityResponse = {
460
452
  errors?: Array<{ message?: string | null } | string | null> | null;
461
453
  };
462
454
 
463
- type ReferenceInsightResponse = {
455
+ type ReferenceNoteResponse = {
464
456
  data?: {
465
457
  ok?: boolean | null;
466
458
  referenceId?: string | null;
@@ -927,9 +919,6 @@ function NewsDeskDashboard({
927
919
  overview: 0,
928
920
  desks: summaryCountFromRecord(summary, "categories"),
929
921
  messages: summaryCountFromRecord(summary, "messages"),
930
- insights: summary?.facets?.messages?.byKind?.insight
931
- ?? summary?.messageKindCounts?.insight
932
- ?? null,
933
922
  assignments: summaryCountFromRecord(summary, "assignments"),
934
923
  references: summaryCountFromRecord(summary, "references")
935
924
  ?? (hasHydratedReferences ? references.length : null),
@@ -1895,11 +1884,11 @@ function NewsDeskDashboard({
1895
1884
  });
1896
1885
  }
1897
1886
 
1898
- async function createInsight(target: InsightTarget, summary: string, body: string): Promise<void> {
1887
+ async function createNote(target: NoteTarget, summary: string, body: string): Promise<void> {
1899
1888
  const cleanSummary = summary.trim();
1900
1889
  const cleanBody = body.trim();
1901
- if (!cleanSummary) throw new Error("Insight summary is required.");
1902
- if (!cleanBody) throw new Error("Insight body is required.");
1890
+ if (!cleanSummary) throw new Error("Note summary is required.");
1891
+ if (!cleanBody) throw new Error("Note body is required.");
1903
1892
  const now = new Date().toISOString();
1904
1893
  const subjectSeed = [
1905
1894
  target.kind,
@@ -1908,10 +1897,10 @@ function NewsDeskDashboard({
1908
1897
  cleanBody,
1909
1898
  now,
1910
1899
  ];
1911
- const messageId = `message-insight-${safeUiId(target.kind)}-${safeUiId(target.lineageId)}-${hashUiKey(subjectSeed)}`;
1900
+ const messageId = `message-note-${safeUiId(target.kind)}-${safeUiId(target.lineageId)}-${hashUiKey(subjectSeed)}`;
1912
1901
  const message: MessageRecord = {
1913
1902
  id: messageId,
1914
- messageKind: "insight",
1903
+ messageKind: "note",
1915
1904
  messageDomain: "knowledge",
1916
1905
  status: "active",
1917
1906
  summary: cleanSummary,
@@ -1929,18 +1918,18 @@ function NewsDeskDashboard({
1929
1918
  targetLineageId: target.lineageId,
1930
1919
  },
1931
1920
  };
1932
- const relation = buildUiInsightRelation(message, target, now);
1933
- setActionState({ id: messageId, message: "insight pending", tone: "pending" });
1921
+ const relation = buildUiNoteRelation(message, target, now);
1922
+ setActionState({ id: messageId, message: "note pending", tone: "pending" });
1934
1923
 
1935
1924
  if (dashboard.isDemo) {
1936
1925
  setMessages((current) => [message, ...current.filter((entry) => entry.id !== message.id)]);
1937
1926
  setSemanticRelations((current) => [relation, ...current.filter((entry) => entry.id !== relation.id)]);
1938
- setActionState({ id: messageId, message: "insight saved", tone: "ok" });
1927
+ setActionState({ id: messageId, message: "note saved", tone: "ok" });
1939
1928
  return;
1940
1929
  }
1941
1930
 
1942
1931
  if (target.kind === "reference") {
1943
- const response = await dataClient.mutations.createReferenceInsight(
1932
+ const response = await dataClient.mutations.createReferenceNote(
1944
1933
  {
1945
1934
  referenceId: target.id,
1946
1935
  summary: cleanSummary,
@@ -1949,7 +1938,7 @@ function NewsDeskDashboard({
1949
1938
  },
1950
1939
  { authMode: USER_POOL_AUTH_MODE },
1951
1940
  );
1952
- const created = assertReferenceInsightMutationSucceeded(response as ReferenceInsightResponse, target.id);
1941
+ const created = assertReferenceNoteMutationSucceeded(response as ReferenceNoteResponse, target.id);
1953
1942
  const persistedMessageId = created.messageId ?? messageId;
1954
1943
  const persistedRelationId = created.relationId ?? relation.id;
1955
1944
  const persistedMessage: MessageRecord = {
@@ -1966,7 +1955,7 @@ function NewsDeskDashboard({
1966
1955
  };
1967
1956
  setMessages((current) => [persistedMessage, ...current.filter((entry) => entry.id !== persistedMessage.id)]);
1968
1957
  setSemanticRelations((current) => [persistedRelation, ...current.filter((entry) => entry.id !== persistedRelation.id)]);
1969
- setActionState({ id: persistedMessageId, message: "insight saved", tone: "ok" });
1958
+ setActionState({ id: persistedMessageId, message: "note saved", tone: "ok" });
1970
1959
  return;
1971
1960
  }
1972
1961
 
@@ -2004,7 +1993,7 @@ function NewsDeskDashboard({
2004
1993
  assertNoGraphQLErrors(relationResponse.errors);
2005
1994
  setMessages((current) => [message, ...current.filter((entry) => entry.id !== message.id)]);
2006
1995
  setSemanticRelations((current) => [relation, ...current.filter((entry) => entry.id !== relation.id)]);
2007
- setActionState({ id: messageId, message: "insight saved", tone: "ok" });
1996
+ setActionState({ id: messageId, message: "note saved", tone: "ok" });
2008
1997
  }
2009
1998
 
2010
1999
  function createLexicalSteeringRule(draft: LexicalRuleDraft) {
@@ -3073,7 +3062,7 @@ function NewsDeskDashboard({
3073
3062
 
3074
3063
  const shellTabCounts = useMemo(() => {
3075
3064
  const entries: Partial<Record<string, NewsroomNavCount>> = {};
3076
- for (const tab of ["overview", "messages", "insights", "assignments", "references", "topics", "concepts", "administration"] as NewsDeskTab[]) {
3065
+ for (const tab of ["overview", "messages", "assignments", "references", "topics", "concepts", "administration"] as NewsDeskTab[]) {
3077
3066
  if (tab === "administration") continue;
3078
3067
  entries[tab] = {
3079
3068
  count: tabCounts[tab],
@@ -3087,7 +3076,9 @@ function NewsDeskDashboard({
3087
3076
  ? mastheadTitle
3088
3077
  : activeTab === "overview"
3089
3078
  ? "Overview"
3090
- : formatDeskSectionHeadline(activeTab);
3079
+ : activeTab === "administration" && administrationPanel === "sections"
3080
+ ? "Sections"
3081
+ : formatDeskSectionHeadline(activeTab);
3091
3082
 
3092
3083
  return (
3093
3084
  <>
@@ -3124,8 +3115,8 @@ function NewsDeskDashboard({
3124
3115
  >
3125
3116
  {activeTab !== "overview" && activeTab !== "assignments" && activeTab !== "messages" && activeTab !== "references" && activeTab !== "topics" && activeTab !== "concepts" && activeTab !== "search" ? (
3126
3117
  <NewsroomOpsSectionIntro
3127
- description={formatDeskSectionLede(activeTab)}
3128
- title={formatDeskSectionHeadline(activeTab)}
3118
+ description={activeTab === "administration" && administrationPanel === "sections" ? "Organize the publication's sections and the ontology that classifies its coverage." : formatDeskSectionLede(activeTab)}
3119
+ title={activeTab === "administration" && administrationPanel === "sections" ? "Sections" : formatDeskSectionHeadline(activeTab)}
3129
3120
  />
3130
3121
  ) : null}
3131
3122
 
@@ -3145,7 +3136,7 @@ function NewsDeskDashboard({
3145
3136
  <DeepNewsroomSectionView
3146
3137
  demo={Boolean(dashboard.isDemo)}
3147
3138
  disabled={controlsDisabled}
3148
- onCreateInsight={createInsight}
3139
+ onCreateNote={createNote}
3149
3140
  section={activeNewsroomSection}
3150
3141
  sectionId={sectionPageId ?? ""}
3151
3142
  sectionsLoaded={loadedSections.sections}
@@ -3171,6 +3162,12 @@ function NewsDeskDashboard({
3171
3162
  semanticNodes={semanticNodes}
3172
3163
  />
3173
3164
  ) : null}
3165
+ {!isSectionPage && (activeTab === "desks" || activeTab === "topics") ? (
3166
+ <nav aria-label="Taxonomy views" className="taxonomy-view-switch" data-taxonomy-view-switch>
3167
+ <Link aria-current={activeTab === "desks" ? "page" : undefined} href={getNewsroomNavHref("/newsroom/desks", Boolean(dashboard.isDemo))}>Categories</Link>
3168
+ <Link aria-current={activeTab === "topics" ? "page" : undefined} href={getNewsroomNavHref("/newsroom/topics", Boolean(dashboard.isDemo))}>Topic proposals</Link>
3169
+ </nav>
3170
+ ) : null}
3174
3171
  {!isSectionPage && activeTab === "desks" ? (
3175
3172
  <DesksDeskView
3176
3173
  assignments={assignments}
@@ -3198,14 +3195,10 @@ function NewsDeskDashboard({
3198
3195
  ) : null}
3199
3196
  {!isSectionPage && activeTab === "concepts" ? (
3200
3197
  <ConceptsDeskView
3201
- categories={mergeCategoryRecords(categorys, activeCategoryTreeNodes)}
3202
- disabled={controlsDisabled}
3203
3198
  graph={graph}
3204
- initialCategoryLineageId={initialSelection.category}
3205
3199
  initialNodeLineageId={initialSelection.node}
3206
- onCreateInsight={createInsight}
3200
+ isDemo={Boolean(dashboard.isDemo)}
3207
3201
  semanticNodes={semanticNodes}
3208
- summary={summary}
3209
3202
  />
3210
3203
  ) : null}
3211
3204
  {!isSectionPage && activeTab === "references" ? (
@@ -3241,15 +3234,6 @@ function NewsDeskDashboard({
3241
3234
  references={references}
3242
3235
  />
3243
3236
  ) : null}
3244
- {!isSectionPage && activeTab === "insights" ? (
3245
- <InsightsDeskView
3246
- graph={graph}
3247
- initialThreadId={initialSelection.forumThread}
3248
- isDemo={Boolean(dashboard.isDemo)}
3249
- messages={messages}
3250
- summary={summary}
3251
- />
3252
- ) : null}
3253
3237
  {!isSectionPage && activeTab === "messages" ? (
3254
3238
  <MessagesDeskView
3255
3239
  assignments={assignments}
@@ -4193,20 +4177,20 @@ function RotatingSectionTriangleIcon({ expanded }: { expanded: boolean }) {
4193
4177
  function DeepNewsroomSectionView({
4194
4178
  demo,
4195
4179
  disabled,
4196
- onCreateInsight,
4180
+ onCreateNote,
4197
4181
  section,
4198
4182
  sectionId,
4199
4183
  sectionsLoaded,
4200
4184
  }: {
4201
4185
  demo: boolean;
4202
4186
  disabled: boolean;
4203
- onCreateInsight: (target: InsightTarget, summary: string, body: string) => Promise<void>;
4187
+ onCreateNote: (target: NoteTarget, summary: string, body: string) => Promise<void>;
4204
4188
  section: NewsroomSectionRecord | null;
4205
4189
  sectionId: string;
4206
4190
  sectionsLoaded: boolean;
4207
4191
  }) {
4208
- const insightTarget = section ? insightTargetForNewsroomSection(section) : null;
4209
- const insightComposer = useNewsroomInsightComposer(insightTarget, disabled, onCreateInsight);
4192
+ const noteTarget = section ? noteTargetForNewsroomSection(section) : null;
4193
+ const noteComposer = useNewsroomNoteComposer(noteTarget, disabled, onCreateNote);
4210
4194
 
4211
4195
  if (!section) {
4212
4196
  return (
@@ -4235,13 +4219,13 @@ function DeepNewsroomSectionView({
4235
4219
  <h2 data-newsroom-deep-section-title>{section.title}</h2>
4236
4220
  <p>{displayNewsroomSectionShortTitle(section)}</p>
4237
4221
  <div className="newsroom-deep-section__header-actions">
4238
- <button type="button" disabled={insightComposer.action.disabled} onClick={insightComposer.action.onSelect}>
4239
- {insightComposer.loading ? "Saving Insight" : "Add Insight"}
4222
+ <button type="button" disabled={noteComposer.action.disabled} onClick={noteComposer.action.onSelect}>
4223
+ {noteComposer.loading ? "Saving Note" : "Add Note"}
4240
4224
  </button>
4241
4225
  <Link href={getNewsDeskTabHref("/newsroom", demo)}>Back to Newsroom</Link>
4242
4226
  </div>
4243
4227
  </header>
4244
- {insightComposer.error ? <div className="category-steering-alert" role="status">{insightComposer.error}</div> : null}
4228
+ {noteComposer.error ? <div className="category-steering-alert" role="status">{noteComposer.error}</div> : null}
4245
4229
  <div className="newsroom-deep-section__body">
4246
4230
  <article className="newsroom-deep-section__field newsroom-deep-section__field--lead">
4247
4231
  <p className="story-label">Mission</p>
@@ -4258,7 +4242,7 @@ function DeepNewsroomSectionView({
4258
4242
  </article>
4259
4243
  ))}
4260
4244
  </div>
4261
- {insightComposer.dialog}
4245
+ {noteComposer.dialog}
4262
4246
  </section>
4263
4247
  );
4264
4248
  }
@@ -4449,9 +4433,9 @@ function AdministrationDeskView({
4449
4433
  <div className="news-desk-columns news-desk-columns--administration" data-news-desk-section="administration">
4450
4434
  <div className="news-desk-main-column">
4451
4435
  <section className="category-steering-section category-steering-section--lead" aria-labelledby="newsroom-administration-title">
4452
- <SectionHeader title="Administration" detail="Settings for users, policies, sections, and procedures" />
4453
- <div className="news-desk-settings-shell">
4454
- <nav className="news-desk-settings-nav" aria-label="Administration settings">
4436
+ <SectionHeader title={administrationPanel === "sections" ? "Sections" : "Administration"} detail={administrationPanel === "sections" ? "The publication's sections and ontology" : "Settings for users, policies, and procedures"} />
4437
+ <div className="news-desk-settings-shell" data-settings-nav={administrationPanel === "sections" ? "hidden" : undefined}>
4438
+ {administrationPanel === "sections" ? null : <nav className="news-desk-settings-nav" aria-label="Administration settings">
4455
4439
  <Link
4456
4440
  href={administrationPanelHref("users", isDemo)}
4457
4441
  data-news-desk-admin-nav="users"
@@ -4470,15 +4454,6 @@ function AdministrationDeskView({
4470
4454
  <strong>Policies</strong>
4471
4455
  <span>Publication and category doctrine</span>
4472
4456
  </Link>
4473
- <Link
4474
- href={administrationPanelHref("sections", isDemo)}
4475
- data-news-desk-admin-nav="sections"
4476
- data-active={administrationPanel === "sections" || undefined}
4477
- onClick={() => onPanelChange("sections")}
4478
- >
4479
- <strong>Sections</strong>
4480
- <span>Canonical and floating newspaper sections</span>
4481
- </Link>
4482
4457
  <Link
4483
4458
  href={administrationPanelHref("procedures", isDemo)}
4484
4459
  data-news-desk-admin-nav="procedures"
@@ -4499,7 +4474,7 @@ function AdministrationDeskView({
4499
4474
  <span>Addresses from the site&apos;s signup forms</span>
4500
4475
  </Link>
4501
4476
  ) : null}
4502
- </nav>
4477
+ </nav>}
4503
4478
  <div className="news-desk-settings-panel">
4504
4479
  {administrationPanel === "signups" ? <SignupEntriesPanel /> : null}
4505
4480
  {administrationPanel === "users" ? (
@@ -4987,7 +4962,7 @@ function AdministrationSectionsPanel({
4987
4962
 
4988
4963
  return (
4989
4964
  <div data-news-desk-admin-panel="sections">
4990
- <SectionHeader title="Newspaper Sections" detail={`${sortedSections.length} configured sections`} />
4965
+ <SectionHeader title="Sections" detail={`${sortedSections.length} configured sections`} />
4991
4966
  <div className="news-desk-administration-category-layout">
4992
4967
  <div className="news-desk-administration-category-list" data-news-desk-section-list>
4993
4968
  {sortedSections.length ? (
@@ -5824,7 +5799,7 @@ function DesksDeskView({
5824
5799
  }, [initialRootKey, roots, selectedRoot, selectedRootKey]);
5825
5800
 
5826
5801
  return (
5827
- <div className="news-desk-columns" data-news-desk-section="desks">
5802
+ <div className="news-desk-columns news-desk-columns--administration news-desk-columns--taxonomy" data-news-desk-section="desks">
5828
5803
  <div className="news-desk-main-column">
5829
5804
  <section className="category-steering-section category-steering-section--lead" aria-labelledby="newsroom-desks-title">
5830
5805
  <SectionHeader title="News Desks" detail={`${roots.length} root topic desks`} />
@@ -6374,148 +6349,27 @@ function TopicProposalEditModal({
6374
6349
  }
6375
6350
 
6376
6351
  function ConceptsDeskView({
6377
- categories,
6378
- disabled,
6379
6352
  graph,
6380
- initialCategoryLineageId,
6381
6353
  initialNodeLineageId,
6382
- onCreateInsight,
6354
+ isDemo,
6383
6355
  semanticNodes,
6384
- summary,
6385
6356
  }: {
6386
- categories: CategorySteeringCategory[];
6387
- disabled: boolean;
6388
6357
  graph: SemanticGraph;
6389
- initialCategoryLineageId?: string | null;
6390
6358
  initialNodeLineageId?: string | null;
6391
- onCreateInsight: (target: InsightTarget, summary: string, body: string) => Promise<void>;
6359
+ isDemo: boolean;
6392
6360
  semanticNodes: SemanticNodeRecord[];
6393
- summary?: NewsroomSummaryRecord | null;
6394
6361
  }) {
6395
- const [nodeKindFilter, setNodeKindFilter] = useState("");
6396
- const [nodeStatusFilter, setNodeStatusFilter] = useState("");
6397
- const [selectedNodeLineageId, setSelectedNodeLineageId] = useState(initialNodeLineageId ?? "");
6398
- const [isConceptDetailOpen, setIsConceptDetailOpen] = useState(Boolean(initialNodeLineageId));
6399
- const categoryContext = useMemo(() => buildCategoryDrilldownContext(categories, initialCategoryLineageId), [categories, initialCategoryLineageId]);
6400
6362
  const feed = useNewsroomPagedRows({
6401
6363
  initialItems: semanticNodes,
6402
- enabled: !categoryContext?.primary,
6403
- resetKey: `semanticNodes:${nodeKindFilter}:${nodeStatusFilter}`,
6404
- loadPage: (nextToken) => loadNewsroomSemanticNodePage({
6405
- nodeKind: nodeKindFilter,
6406
- status: nodeStatusFilter,
6407
- nextToken,
6408
- }),
6364
+ enabled: true,
6365
+ resetKey: "semanticNodes:wiki",
6366
+ loadPage: (nextToken) => loadNewsroomSemanticNodePage({ nodeKind: "", status: "", nextToken }),
6409
6367
  });
6410
- const categoryFilter = categoryContext.primary ? graph.resolve("category", categoryLineageId(categoryContext.primary)) : null;
6411
- const categoryConceptLineages = useMemo(() => (
6412
- categoryContext.primary
6413
- ? new Set(semanticNodesForCategoryContext(graph, categoryContext).map((node) => node.lineageId))
6414
- : null
6415
- ), [categoryContext, graph]);
6416
- const feedNodes = useMemo(() => (
6417
- feed.items.length
6418
- ? feed.items
6419
- : (feed.isLoading || Boolean(feed.error))
6420
- ? semanticNodes
6421
- : feed.items
6422
- ), [feed.error, feed.isLoading, feed.items, semanticNodes]);
6423
- const visibleNodes = useMemo(() => (
6424
- categoryConceptLineages
6425
- ? semanticNodes.filter((node) => categoryConceptLineages.has(node.lineageId ?? node.id))
6426
- : feedNodes
6427
- ), [categoryConceptLineages, feedNodes, semanticNodes]);
6428
- const fallbackConceptCategories = useMemo(() => (
6429
- visibleNodes.length
6430
- ? []
6431
- : categories.filter((category) => category.status !== "deprecated" && category.status !== "archived")
6432
- ), [categories, visibleNodes.length]);
6433
- const requestedNodeLineageId = selectedNodeLineageId || initialNodeLineageId || "";
6434
- const selected = selectSemanticNodeSummary(graph, visibleNodes, requestedNodeLineageId)
6435
- ?? (requestedNodeLineageId ? selectSemanticNodeSummary(graph, semanticNodes, requestedNodeLineageId) : null)
6436
- ?? (requestedNodeLineageId ? selectCategorySummary(graph, categories, requestedNodeLineageId) : null)
6437
- ?? categoryFilter
6438
- ?? selectSemanticNodeSummary(graph, visibleNodes)
6439
- ?? selectCategorySummary(graph, fallbackConceptCategories);
6440
- const conceptKnowledgeQuery = useNewsroomKnowledgeContext(selected ? {
6441
- anchor: {
6442
- kind: selected.kind === "category" ? "category" : "semanticNode",
6443
- id: selected.id ?? selected.lineageId,
6444
- lineageId: selected.lineageId,
6445
- },
6446
- title: selected.label,
6447
- subtitle: selected.subtitle,
6448
- } : null);
6449
- const conceptInsight = useNewsroomInsightComposer(
6450
- selected && (selected.kind === "category" || selected.kind === "semanticNode") ? insightTargetForSemanticObject(selected) : null,
6451
- disabled,
6452
- onCreateInsight,
6453
- );
6454
- const detail = categoryFilter
6455
- ? `${visibleNodes.length} graph nodes associated with ${categoryFilter.label}`
6456
- : visibleNodes.length
6457
- ? `${summaryCountFromRecord(summary, "semanticNodes") || visibleNodes.length} graph nodes`
6458
- : `${fallbackConceptCategories.length} taxonomy concepts`;
6459
- const nodeKindCounts = categoryFilter ? countSemanticNodesByKind(visibleNodes) : summary?.facets?.semanticNodes?.byNodeKind ?? countSemanticNodesByKind(visibleNodes);
6460
- const nodeStatusCounts = categoryFilter ? countSemanticNodesByStatus(visibleNodes) : summary?.facets?.semanticNodes?.byStatus ?? countSemanticNodesByStatus(visibleNodes);
6461
- const nodeKinds = sortedCountOptions(nodeKindCounts).slice(0, 8);
6462
- const cards = visibleNodes.length
6463
- ? visibleNodes.map((node, index) => semanticNodeToNewsroomCard(node, index))
6464
- : fallbackConceptCategories.map((category, index) => categoryToConceptNewsroomCard(category, index));
6465
- const cardIds = useMemo(() => new Set(cards.map((card) => card.id)), [cards]);
6466
- const selectedLineageId = selected && cardIds.has(selected.lineageId) ? selected.lineageId : null;
6467
- const selectNode = (lineageId: string) => {
6468
- setSelectedNodeLineageId(lineageId);
6469
- setIsConceptDetailOpen(true);
6470
- pushNewsroomDetailUrl("concepts", lineageId, false);
6471
- };
6472
- return (
6473
- <>
6474
- <NewsroomListDetailShell
6475
- animatedDetail
6476
- sectionKey="concepts"
6477
- canExpandDetail={Boolean(selected)}
6478
- detailOpen={isConceptDetailOpen}
6479
- selectionScrollKey={selectedLineageId}
6480
- actions={[conceptInsight.action]}
6481
- utilityActions={[conceptKnowledgeQuery.action]}
6482
- lede={(
6483
- <NewsroomDeskSectionLede headingId="semantic-concepts-title" section="concepts" />
6484
- )}
6485
- list={(
6486
- <section className="category-steering-section category-steering-section--lead" aria-label={detail}>
6487
- <NewsroomCardGrid
6488
- cards={cards}
6489
- emptyLabel={feed.isLoading ? "Loading semantic concepts" : feed.error ?? "No semantic nodes imported"}
6490
- filterLabel="Concept kind"
6491
- filterOptions={[
6492
- { key: "", label: "All concept kinds", count: summaryCountFromRecord(summary, "semanticNodes") || visibleNodes.length },
6493
- ...nodeKinds.map((option) => ({ key: option.key, label: formatAssignmentTypeLabel(option.key), count: option.count })),
6494
- ]}
6495
- filterValue={nodeKindFilter}
6496
- footerLabel={feed.error ?? undefined}
6497
- hasMore={!categoryFilter && feed.hasMore}
6498
- isLoadingMore={feed.isLoadingMore}
6499
- metricValue={nodeStatusFilter}
6500
- metrics={[
6501
- { key: "", label: "All", count: summaryCountFromRecord(summary, "semanticNodes") || visibleNodes.length },
6502
- ...sortedCountOptions(nodeStatusCounts).map((option) => ({ key: option.key, label: option.key, count: option.count })),
6503
- ]}
6504
- onFilterChange={setNodeKindFilter}
6505
- onLoadMore={feed.loadMore}
6506
- onMetricChange={setNodeStatusFilter}
6507
- onSelect={selectNode}
6508
- selectedId={selectedLineageId}
6509
- />
6510
- </section>
6511
- )}
6512
- onCloseDetail={() => setIsConceptDetailOpen(false)}
6513
- detail={<SemanticDetailPanel graph={graph} selected={selected} knowledgeQuery={conceptKnowledgeQuery} />}
6514
- />
6515
- {conceptKnowledgeQuery.dialog}
6516
- {conceptInsight.dialog}
6517
- </>
6518
- );
6368
+ useEffect(() => {
6369
+ if (feed.hasMore && !feed.isLoadingMore) feed.loadMore();
6370
+ }, [feed]);
6371
+ const nodes = feed.items.length ? feed.items : semanticNodes;
6372
+ return <WikiConceptsView demo={isDemo} graph={graph} initialNodeLineageId={initialNodeLineageId} nodes={nodes} />;
6519
6373
  }
6520
6374
 
6521
6375
  function ReferencesDeskView({
@@ -6529,7 +6383,7 @@ function ReferencesDeskView({
6529
6383
  initialReferenceLineageId,
6530
6384
  isDemo,
6531
6385
  deepLinkFetchEnabled = true,
6532
- onCreateInsight,
6386
+ onCreateNote,
6533
6387
  onHydrateReference,
6534
6388
  onMoveCorpus,
6535
6389
  onReview,
@@ -6554,7 +6408,7 @@ function ReferencesDeskView({
6554
6408
  initialReferenceLineageId?: string | null;
6555
6409
  isDemo?: boolean;
6556
6410
  deepLinkFetchEnabled?: boolean;
6557
- onCreateInsight: (target: InsightTarget, summary: string, body: string) => Promise<void>;
6411
+ onCreateNote: (target: NoteTarget, summary: string, body: string) => Promise<void>;
6558
6412
  onHydrateReference?: (reference: ReferenceRecord) => void;
6559
6413
  onMoveCorpus: (reference: ReferenceRecord, corpusId: string) => void;
6560
6414
  onReview: (reference: ReferenceRecord, action: ReferenceCurationAction, note?: string, reasonCode?: ReferenceRejectionReasonCode | null) => void;
@@ -6958,7 +6812,7 @@ function ReferencesDeskView({
6958
6812
  corpora={corpora}
6959
6813
  disabled={disabled}
6960
6814
  graph={graph}
6961
- onCreateInsight={onCreateInsight}
6815
+ onCreateNote={onCreateNote}
6962
6816
  onMoveCorpus={onMoveCorpus}
6963
6817
  onReview={runReferenceAction}
6964
6818
  onReviewTopicLabel={onReviewTopicLabel}
@@ -7010,365 +6864,6 @@ function NewsroomDeskSectionLede({
7010
6864
  );
7011
6865
  }
7012
6866
 
7013
- function InsightsDeskView({
7014
- graph,
7015
- initialThreadId = null,
7016
- isDemo = false,
7017
- messages,
7018
- summary,
7019
- }: {
7020
- graph: SemanticGraph;
7021
- initialThreadId?: string | null;
7022
- isDemo?: boolean;
7023
- messages: MessageRecord[];
7024
- summary?: NewsroomSummaryRecord | null;
7025
- }) {
7026
- const pathname = usePathname();
7027
- const pathnameThreadId = useMemo(
7028
- () => parseInsightThreadIdFromNewsroomPathname(pathname),
7029
- [pathname],
7030
- );
7031
- const resolvedInitialThreadId = initialThreadId ?? pathnameThreadId ?? "";
7032
- const forumMessageAnchorId = useForumMessageAnchorId();
7033
- const [domainFilter, setDomainFilter] = useState(() => {
7034
- if (typeof window === "undefined") return "";
7035
- return readInsightsIndexFilters(new URLSearchParams(window.location.search)).domain;
7036
- });
7037
- const [threads, setThreads] = useState<ForumThreadWithMessages[]>([]);
7038
- const [threadsLoading, setThreadsLoading] = useState(false);
7039
- const [threadsError, setThreadsError] = useState<string | null>(null);
7040
- const [nextToken, setNextToken] = useState<string | null>(null);
7041
- const [selectedThreadId, setSelectedThreadId] = useState(resolvedInitialThreadId);
7042
- const [forumView, setForumView] = useState<ForumViewState>(
7043
- resolvedInitialThreadId ? { mode: "thread", threadId: resolvedInitialThreadId } : { mode: "index" },
7044
- );
7045
- const [replyParentId, setReplyParentId] = useState("");
7046
- const [composeSummary, setComposeSummary] = useState("");
7047
- const [composeContent, setComposeContent] = useState("");
7048
- const [composeError, setComposeError] = useState<string | null>(null);
7049
- const [deletingMessageId, setDeletingMessageId] = useState("");
7050
-
7051
- const syncInsightsIndexUrl = useCallback((nextDomain: string, replace = true) => {
7052
- if (isDemo || forumView.mode === "thread") return;
7053
- syncBrowserNewsroomIndexUrl("insights", effectiveInsightsIndexFilters({ domain: nextDomain }), { replace });
7054
- }, [forumView.mode, isDemo]);
7055
-
7056
- useEffect(() => {
7057
- if (isDemo || forumView.mode === "thread") return;
7058
- syncInsightsIndexUrl(domainFilter, true);
7059
- }, [domainFilter, forumView.mode, isDemo, syncInsightsIndexUrl]);
7060
-
7061
- const refreshThreads = useCallback(async (options?: { append?: boolean; token?: string | null }) => {
7062
- if (isDemo) {
7063
- const demoThreads = messages
7064
- .filter((message) => message.messageKind === "insight")
7065
- .filter((message) => !domainFilter || message.messageDomain === domainFilter)
7066
- .map((message) => buildDemoInsightForumThread(message, messages));
7067
- setThreads(demoThreads);
7068
- setThreadsError(null);
7069
- setNextToken(null);
7070
- return;
7071
- }
7072
- setThreadsLoading(true);
7073
- try {
7074
- const result = await loadInsightForumThreads({
7075
- domain: domainFilter,
7076
- nextToken: options?.token ?? null,
7077
- includeMessages: true,
7078
- });
7079
- setThreads((current) => (options?.append ? [...current, ...result.threads] : result.threads));
7080
- setNextToken(result.nextToken);
7081
- setThreadsError(null);
7082
- setSelectedThreadId((current) => (current && result.threads.some((thread) => thread.id === current) ? current : current));
7083
- } catch (error) {
7084
- setThreads([]);
7085
- setThreadsError(error instanceof Error ? error.message : "Could not load insight threads.");
7086
- setNextToken(null);
7087
- } finally {
7088
- setThreadsLoading(false);
7089
- }
7090
- }, [domainFilter, isDemo, messages]);
7091
-
7092
- useEffect(() => {
7093
- void refreshThreads();
7094
- }, [refreshThreads]);
7095
-
7096
- useEffect(() => {
7097
- if (!resolvedInitialThreadId || threadsLoading) return;
7098
- if (!threads.some((thread) => thread.id === resolvedInitialThreadId)) return;
7099
- if (selectedThreadId === resolvedInitialThreadId && forumView.mode === "thread") return;
7100
- setSelectedThreadId(resolvedInitialThreadId);
7101
- setForumView({ mode: "thread", threadId: resolvedInitialThreadId });
7102
- }, [forumView.mode, resolvedInitialThreadId, selectedThreadId, threads, threadsLoading]);
7103
-
7104
- useEffect(() => {
7105
- const handlePopState = () => {
7106
- const route = readCurrentForumRoute();
7107
- if (route.surface === "insight_forum" && route.threadId) {
7108
- setSelectedThreadId(route.threadId);
7109
- setForumView({ mode: "thread", threadId: route.threadId });
7110
- return;
7111
- }
7112
- setSelectedThreadId("");
7113
- setForumView({ mode: "index" });
7114
- };
7115
- window.addEventListener("popstate", handlePopState);
7116
- return () => window.removeEventListener("popstate", handlePopState);
7117
- }, []);
7118
-
7119
- const selectedThread = threads.find((thread) => thread.id === selectedThreadId) ?? null;
7120
- const domainOptions = useMemo(() => {
7121
- const domains = new Set<string>();
7122
- for (const message of messages) {
7123
- if (message.messageKind === "insight" && message.messageDomain) domains.add(message.messageDomain);
7124
- }
7125
- for (const thread of threads) {
7126
- if (thread.sectionKey) domains.add(thread.sectionKey);
7127
- }
7128
- return sortedCountOptions(
7129
- Object.fromEntries([...domains].map((domain) => [domain, threads.filter((thread) => thread.sectionKey === domain).length])),
7130
- );
7131
- }, [messages, threads]);
7132
-
7133
- const openThread = useCallback((threadId: string, options?: { messageId?: string | null; replace?: boolean }) => {
7134
- setSelectedThreadId(threadId);
7135
- setForumView({ mode: "thread", threadId });
7136
- pushInsightThreadUrl(threadId, {
7137
- demo: isDemo,
7138
- messageId: options?.messageId,
7139
- replace: options?.replace,
7140
- });
7141
- }, [isDemo]);
7142
-
7143
- const closeThread = useCallback(() => {
7144
- setSelectedThreadId("");
7145
- setForumView({ mode: "index" });
7146
- pushInsightThreadUrl(null, { demo: isDemo, replace: true });
7147
- }, [isDemo]);
7148
-
7149
- const postThreadMessage = useCallback(async () => {
7150
- if (!selectedThread) return;
7151
- const content = composeContent.trim();
7152
- if (!content) {
7153
- setComposeError("Message content is required.");
7154
- return;
7155
- }
7156
- const summary = composeSummary.trim() || content.slice(0, 120);
7157
- setComposeError(null);
7158
- try {
7159
- await appendInsightThreadReplyRecord({
7160
- threadId: selectedThread.id,
7161
- summary,
7162
- content,
7163
- role: "human",
7164
- authorLabel: "human-editor",
7165
- parentMessageId: replyParentId || undefined,
7166
- });
7167
- setComposeSummary("");
7168
- setComposeContent("");
7169
- setReplyParentId("");
7170
- await refreshThreads();
7171
- const reloaded = await loadInsightForumThreadById(selectedThread.id, { includeMessages: true });
7172
- if (reloaded) {
7173
- setThreads((current) => current.map((thread) => (thread.id === reloaded.id ? reloaded : thread)));
7174
- }
7175
- setForumView({ mode: "thread", threadId: selectedThread.id });
7176
- } catch (error) {
7177
- setComposeError(error instanceof Error ? error.message : "Could not post thread message.");
7178
- }
7179
- }, [composeContent, composeSummary, refreshThreads, replyParentId, selectedThread]);
7180
-
7181
- const deleteThreadMessage = useCallback(async (threadId: string, messageId: string) => {
7182
- setDeletingMessageId(messageId);
7183
- try {
7184
- await deleteInsightThreadMessageRecord({ threadId, messageId });
7185
- if (replyParentId === messageId) setReplyParentId("");
7186
- await refreshThreads();
7187
- const reloaded = await loadInsightForumThreadById(threadId, { includeMessages: true });
7188
- if (reloaded) {
7189
- setThreads((current) => current.map((thread) => (thread.id === reloaded.id ? reloaded : thread)));
7190
- }
7191
- setComposeError(null);
7192
- } catch (error) {
7193
- setComposeError(error instanceof Error ? error.message : "Could not delete message.");
7194
- } finally {
7195
- setDeletingMessageId("");
7196
- }
7197
- }, [refreshThreads, replyParentId]);
7198
-
7199
- const insightKnowledgeQuery = useNewsroomKnowledgeContext(selectedThread ? {
7200
- anchor: { kind: "message", id: selectedThread.id, lineageId: selectedThread.id },
7201
- title: selectedThread.title ?? "Insight thread",
7202
- subtitle: selectedThread.sectionKey ?? "insight",
7203
- } : null);
7204
-
7205
- return (
7206
- <>
7207
- <NewsroomListDetailShell
7208
- sectionKey="insights"
7209
- canExpandDetail={forumView.mode === "thread" && Boolean(forumView.threadId)}
7210
- detailOpen={forumView.mode === "thread" && Boolean(forumView.threadId)}
7211
- selectionScrollKey={forumView.mode === "thread" ? forumView.threadId : null}
7212
- utilityActions={[insightKnowledgeQuery.action]}
7213
- lede={(
7214
- <NewsroomDeskSectionLede
7215
- headingId="insights-forum-title"
7216
- section="insights"
7217
- headline="Insights"
7218
- lede="Recent research insights in reverse chronological order. Each insight opens a forum thread for discussion and refinement."
7219
- controls={(
7220
- <div className="news-desk-reference-sort-controls" role="group" aria-label="Insight domain filter">
7221
- <span className="news-desk-reference-sort-controls__label">Domain</span>
7222
- <button
7223
- type="button"
7224
- data-active={!domainFilter || undefined}
7225
- onClick={() => {
7226
- setDomainFilter("");
7227
- syncInsightsIndexUrl("", true);
7228
- }}
7229
- >
7230
- All
7231
- </button>
7232
- <button
7233
- type="button"
7234
- data-active={domainFilter === "knowledge" || undefined}
7235
- onClick={() => {
7236
- setDomainFilter("knowledge");
7237
- syncInsightsIndexUrl("knowledge", true);
7238
- }}
7239
- >
7240
- Knowledge
7241
- </button>
7242
- <button
7243
- type="button"
7244
- data-active={domainFilter === "assignment_work" || undefined}
7245
- onClick={() => {
7246
- setDomainFilter("assignment_work");
7247
- syncInsightsIndexUrl("assignment_work", true);
7248
- }}
7249
- >
7250
- Assignment work
7251
- </button>
7252
- </div>
7253
- )}
7254
- />
7255
- )}
7256
- list={(
7257
- <section className="category-steering-section category-steering-section--lead" aria-label="Insight forum threads">
7258
- <ForumThreadIndex
7259
- threads={threads}
7260
- sections={[]}
7261
- isLoading={threadsLoading}
7262
- error={threadsError}
7263
- emptyLabel="No insight threads match this filter."
7264
- variant="insight"
7265
- toolbar={domainOptions.length > 2 ? (
7266
- <div className="news-desk-forum-toolbar">
7267
- <select
7268
- aria-label="Insight domain"
7269
- value={domainFilter}
7270
- onChange={(event) => {
7271
- setDomainFilter(event.target.value);
7272
- syncInsightsIndexUrl(event.target.value, true);
7273
- }}
7274
- >
7275
- <option value="">All domains ({threads.length})</option>
7276
- {domainOptions.map((option) => (
7277
- <option key={option.key} value={option.key}>{option.key} ({option.count})</option>
7278
- ))}
7279
- </select>
7280
- {nextToken ? (
7281
- <button type="button" disabled={threadsLoading} onClick={() => void refreshThreads({ append: true, token: nextToken })}>
7282
- Load more
7283
- </button>
7284
- ) : null}
7285
- </div>
7286
- ) : nextToken ? (
7287
- <div className="news-desk-forum-toolbar">
7288
- <button type="button" disabled={threadsLoading} onClick={() => void refreshThreads({ append: true, token: nextToken })}>
7289
- Load more
7290
- </button>
7291
- </div>
7292
- ) : null}
7293
- onOpenThread={(threadId) => openThread(threadId)}
7294
- />
7295
- </section>
7296
- )}
7297
- detail={forumView.mode === "thread" && selectedThread ? (
7298
- <ForumThreadView
7299
- thread={selectedThread}
7300
- sections={[]}
7301
- replyParentId={replyParentId}
7302
- composeSummary={composeSummary}
7303
- composeContent={composeContent}
7304
- composeError={composeError}
7305
- focusMessageId={forumMessageAnchorId}
7306
- isDemo={isDemo}
7307
- variant="insight"
7308
- onBack={closeThread}
7309
- onReplyTarget={(messageId) => {
7310
- setReplyParentId(messageId);
7311
- pushInsightThreadUrl(selectedThread.id, { demo: isDemo, messageId, replace: true });
7312
- }}
7313
- onClearReplyTarget={() => {
7314
- setReplyParentId("");
7315
- pushInsightThreadUrl(selectedThread.id, { demo: isDemo, replace: true });
7316
- }}
7317
- onSummaryChange={setComposeSummary}
7318
- onContentChange={setComposeContent}
7319
- onSubmit={() => void postThreadMessage()}
7320
- onDeleteMessage={(messageId) => {
7321
- if (!selectedThread || deletingMessageId === messageId) return;
7322
- void deleteThreadMessage(selectedThread.id, messageId);
7323
- }}
7324
- deletingMessageId={deletingMessageId}
7325
- />
7326
- ) : forumView.mode === "thread" ? (
7327
- <section className="category-steering-section">
7328
- <SectionHeader title="Thread Unavailable" detail="Insights" />
7329
- <EmptyRow label="That insight thread is not available. Return to the index or adjust filters." />
7330
- </section>
7331
- ) : null}
7332
- />
7333
- {insightKnowledgeQuery.dialog}
7334
- </>
7335
- );
7336
- }
7337
-
7338
- function buildDemoInsightForumThread(root: MessageRecord, allMessages: MessageRecord[]): ForumThreadWithMessages {
7339
- const replies = allMessages.filter((message) => (
7340
- message.threadId === root.id
7341
- && message.id !== root.id
7342
- && String(message.status || "active") === "active"
7343
- ));
7344
- const messages = [root, ...replies].sort(
7345
- (left, right) => Number(left.sequenceNumber ?? 0) - Number(right.sequenceNumber ?? 0),
7346
- );
7347
- const lastMessage = messages.at(-1) ?? root;
7348
- return {
7349
- id: root.id,
7350
- threadKind: "insight_forum",
7351
- status: "active",
7352
- title: root.summary ?? "Insight",
7353
- summary: root.messageDomain ?? null,
7354
- primaryAnchorKind: "message",
7355
- primaryAnchorId: root.id,
7356
- primaryAnchorLineageId: root.id,
7357
- primaryAnchorKey: `message#${root.id}`,
7358
- createdByLabel: root.authorLabel ?? null,
7359
- messageCount: messages.length,
7360
- lastMessageId: lastMessage.id,
7361
- lastMessageAt: lastMessage.updatedAt ?? lastMessage.createdAt,
7362
- metadata: metadataRecord(root.metadata),
7363
- createdAt: root.createdAt,
7364
- updatedAt: lastMessage.updatedAt ?? lastMessage.createdAt,
7365
- newsroomFeedKey: "messages",
7366
- messages,
7367
- scope: "insight",
7368
- sectionKey: root.messageDomain ?? null,
7369
- };
7370
- }
7371
-
7372
6867
  function MessagesDeskView({
7373
6868
  assignments,
7374
6869
  graph,
@@ -7975,7 +7470,6 @@ function ForumThreadIndex({
7975
7470
  emptyLabel,
7976
7471
  toolbar,
7977
7472
  composer,
7978
- variant = "edition",
7979
7473
  onOpenThread,
7980
7474
  }: {
7981
7475
  threads: ForumThreadWithMessages[];
@@ -7985,7 +7479,6 @@ function ForumThreadIndex({
7985
7479
  emptyLabel: string;
7986
7480
  toolbar?: ReactNode;
7987
7481
  composer?: ReactNode;
7988
- variant?: "edition" | "insight";
7989
7482
  onOpenThread: (threadId: string) => void;
7990
7483
  }) {
7991
7484
  return (
@@ -7997,7 +7490,7 @@ function ForumThreadIndex({
7997
7490
  <thead>
7998
7491
  <tr>
7999
7492
  <th>Topic</th>
8000
- <th>{variant === "insight" ? "Domain" : "Scope"}</th>
7493
+ <th>Scope</th>
8001
7494
  <th>Replies</th>
8002
7495
  <th>Last Post</th>
8003
7496
  </tr>
@@ -8010,11 +7503,9 @@ function ForumThreadIndex({
8010
7503
  );
8011
7504
  const lastMessage = sortedMessages.at(-1) ?? null;
8012
7505
  const replies = Math.max(activeMessages.length - 1, 0);
8013
- const scopeLabel = thread.scope === "insight"
8014
- ? (thread.sectionKey ?? thread.summary ?? "Insight")
8015
- : thread.scope === "edition"
8016
- ? "Edition"
8017
- : `Section: ${normalizeForumThreadSectionLabel(thread, sections)}`;
7506
+ const scopeLabel = thread.scope === "edition"
7507
+ ? "Edition"
7508
+ : `Section: ${normalizeForumThreadSectionLabel(thread, sections)}`;
8018
7509
  return (
8019
7510
  <tr key={thread.id}>
8020
7511
  <td>
@@ -8128,7 +7619,6 @@ function ForumThreadView({
8128
7619
  deletingMessageId,
8129
7620
  focusMessageId = null,
8130
7621
  isDemo = false,
8131
- variant = "edition",
8132
7622
  onBack,
8133
7623
  onReplyTarget,
8134
7624
  onDeleteMessage,
@@ -8146,7 +7636,6 @@ function ForumThreadView({
8146
7636
  deletingMessageId?: string;
8147
7637
  focusMessageId?: string | null;
8148
7638
  isDemo?: boolean;
8149
- variant?: "edition" | "insight";
8150
7639
  onBack: () => void;
8151
7640
  onReplyTarget: (messageId: string) => void;
8152
7641
  onDeleteMessage: (messageId: string) => void;
@@ -8159,9 +7648,7 @@ function ForumThreadView({
8159
7648
  (left, right) => Number(left.sequenceNumber ?? 0) - Number(right.sequenceNumber ?? 0),
8160
7649
  );
8161
7650
  const visibleCount = messages.length;
8162
- const threadShareUrl = variant === "insight"
8163
- ? buildInsightThreadUrl(thread.id, { demo: isDemo })
8164
- : buildForumThreadUrl(thread.id, { demo: isDemo });
7651
+ const threadShareUrl = buildForumThreadUrl(thread.id, { demo: isDemo });
8165
7652
 
8166
7653
  useEffect(() => {
8167
7654
  if (!focusMessageId || typeof window === "undefined") return;
@@ -8190,11 +7677,9 @@ function ForumThreadView({
8190
7677
  Back to Threads
8191
7678
  </button>
8192
7679
  <p className="story-label">
8193
- {thread.scope === "insight"
8194
- ? `Insight · ${thread.sectionKey ?? "discussion"}`
8195
- : thread.scope === "edition"
8196
- ? "Edition Thread"
8197
- : `Section Thread: ${normalizeForumThreadSectionLabel(thread, sections)}`}
7680
+ {thread.scope === "edition"
7681
+ ? "Edition Thread"
7682
+ : `Section Thread: ${normalizeForumThreadSectionLabel(thread, sections)}`}
8198
7683
  </p>
8199
7684
  <h3>{thread.title}</h3>
8200
7685
  </div>
@@ -8234,9 +7719,7 @@ function ForumThreadView({
8234
7719
  </DropdownMenuItem>
8235
7720
  <DropdownMenuItem
8236
7721
  onSelect={() => {
8237
- const url = variant === "insight"
8238
- ? buildInsightThreadUrl(thread.id, { messageId: message.id, demo: isDemo })
8239
- : buildForumThreadUrl(thread.id, { messageId: message.id, demo: isDemo });
7722
+ const url = buildForumThreadUrl(thread.id, { messageId: message.id, demo: isDemo });
8240
7723
  void copyShareLink(url);
8241
7724
  }}
8242
7725
  >
@@ -8539,7 +8022,7 @@ function NewsroomListDetailShell({
8539
8022
  list: ReactNode;
8540
8023
  onCloseDetail?: () => void;
8541
8024
  selectionScrollKey?: string | null;
8542
- sectionKey: "assignments" | "concepts" | "insights" | "messages" | "references" | "topics";
8025
+ sectionKey: "assignments" | "concepts" | "messages" | "references" | "topics";
8543
8026
  utilityActions?: NewsroomDetailAction[];
8544
8027
  }) {
8545
8028
  const [detailMode, setDetailMode] = useState<"split" | "full">("split");
@@ -9051,7 +8534,7 @@ function LibraryBigIcon() {
9051
8534
  );
9052
8535
  }
9053
8536
 
9054
- function InsightIcon() {
8537
+ function NoteIcon() {
9055
8538
  return (
9056
8539
  <svg
9057
8540
  aria-hidden="true"
@@ -9512,7 +8995,7 @@ function SemanticDetailPanel({
9512
8995
  }
9513
8996
 
9514
8997
  const messages = graph.messagesFor(selected.kind, selected.lineageId);
9515
- const insights = graph.insightsFor(selected.kind, selected.lineageId);
8998
+ const notes = graph.notesFor(selected.kind, selected.lineageId);
9516
8999
  const attachments = selected.kind === "reference" ? graph.attachmentsForReference(selected.lineageId) : [];
9517
9000
  const neighborGroups = graph.neighbors(selected.kind, selected.lineageId);
9518
9001
  const selectedReference = selected.kind === "reference" ? selected.record as ReferenceRecord : null;
@@ -9586,8 +9069,8 @@ function SemanticDetailPanel({
9586
9069
  ) : null}
9587
9070
  </div>
9588
9071
  ) : null}
9589
- {insights.length ? (
9590
- <InsightMessageBlock insights={insights} />
9072
+ {notes.length ? (
9073
+ <NoteMessageBlock notes={notes} />
9591
9074
  ) : null}
9592
9075
  {messages.length ? (
9593
9076
  <div className="news-desk-detail-block">
@@ -9791,11 +9274,11 @@ function useNewsroomKnowledgeContext(target: KnowledgeQueryTarget | null) {
9791
9274
  };
9792
9275
  }
9793
9276
 
9794
- function useNewsroomInsightComposer(
9795
- target: InsightTarget | null,
9277
+ function useNewsroomNoteComposer(
9278
+ target: NoteTarget | null,
9796
9279
  disabled: boolean,
9797
- onCreate: (target: InsightTarget, summary: string, body: string) => Promise<void>,
9798
- ): InsightComposerControl {
9280
+ onCreate: (target: NoteTarget, summary: string, body: string) => Promise<void>,
9281
+ ): NoteComposerControl {
9799
9282
  const [dialogOpen, setDialogOpen] = useState(false);
9800
9283
  const [summary, setSummary] = useState("");
9801
9284
  const [body, setBody] = useState("");
@@ -9804,7 +9287,7 @@ function useNewsroomInsightComposer(
9804
9287
  loading: false,
9805
9288
  targetKey: null,
9806
9289
  });
9807
- const targetKey = target ? insightTargetKey(target) : null;
9290
+ const targetKey = target ? noteTargetKey(target) : null;
9808
9291
 
9809
9292
  useEffect(() => {
9810
9293
  setDialogOpen(false);
@@ -9818,7 +9301,7 @@ function useNewsroomInsightComposer(
9818
9301
  const cleanSummary = summary.trim();
9819
9302
  const cleanBody = body.trim();
9820
9303
  if (!cleanSummary || !cleanBody) {
9821
- setState({ error: "Insight summary and body are required.", loading: false, targetKey });
9304
+ setState({ error: "Note summary and body are required.", loading: false, targetKey });
9822
9305
  return;
9823
9306
  }
9824
9307
  setState({ error: null, loading: true, targetKey });
@@ -9830,7 +9313,7 @@ function useNewsroomInsightComposer(
9830
9313
  setBody("");
9831
9314
  } catch (error) {
9832
9315
  setState({
9833
- error: error instanceof Error ? error.message : "Could not save insight.",
9316
+ error: error instanceof Error ? error.message : "Could not save note.",
9834
9317
  loading: false,
9835
9318
  targetKey,
9836
9319
  });
@@ -9842,15 +9325,15 @@ function useNewsroomInsightComposer(
9842
9325
 
9843
9326
  return {
9844
9327
  action: {
9845
- ariaLabel: "Add insight",
9328
+ ariaLabel: "Add note",
9846
9329
  disabled: disabled || !target || loading,
9847
- icon: <InsightIcon />,
9848
- key: "add-insight",
9849
- label: loading ? "Saving Insight" : "Add Insight",
9330
+ icon: <NoteIcon />,
9331
+ key: "add-note",
9332
+ label: loading ? "Saving Note" : "Add Note",
9850
9333
  onSelect: () => setDialogOpen(true),
9851
9334
  } satisfies NewsroomDetailAction,
9852
9335
  dialog: target ? (
9853
- <InsightComposerDialog
9336
+ <NoteComposerDialog
9854
9337
  body={body}
9855
9338
  disabled={disabled || loading}
9856
9339
  error={error}
@@ -10189,7 +9672,7 @@ function KnowledgeQueryDialog({
10189
9672
  );
10190
9673
  }
10191
9674
 
10192
- function InsightComposerDialog({
9675
+ function NoteComposerDialog({
10193
9676
  body,
10194
9677
  disabled,
10195
9678
  error,
@@ -10210,7 +9693,7 @@ function InsightComposerDialog({
10210
9693
  onSummaryChange: (value: string) => void;
10211
9694
  open: boolean;
10212
9695
  summary: string;
10213
- target: InsightTarget;
9696
+ target: NoteTarget;
10214
9697
  }) {
10215
9698
  useEffect(() => {
10216
9699
  if (!open) return;
@@ -10227,16 +9710,16 @@ function InsightComposerDialog({
10227
9710
  return (
10228
9711
  <div
10229
9712
  className="news-desk-modal"
10230
- data-news-desk-insight-modal
9713
+ data-news-desk-note-modal
10231
9714
  onMouseDown={(event) => {
10232
9715
  if (event.target === event.currentTarget && !disabled) onClose();
10233
9716
  }}
10234
9717
  >
10235
- <div className="news-desk-modal__dialog news-desk-modal__dialog--knowledge-query" role="dialog" aria-modal="true" aria-labelledby="insight-composer-title">
9718
+ <div className="news-desk-modal__dialog news-desk-modal__dialog--knowledge-query" role="dialog" aria-modal="true" aria-labelledby="note-composer-title">
10236
9719
  <header className="news-desk-modal__header">
10237
9720
  <div>
10238
- <p className="story-label">Insight</p>
10239
- <h3 id="insight-composer-title">{target.title}</h3>
9721
+ <p className="story-label">Note</p>
9722
+ <h3 id="note-composer-title">{target.title}</h3>
10240
9723
  <span>{target.kind} / {target.lineageId}</span>
10241
9724
  </div>
10242
9725
  <button type="button" disabled={disabled} onClick={onClose}>Close</button>
@@ -10251,7 +9734,7 @@ function InsightComposerDialog({
10251
9734
  />
10252
9735
  </label>
10253
9736
  <label>
10254
- <span>Insight Body</span>
9737
+ <span>Note Body</span>
10255
9738
  <textarea
10256
9739
  disabled={disabled}
10257
9740
  rows={8}
@@ -10264,7 +9747,7 @@ function InsightComposerDialog({
10264
9747
  <footer className="news-desk-modal__actions">
10265
9748
  <button type="button" disabled={disabled} onClick={onClose}>Cancel</button>
10266
9749
  <button type="button" className="news-desk-assignment-create-button" disabled={disabled || !summary.trim() || !body.trim()} onClick={onRun}>
10267
- {disabled ? "Saving" : "Save Insight"}
9750
+ {disabled ? "Saving" : "Save Note"}
10268
9751
  </button>
10269
9752
  </footer>
10270
9753
  </div>
@@ -11011,14 +10494,14 @@ function PayloadDetailBlock({
11011
10494
  );
11012
10495
  }
11013
10496
 
11014
- function InsightMessageBlock({ insights }: { insights: MessageRecord[] }) {
10497
+ function NoteMessageBlock({ notes }: { notes: MessageRecord[] }) {
11015
10498
  return (
11016
10499
  <div className="news-desk-detail-block">
11017
- <p className="story-label">Insights</p>
11018
- {insights.slice(0, 6).map((message) => (
10500
+ <p className="story-label">Notes</p>
10501
+ {notes.slice(0, 6).map((message) => (
11019
10502
  <div className="news-desk-detail-line" key={message.id}>
11020
10503
  <span>{message.authorLabel ?? "knowledge"}</span>
11021
- <strong>{message.summary ?? "Stored insight"}</strong>
10504
+ <strong>{message.summary ?? "Stored note"}</strong>
11022
10505
  </div>
11023
10506
  ))}
11024
10507
  </div>
@@ -11032,7 +10515,7 @@ function ReferenceDetailPanel({
11032
10515
  curation,
11033
10516
  disabled,
11034
10517
  graph,
11035
- onCreateInsight,
10518
+ onCreateNote,
11036
10519
  onMoveCorpus,
11037
10520
  onReview,
11038
10521
  onReviewTopicLabel,
@@ -11050,7 +10533,7 @@ function ReferenceDetailPanel({
11050
10533
  curation: ReferenceCurationDisplayState | null;
11051
10534
  disabled: boolean;
11052
10535
  graph: SemanticGraph;
11053
- onCreateInsight: (target: InsightTarget, summary: string, body: string) => Promise<void>;
10536
+ onCreateNote: (target: NoteTarget, summary: string, body: string) => Promise<void>;
11054
10537
  onMoveCorpus: (reference: ReferenceRecord, corpusId: string) => void;
11055
10538
  onReview: (action: ReferenceCurationAction) => void;
11056
10539
  onReviewTopicLabel: (input: { action: TopicLabelAction; category: CategorySteeringCategory; note?: string | null; reference: ReferenceRecord; sourceRelationId?: string | null }) => void;
@@ -11329,7 +10812,7 @@ function ReferenceDetailPanel({
11329
10812
 
11330
10813
  const lineageId = referenceLineageId;
11331
10814
  const messages = graph.messagesFor("reference", lineageId);
11332
- const insights = graph.insightsFor("reference", lineageId);
10815
+ const notes = graph.notesFor("reference", lineageId);
11333
10816
  const neighborGroups = graph.neighbors("reference", lineageId);
11334
10817
  const fallbackOutgoingCitationGroup = neighborGroups.find((group) => group.direction === "outgoing" && group.predicate === "cites") ?? null;
11335
10818
  const fallbackIncomingCitationGroup = neighborGroups.find((group) => group.direction === "incoming" && group.predicate === "cites") ?? null;
@@ -11448,10 +10931,10 @@ function ReferenceDetailPanel({
11448
10931
  reference={reference}
11449
10932
  semanticRelations={semanticRelations}
11450
10933
  />
11451
- <ReferenceInsightPanel
10934
+ <ReferenceNotePanel
11452
10935
  disabled={disabled}
11453
- insights={insights}
11454
- onCreateInsight={onCreateInsight}
10936
+ notes={notes}
10937
+ onCreateNote={onCreateNote}
11455
10938
  reference={reference}
11456
10939
  />
11457
10940
  <div data-news-desk-reference-metadata-expander>
@@ -12341,49 +11824,49 @@ function ReferenceCurationStatusPanel({ status }: { status: ReferenceCurationRun
12341
11824
  );
12342
11825
  }
12343
11826
 
12344
- function ReferenceInsightPanel({
11827
+ function ReferenceNotePanel({
12345
11828
  disabled,
12346
- insights,
12347
- onCreateInsight,
11829
+ notes,
11830
+ onCreateNote,
12348
11831
  reference,
12349
11832
  }: {
12350
11833
  disabled: boolean;
12351
- insights: MessageRecord[];
12352
- onCreateInsight: (target: InsightTarget, summary: string, body: string) => Promise<void>;
11834
+ notes: MessageRecord[];
11835
+ onCreateNote: (target: NoteTarget, summary: string, body: string) => Promise<void>;
12353
11836
  reference: ReferenceRecord;
12354
11837
  }) {
12355
11838
  const [composerOpen, setComposerOpen] = useState(false);
12356
11839
  const [body, setBody] = useState("");
12357
11840
  const [error, setError] = useState<string | null>(null);
12358
11841
  const [saving, setSaving] = useState(false);
12359
- const target = useMemo(() => insightTargetForReference(reference), [reference]);
12360
- const hasInsights = insights.length > 0;
11842
+ const target = useMemo(() => noteTargetForReference(reference), [reference]);
11843
+ const hasNotes = notes.length > 0;
12361
11844
 
12362
11845
  return (
12363
- <section className="news-desk-reference-workflow" data-reference-insight-workflow>
11846
+ <section className="news-desk-reference-workflow" data-reference-note-workflow>
12364
11847
  <header className="news-desk-reference-workflow__header">
12365
- <p className="story-label">Insights</p>
12366
- {!hasInsights && !composerOpen ? (
11848
+ <p className="story-label">Notes</p>
11849
+ {!hasNotes && !composerOpen ? (
12367
11850
  <button
12368
11851
  type="button"
12369
- data-news-desk-reference-insight-trigger
11852
+ data-news-desk-reference-note-trigger
12370
11853
  disabled={disabled || saving}
12371
11854
  onClick={() => {
12372
11855
  setComposerOpen(true);
12373
11856
  setError(null);
12374
11857
  }}
12375
11858
  >
12376
- Add insight
11859
+ Add note
12377
11860
  </button>
12378
11861
  ) : null}
12379
11862
  </header>
12380
- <div className="news-desk-reference-insight-shell" data-reference-insight-input>
12381
- {hasInsights ? (
12382
- <div className="news-desk-reference-insight-list" data-reference-insight-state>
12383
- {insights.map((message) => (
11863
+ <div className="news-desk-reference-note-shell" data-reference-note-input>
11864
+ {hasNotes ? (
11865
+ <div className="news-desk-reference-note-list" data-reference-note-state>
11866
+ {notes.map((message) => (
12384
11867
  <article className="news-desk-reference-topic-state-row" key={message.id}>
12385
11868
  <div className="news-desk-reference-topic-state-row__headline">
12386
- <strong>{message.summary ?? "Stored insight"}</strong>
11869
+ <strong>{message.summary ?? "Stored note"}</strong>
12387
11870
  <span>{message.authorLabel ?? message.source ?? "knowledge"}</span>
12388
11871
  </div>
12389
11872
  <div className="news-desk-reference-topic-state-row__meta">
@@ -12393,35 +11876,35 @@ function ReferenceInsightPanel({
12393
11876
  ))}
12394
11877
  </div>
12395
11878
  ) : null}
12396
- {hasInsights && !composerOpen ? (
11879
+ {hasNotes && !composerOpen ? (
12397
11880
  <button
12398
11881
  type="button"
12399
- data-news-desk-reference-insight-trigger
11882
+ data-news-desk-reference-note-trigger
12400
11883
  disabled={disabled || saving}
12401
11884
  onClick={() => {
12402
11885
  setComposerOpen(true);
12403
11886
  setError(null);
12404
11887
  }}
12405
11888
  >
12406
- Add insight
11889
+ Add note
12407
11890
  </button>
12408
11891
  ) : null}
12409
11892
  {composerOpen ? (
12410
11893
  <form
12411
- className="news-desk-reference-insight-form"
12412
- data-news-desk-reference-insight-form
11894
+ className="news-desk-reference-note-form"
11895
+ data-news-desk-reference-note-form
12413
11896
  onSubmit={(event) => {
12414
11897
  event.preventDefault();
12415
11898
  if (disabled || saving) return;
12416
11899
  const cleanBody = body.trim();
12417
11900
  if (!cleanBody) {
12418
- setError("Insight text is required.");
11901
+ setError("Note text is required.");
12419
11902
  return;
12420
11903
  }
12421
- const cleanSummary = summarizeInsightForStorage(cleanBody);
11904
+ const cleanSummary = summarizeNoteForStorage(cleanBody);
12422
11905
  setSaving(true);
12423
11906
  setError(null);
12424
- void onCreateInsight(target, cleanSummary, cleanBody)
11907
+ void onCreateNote(target, cleanSummary, cleanBody)
12425
11908
  .then(() => {
12426
11909
  setSaving(false);
12427
11910
  setBody("");
@@ -12429,12 +11912,12 @@ function ReferenceInsightPanel({
12429
11912
  })
12430
11913
  .catch((submitError) => {
12431
11914
  setSaving(false);
12432
- setError(submitError instanceof Error ? submitError.message : "Could not save insight.");
11915
+ setError(submitError instanceof Error ? submitError.message : "Could not save note.");
12433
11916
  });
12434
11917
  }}
12435
11918
  >
12436
11919
  <label className="news-desk-reference-curation-note">
12437
- <span>Insight</span>
11920
+ <span>Note</span>
12438
11921
  <textarea
12439
11922
  disabled={disabled || saving}
12440
11923
  value={body}
@@ -12442,9 +11925,9 @@ function ReferenceInsightPanel({
12442
11925
  />
12443
11926
  </label>
12444
11927
  {error ? <p className="news-desk-detail-copy">{error}</p> : null}
12445
- <div className="news-desk-reference-insight-form__actions">
11928
+ <div className="news-desk-reference-note-form__actions">
12446
11929
  <button type="submit" disabled={disabled || saving || !body.trim()}>
12447
- {saving ? "Saving" : "Save insight"}
11930
+ {saving ? "Saving" : "Save note"}
12448
11931
  </button>
12449
11932
  <button
12450
11933
  type="button"
@@ -12465,9 +11948,9 @@ function ReferenceInsightPanel({
12465
11948
  );
12466
11949
  }
12467
11950
 
12468
- function summarizeInsightForStorage(body: string): string {
11951
+ function summarizeNoteForStorage(body: string): string {
12469
11952
  const normalized = body.trim().replace(/\s+/g, " ");
12470
- if (!normalized) return "Insight";
11953
+ if (!normalized) return "Note";
12471
11954
  const firstSentenceMatch = normalized.match(/(.+?[.!?])(?:\s|$)/);
12472
11955
  const candidate = (firstSentenceMatch?.[1] ?? normalized).trim();
12473
11956
  if (candidate.length <= 120) return candidate;
@@ -13332,7 +12815,7 @@ function buildUiAnalysisReindexAssignmentPlan({
13332
12815
  return { assignment, event, relation, metadata };
13333
12816
  }
13334
12817
 
13335
- function insightTargetForReference(reference: ReferenceRecord): InsightTarget {
12818
+ function noteTargetForReference(reference: ReferenceRecord): NoteTarget {
13336
12819
  return {
13337
12820
  kind: "reference",
13338
12821
  id: reference.id,
@@ -13343,7 +12826,7 @@ function insightTargetForReference(reference: ReferenceRecord): InsightTarget {
13343
12826
  };
13344
12827
  }
13345
12828
 
13346
- function insightTargetForNewsroomSection(section: NewsroomSectionRecord): InsightTarget {
12829
+ function noteTargetForNewsroomSection(section: NewsroomSectionRecord): NoteTarget {
13347
12830
  return {
13348
12831
  kind: "newsroomSection",
13349
12832
  id: section.id,
@@ -13354,7 +12837,7 @@ function insightTargetForNewsroomSection(section: NewsroomSectionRecord): Insigh
13354
12837
  };
13355
12838
  }
13356
12839
 
13357
- function insightTargetForSemanticObject(object: SemanticObjectSummary): InsightTarget | null {
12840
+ function noteTargetForSemanticObject(object: SemanticObjectSummary): NoteTarget | null {
13358
12841
  if (object.kind !== "category" && object.kind !== "semanticNode") return null;
13359
12842
  return {
13360
12843
  kind: object.kind,
@@ -13366,21 +12849,21 @@ function insightTargetForSemanticObject(object: SemanticObjectSummary): InsightT
13366
12849
  };
13367
12850
  }
13368
12851
 
13369
- function insightTargetKey(target: InsightTarget): string {
12852
+ function noteTargetKey(target: NoteTarget): string {
13370
12853
  return `${target.kind}#${target.lineageId}`;
13371
12854
  }
13372
12855
 
13373
- function buildUiInsightRelation(message: MessageRecord, target: InsightTarget, now: string): SemanticRelationRecord {
12856
+ function buildUiNoteRelation(message: MessageRecord, target: NoteTarget, now: string): SemanticRelationRecord {
13374
12857
  const subjectStateKey = semanticStateKey("message", message.id);
13375
12858
  const objectStateKey = semanticStateKey(target.kind, target.lineageId);
13376
12859
  const subjectVersionKey = semanticVersionKey("message", message.id);
13377
12860
  const objectVersionKey = semanticVersionKey(target.kind, target.id);
13378
12861
  return {
13379
- id: `semantic-relation-${hashUiKey([subjectVersionKey, "insight_about", objectVersionKey])}`,
12862
+ id: `semantic-relation-${hashUiKey([subjectVersionKey, "note_about", objectVersionKey])}`,
13380
12863
  relationState: "current",
13381
- predicate: "insight_about",
13382
- relationTypeId: "semantic-relation-type-insight-about",
13383
- relationTypeKey: "insight_about",
12864
+ predicate: "note_about",
12865
+ relationTypeId: "semantic-relation-type-note-about",
12866
+ relationTypeKey: "note_about",
13384
12867
  relationDomain: "knowledge",
13385
12868
  subjectKind: "message",
13386
12869
  subjectId: message.id,
@@ -13393,7 +12876,7 @@ function buildUiInsightRelation(message: MessageRecord, target: InsightTarget, n
13393
12876
  subjectStateKey,
13394
12877
  objectStateKey,
13395
12878
  objectSubjectStateKey: `${objectStateKey}#message`,
13396
- predicateObjectStateKey: `insight_about#${objectStateKey}`,
12879
+ predicateObjectStateKey: `note_about#${objectStateKey}`,
13397
12880
  subjectVersionKey,
13398
12881
  objectVersionKey,
13399
12882
  score: null,
@@ -13890,10 +13373,10 @@ function compareDoctrineCategories(left: DoctrineCategory, right: DoctrineCatego
13890
13373
 
13891
13374
  function formatDeskSectionHeadline(section: NewsDeskTab): string {
13892
13375
  if (section === "search") return "Search";
13893
- if (section === "topics") return "Topics";
13376
+ if (section === "desks") return "Taxonomy";
13377
+ if (section === "topics") return "Topic proposals";
13894
13378
  if (section === "concepts") return "Concepts";
13895
13379
  if (section === "references") return "References";
13896
- if (section === "insights") return "Insights";
13897
13380
  if (section === "messages") return "Messages";
13898
13381
  if (section === "assignments") return "Assignments";
13899
13382
  if (section === "administration") return "Administration";
@@ -13902,13 +13385,13 @@ function formatDeskSectionHeadline(section: NewsDeskTab): string {
13902
13385
 
13903
13386
  function formatDeskSectionLede(section: NewsDeskTab): string {
13904
13387
  if (section === "search") return "Search the newsroom knowledge base with semantic and ontology context.";
13905
- if (section === "topics") return "Review and shape the subject areas the newsroom covers.";
13388
+ if (section === "desks") return "The desks and topic categories that organize coverage, with each desk's mission and policies.";
13389
+ if (section === "topics") return "Review suggested changes to the subject areas the newsroom covers.";
13906
13390
  if (section === "concepts") return "Browse people, organizations, places, and ideas found in the source material.";
13907
13391
  if (section === "references") return "Review source materials before they become usable evidence.";
13908
- if (section === "insights") return "Browse recent research insights and discuss each one in a forum thread to refine the synthesis.";
13909
13392
  if (section === "messages") return "Read notes, rationales, forum threads, curation decisions, and other work products from people and agents.";
13910
13393
  if (section === "assignments") return "Create, filter, claim, and complete newsroom work.";
13911
- if (section === "administration") return "Manage users, roles, doctrine, configurable newspaper sections, and newsroom procedures.";
13394
+ if (section === "administration") return "Manage users, roles, doctrine, and newsroom procedures.";
13912
13395
  return "";
13913
13396
  }
13914
13397
 
@@ -16537,21 +16020,21 @@ function assertReferenceQualityMutationSucceeded(
16537
16020
  return response.data;
16538
16021
  }
16539
16022
 
16540
- function assertReferenceInsightMutationSucceeded(
16541
- response: ReferenceInsightResponse,
16023
+ function assertReferenceNoteMutationSucceeded(
16024
+ response: ReferenceNoteResponse,
16542
16025
  referenceId: string,
16543
- ): NonNullable<ReferenceInsightResponse["data"]> {
16026
+ ): NonNullable<ReferenceNoteResponse["data"]> {
16544
16027
  if (response.errors?.length) {
16545
16028
  throw new Error(response.errors.map(formatGraphQLError).join("; "));
16546
16029
  }
16547
16030
  if (!response.data?.ok) {
16548
- throw new Error(`Reference insight was not saved for ${referenceId}.`);
16031
+ throw new Error(`Reference note was not saved for ${referenceId}.`);
16549
16032
  }
16550
16033
  if (response.data.referenceId && response.data.referenceId !== referenceId) {
16551
- throw new Error(`Reference insight response did not match ${referenceId}.`);
16034
+ throw new Error(`Reference note response did not match ${referenceId}.`);
16552
16035
  }
16553
16036
  if (!response.data.messageId || !response.data.relationId) {
16554
- throw new Error(`Reference insight saved without required audit relations for ${referenceId}.`);
16037
+ throw new Error(`Reference note saved without required audit relations for ${referenceId}.`);
16555
16038
  }
16556
16039
  return response.data;
16557
16040
  }