@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.
- package/amplify/data/resource.js +134 -3
- package/amplify/data/resource.ts +145 -3
- package/amplify/data/schema.js +131 -0
- package/amplify/data/schema.ts +142 -0
- package/amplify/functions/category-action/handler.ts +272 -20
- package/amplify/functions/console-chat-responder/src/main.rs +1 -1
- package/amplify/functions/content-actions/handler.py +23 -0
- package/amplify/functions/content-actions/requirements.txt +1 -1
- package/amplify/functions/content-actions/resource.js +1 -1
- package/amplify/functions/content-actions/resource.ts +1 -1
- package/amplify/functions/model-attachment-upload/handler.ts +1 -0
- package/amplify/functions/renditions/requirements.txt +1 -1
- package/amplify/functions/shared/curation-delegation-flow.ts +147 -0
- package/amplify/functions/shared/curation-delegation.ts +115 -0
- package/amplify/functions/shared/python-bundle.js +3 -3
- package/amplify/functions/shared/python-bundle.ts +3 -3
- package/amplify/functions/shared/reference-curation-metadata.ts +51 -0
- package/amplify/functions/shared/slack-events.ts +2 -2
- package/amplify/publication/renditions-function.js +1 -1
- package/amplify/publication/renditions-function.ts +1 -1
- package/app/[year]/[month]/[day]/[articleSlug]/page.tsx +2 -1
- package/app/[year]/[month]/[day]/edition-route-page.tsx +2 -1
- package/app/[year]/[month]/[day]/page/[pageNumber]/page.tsx +2 -1
- package/app/[year]/[month]/[day]/page.tsx +3 -0
- package/app/[year]/[month]/[day]/section/[sectionKey]/page.tsx +2 -0
- package/app/api/archive/editions/route.ts +2 -0
- package/app/api/auth/prospectus/accounts/route.ts +9 -0
- package/app/api/local-preview/derive/route.ts +28 -0
- package/app/api/local-preview/media/route.ts +25 -0
- package/app/archive/page.tsx +3 -0
- package/app/globals.css +28 -28
- package/app/layout.tsx +8 -5
- package/app/newsroom/[section]/[...selection]/page.tsx +0 -1
- package/app/newsroom/[section]/page.tsx +8 -1
- package/app/newsroom/articles/page.tsx +10 -0
- package/app/newsroom/insights/[threadId]/page.tsx +5 -16
- package/app/newsroom/insights/page.tsx +4 -12
- package/app/newsroom/sections/[sectionId]/page.tsx +3 -0
- package/app/page.tsx +30 -1
- package/components/administration-delegations-panel.tsx +156 -0
- package/components/curation-delegation-client.ts +104 -0
- package/components/item-stream-view.tsx +63 -0
- package/components/markus-ir-preview.tsx +47 -2
- package/components/news-desk-client-provider.tsx +17 -2
- package/components/news-desk-page.tsx +0 -1
- package/components/news-desk-taxonomy-client.ts +154 -240
- package/components/newspaper.tsx +4 -4
- package/components/newsroom-article-editor.tsx +138 -40
- package/components/newsroom-assignment-detail.tsx +79 -0
- package/components/newsroom-assignments-view.tsx +1 -1
- package/components/newsroom-board-view.tsx +153 -43
- package/components/newsroom-client-shell.tsx +8 -1
- package/components/newsroom-newsprint-board.css +5 -46
- package/components/newsroom-ops-overview.tsx +27 -5
- package/components/newsroom-organizer-shell.tsx +36 -13
- package/components/newsroom-organizer.css +269 -13
- package/components/newsroom-references-view.tsx +118 -23
- package/components/newsroom-topics-view.tsx +1 -1
- package/components/priming-proof-page.tsx +17 -8
- package/components/prospectus-account-chooser.tsx +131 -0
- package/components/reference-cyclotron-priming.tsx +85 -0
- package/components/reference-cyclotron-status.tsx +5 -5
- package/components/reference-cyclotron-suggestion.tsx +143 -0
- package/components/reference-live-agreement.tsx +62 -0
- package/components/reference-section-suggestion.tsx +104 -0
- package/components/story-reporting-trail-client.ts +91 -0
- package/components/story-reporting-trail.tsx +115 -0
- package/components/topic-steering-workspace.tsx +280 -679
- package/components/ui/sheet.tsx +10 -4
- package/components/use-paged-content-item-versions.ts +151 -0
- package/components/wiki-concepts-view.tsx +153 -0
- package/components/wiki-index-client.ts +51 -0
- package/infra/amplify-app-shell.js +17 -3
- package/infra/amplify-app-shell.ts +18 -3
- package/infra/site-config.js +6 -1
- package/infra/site-config.ts +7 -1
- package/lib/approved-priming-proof-reports.ts +6 -3
- package/lib/approved-priming-proofs/papyrus.json +90 -0
- package/lib/article-flow.ts +95 -0
- package/lib/article-frame-solver.ts +519 -0
- package/lib/blog-feature-solver.ts +1 -109
- package/lib/category-repository.ts +1 -1
- package/lib/chrome-text.ts +504 -0
- package/lib/content-actions-client.ts +3 -0
- package/lib/content-actions-demo.ts +55 -0
- package/lib/content-item-lifecycle.ts +34 -5
- package/lib/content-source-context.ts +9 -1
- package/lib/curation-delegation-helpers.ts +69 -0
- package/lib/cyclotron-suggestions.ts +263 -0
- package/lib/front-matter-fields.ts +42 -0
- package/lib/front-mosaic.ts +1133 -0
- package/lib/furniture.ts +385 -0
- package/lib/graphql-content-repository.ts +42 -7
- package/lib/item-stream.ts +47 -0
- package/lib/labels.ts +37 -0
- package/lib/layout-config.ts +237 -0
- package/lib/layout-plan.ts +40 -11
- package/lib/layout-scenarios.ts +21 -11
- package/lib/layout-types.ts +457 -0
- package/lib/layout-warnings.ts +8 -0
- package/lib/local-preview-section-fixtures.ts +72 -0
- package/lib/local-preview-seed.ts +286 -0
- package/lib/local-preview-tenant.ts +27 -0
- package/lib/local-preview.ts +3 -0
- package/lib/newspaper-layout.ts +17 -3410
- package/lib/newsroom-articles.ts +2 -0
- package/lib/newsroom-assignments.ts +14 -1
- package/lib/newsroom-board.ts +99 -47
- package/lib/newsroom-demo-dashboard.ts +1 -1
- package/lib/newsroom-forum-routes.ts +2 -43
- package/lib/newsroom-index-filters.ts +3 -40
- package/lib/newsroom-media-backend.ts +5 -0
- package/lib/newsroom-nav.ts +1 -3
- package/lib/newsroom-organizer-nav.ts +9 -16
- package/lib/newsroom-section-routing.ts +1 -1
- package/lib/newsroom-sections.ts +12 -0
- package/lib/papyrus-web-locations.ts +3 -24
- package/lib/pretext-layout.ts +22 -0
- package/lib/priming-proof-report.ts +1 -0
- package/lib/prospectus-account-client.ts +90 -0
- package/lib/prospectus-sign-in-routes.ts +45 -0
- package/lib/prospectus-site-identity.ts +13 -1
- package/lib/prospectus-tenant.ts +22 -0
- package/lib/reader-static-params.ts +2 -1
- package/lib/relevance-decisions.ts +29 -0
- package/lib/relevance-status.ts +73 -0
- package/lib/reporting-story-budget.ts +2 -2
- package/lib/semantic-graph.ts +5 -5
- package/lib/site-brand.ts +28 -1
- package/lib/site-env.ts +5 -0
- package/lib/site-section-suggestions.ts +120 -0
- package/lib/site-stack.ts +1 -1
- package/lib/story-reporting-trail.ts +106 -0
- package/lib/tenant-keys.ts +32 -0
- package/lib/text-columns.ts +251 -0
- package/lib/wiki.ts +56 -0
- package/package.json +46 -3
- package/renderers/pretext/presentation-shell.tsx +1 -1
- package/renderers/pretext/rhythm-article-page.tsx +1 -18
- package/routes.manifest.json +34 -15
- package/app/newsroom/work/page.tsx +0 -10
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
import type { AssignmentRecord, MessageRecord, ReferenceRecord, SemanticRelationRecord } from "./category-repository";
|
|
2
|
+
import { SHADOW_AGREEMENT_SCHEMA, type ShadowAgreement } from "./cyclotron-suggestions";
|
|
3
|
+
import type { EditableArticle } from "./newsroom-articles";
|
|
4
|
+
import type { MediaAssetRecord } from "./newsroom-media";
|
|
5
|
+
import { EMPTY_STORY_REPORTING_TRAIL, type StoryReportingTrail, type TrailVersion } from "./story-reporting-trail";
|
|
6
|
+
import { contentItemStageKeys, type ContentItemVersionRecord } from "./content-item-lifecycle";
|
|
7
|
+
import { getContentItemStageDefinitions } from "./site-brand";
|
|
8
|
+
import { LOCAL_PREVIEW_TENANTS, getLocalPreviewTenantId } from "./local-preview-tenant";
|
|
9
|
+
|
|
10
|
+
const SEED_TIMESTAMP_BASE = Date.parse("2026-10-01T12:00:00.000Z");
|
|
11
|
+
const ONE_HOUR_IN_MILLISECONDS = 60 * 60 * 1000;
|
|
12
|
+
|
|
13
|
+
const SEED_CONTENT_ITEM_HEADLINES_BY_POSITION_FOR_TENANT_ZERO: readonly (readonly string[])[] = [
|
|
14
|
+
["Three sources confirm the zoning reversal", "Reporting packet: school board redistricting"],
|
|
15
|
+
["Draft in progress: the last ferry crossing", "Draft in progress: neighborhood bakery profile"],
|
|
16
|
+
["Illustration brief: flooded streets, aerial view", "Illustration brief: the market at dawn"],
|
|
17
|
+
["Layout pass: front page lead and rail", "Layout pass: the Saturday supplement"],
|
|
18
|
+
["Published: harbor safety review", "Published: the lighthouse keepers' archive"],
|
|
19
|
+
["Held for the winter issue: the lighthouse keepers' archive", "Evergreen: how the harbor dredging works"],
|
|
20
|
+
["Spiked: the unconfirmed council rumor", "Spiked: the recycled press release"],
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
const SEED_CONTENT_ITEM_HEADLINES_BY_POSITION_FOR_TENANT_ONE: readonly (readonly string[])[] = [
|
|
24
|
+
["Field note: spore dispersal on a damp morning", "Field note: the dung beetle census"],
|
|
25
|
+
["Draft in progress: why fungi throw their caps", "Draft in progress: the compost experiment"],
|
|
26
|
+
["Illustration brief: a cap in flight, close up", "Illustration brief: the mycelium network"],
|
|
27
|
+
["Layout pass: the monthly field guide", "Layout pass: the reader mailbag"],
|
|
28
|
+
["Published: a season of mushroom sightings", "Published: how the forest floor recycles"],
|
|
29
|
+
["Held for spring: the truffle hunt", "Evergreen: a beginner's guide to identification"],
|
|
30
|
+
["Spiked: the unverified foraging claim", "Spiked: the duplicate field report"],
|
|
31
|
+
];
|
|
32
|
+
|
|
33
|
+
const SEED_CONTENT_ITEM_HEADLINES_BY_TENANT = [SEED_CONTENT_ITEM_HEADLINES_BY_POSITION_FOR_TENANT_ZERO, SEED_CONTENT_ITEM_HEADLINES_BY_POSITION_FOR_TENANT_ONE] as const;
|
|
34
|
+
|
|
35
|
+
function headlinesForSelectedTenant(): readonly (readonly string[])[] {
|
|
36
|
+
const tenantId = getLocalPreviewTenantId();
|
|
37
|
+
const tenant = LOCAL_PREVIEW_TENANTS.find(candidate => candidate.accountId === tenantId) ?? LOCAL_PREVIEW_TENANTS[0];
|
|
38
|
+
return SEED_CONTENT_ITEM_HEADLINES_BY_TENANT[tenant.fixtureHeadlineSet] ?? SEED_CONTENT_ITEM_HEADLINES_BY_TENANT[0];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function isoTimestampHoursBeforeBase(hours: number): string {
|
|
42
|
+
return new Date(SEED_TIMESTAMP_BASE - hours * ONE_HOUR_IN_MILLISECONDS).toISOString();
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function slugFromHeadline(headline: string): string {
|
|
46
|
+
return headline.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function createLocalPreviewContentItemVersions(): ContentItemVersionRecord[] {
|
|
50
|
+
const stageKeys = contentItemStageKeys(getContentItemStageDefinitions());
|
|
51
|
+
return stageKeys.flatMap((stageKey, stageIndex) => {
|
|
52
|
+
const tenantHeadlines = headlinesForSelectedTenant();
|
|
53
|
+
const headlines = tenantHeadlines[stageIndex % tenantHeadlines.length];
|
|
54
|
+
return headlines.map((headline, headlineIndex) => {
|
|
55
|
+
const slug = slugFromHeadline(headline);
|
|
56
|
+
return {
|
|
57
|
+
id: `preview-item-version-${stageKey}-${headlineIndex}`,
|
|
58
|
+
lineageId: `preview-item-lineage-${stageKey}-${headlineIndex}`,
|
|
59
|
+
versionNumber: 1,
|
|
60
|
+
stage: stageKey,
|
|
61
|
+
type: "article",
|
|
62
|
+
status: stageKey === "published" ? "published" : "draft",
|
|
63
|
+
title: headline,
|
|
64
|
+
headline,
|
|
65
|
+
slug,
|
|
66
|
+
section: headlineIndex === 0 ? "news" : "features",
|
|
67
|
+
updatedAt: isoTimestampHoursBeforeBase(stageIndex * 3 + headlineIndex),
|
|
68
|
+
};
|
|
69
|
+
});
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function createLocalPreviewReferences(): ReferenceRecord[] {
|
|
74
|
+
const curationStatuses = ["pending", "accepted", "accepted", "rejected", "pending"];
|
|
75
|
+
return curationStatuses.map((curationStatus, index) => ({
|
|
76
|
+
id: `preview-reference-${index}`,
|
|
77
|
+
lineageId: `preview-reference-lineage-${index}`,
|
|
78
|
+
versionNumber: 1,
|
|
79
|
+
versionState: "current",
|
|
80
|
+
corpusId: "preview-corpus",
|
|
81
|
+
externalItemId: `preview-source-${index}`,
|
|
82
|
+
title: `Source document ${index + 1}: municipal filing excerpt`,
|
|
83
|
+
curationStatus,
|
|
84
|
+
curationStatusKey: curationStatus,
|
|
85
|
+
curationStatusReason: curationStatus === "rejected" ? "Duplicate of an earlier filing." : null,
|
|
86
|
+
importedAt: isoTimestampHoursBeforeBase(index * 5),
|
|
87
|
+
updatedAt: isoTimestampHoursBeforeBase(index * 5),
|
|
88
|
+
}));
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function createLocalPreviewAssignments(): AssignmentRecord[] {
|
|
92
|
+
const contentItemVersions = createLocalPreviewContentItemVersions();
|
|
93
|
+
const assignmentTypeKeys = ["research", "reporting", "copywriting"];
|
|
94
|
+
const statuses = ["queued", "claimed", "completed"];
|
|
95
|
+
const assignmentsOnContentItems = contentItemVersions.slice(0, 9).map((version, index) => ({
|
|
96
|
+
id: `preview-assignment-${index}`,
|
|
97
|
+
assignmentTypeKey: assignmentTypeKeys[index % assignmentTypeKeys.length],
|
|
98
|
+
queueKey: assignmentTypeKeys[index % assignmentTypeKeys.length],
|
|
99
|
+
queueStatusKey: `${assignmentTypeKeys[index % assignmentTypeKeys.length]}#${statuses[index % statuses.length]}`,
|
|
100
|
+
status: statuses[index % statuses.length],
|
|
101
|
+
priority: 2,
|
|
102
|
+
title: `${assignmentTypeKeys[index % assignmentTypeKeys.length]} for: ${version.headline}`,
|
|
103
|
+
itemLineageId: version.lineageId,
|
|
104
|
+
createdAt: isoTimestampHoursBeforeBase(index * 2),
|
|
105
|
+
updatedAt: isoTimestampHoursBeforeBase(index * 2),
|
|
106
|
+
}));
|
|
107
|
+
const assignmentsWithoutContentItem = ["Pitch: community radio partnership", "Backfill the archive index"].map((title, index) => ({
|
|
108
|
+
id: `preview-assignment-standalone-${index}`,
|
|
109
|
+
assignmentTypeKey: "research",
|
|
110
|
+
queueKey: "research",
|
|
111
|
+
queueStatusKey: "research#queued",
|
|
112
|
+
status: "queued",
|
|
113
|
+
priority: 2,
|
|
114
|
+
title,
|
|
115
|
+
createdAt: isoTimestampHoursBeforeBase(index),
|
|
116
|
+
updatedAt: isoTimestampHoursBeforeBase(index),
|
|
117
|
+
}));
|
|
118
|
+
return [...assignmentsOnContentItems, ...assignmentsWithoutContentItem];
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
export function createLocalPreviewRealPublishedVersions(): ContentItemVersionRecord[] {
|
|
122
|
+
return [];
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function createLocalPreviewEditableArticle(id: string): EditableArticle | null {
|
|
126
|
+
const version = createLocalPreviewContentItemVersions().find(candidate => candidate.id === id);
|
|
127
|
+
if (!version) return null;
|
|
128
|
+
const headline = version.headline || version.title || version.slug;
|
|
129
|
+
return {
|
|
130
|
+
id: version.id,
|
|
131
|
+
lineageId: version.lineageId,
|
|
132
|
+
type: version.type,
|
|
133
|
+
section: version.section ?? null,
|
|
134
|
+
slug: version.slug,
|
|
135
|
+
frontMatterYaml: `title: ${headline}\nsection: ${version.section ?? ""}`,
|
|
136
|
+
bodyMarkus: `# ${headline}\n\nThe local preview shows this story's record. Its full body text is loaded from the live backend.`,
|
|
137
|
+
contentHash: null,
|
|
138
|
+
status: version.status === "published" ? "published" : "draft",
|
|
139
|
+
versionNumber: version.versionNumber,
|
|
140
|
+
aliases: [],
|
|
141
|
+
everPublished: version.status === "published",
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
export function recordLocalPreviewDesk(articleId: string, deskId: string | null): void {
|
|
146
|
+
const version = createLocalPreviewContentItemVersions().find(candidate => candidate.id === articleId);
|
|
147
|
+
if (version) version.section = deskId;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const previewSavedVersions = new Map<string, TrailVersion[]>();
|
|
151
|
+
const previewLineageByVersionId = new Map<string, string>();
|
|
152
|
+
|
|
153
|
+
export function recordLocalPreviewVersion(lineageId: string, version: TrailVersion): void {
|
|
154
|
+
previewSavedVersions.set(lineageId, [version, ...(previewSavedVersions.get(lineageId) ?? [])]);
|
|
155
|
+
previewLineageByVersionId.set(version.id, lineageId);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
export function lineageForPreviewVersion(versionId: string): string | null {
|
|
159
|
+
return previewLineageByVersionId.get(versionId) ?? null;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
export function createLocalPreviewReportingTrail(lineageId: string): StoryReportingTrail {
|
|
163
|
+
const saved = previewSavedVersions.get(lineageId) ?? [];
|
|
164
|
+
return { ...EMPTY_STORY_REPORTING_TRAIL, versions: saved };
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function createLocalPreviewMediaAssets(_itemId: string): MediaAssetRecord[] {
|
|
168
|
+
return [];
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
type PreviewSuggestionPlan = {
|
|
172
|
+
label: "include" | "exclude";
|
|
173
|
+
confidence: number;
|
|
174
|
+
curation: "none" | "human" | "service" | "delegated";
|
|
175
|
+
provenance: boolean;
|
|
176
|
+
};
|
|
177
|
+
|
|
178
|
+
const PREVIEW_SUGGESTION_PLANS: readonly PreviewSuggestionPlan[] = [
|
|
179
|
+
{ label: "include", confidence: 0.91, curation: "none", provenance: true },
|
|
180
|
+
{ label: "include", confidence: 0.86, curation: "human", provenance: true },
|
|
181
|
+
{ label: "exclude", confidence: 0.72, curation: "service", provenance: true },
|
|
182
|
+
{ label: "exclude", confidence: 0.88, curation: "delegated", provenance: true },
|
|
183
|
+
{ label: "exclude", confidence: 0.64, curation: "none", provenance: false },
|
|
184
|
+
];
|
|
185
|
+
|
|
186
|
+
const PREVIEW_CYCLOTRON_ID = "papyrus-relevance";
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Fixture data for reviewing the cyclotron suggestion UI without a backend:
|
|
190
|
+
* suggestions with provenance on the first references, a mix of human and
|
|
191
|
+
* non-human curation messages, and a live-agreement payload.
|
|
192
|
+
*/
|
|
193
|
+
export function createLocalPreviewCyclotronFixtures(references: ReferenceRecord[]): {
|
|
194
|
+
relations: SemanticRelationRecord[];
|
|
195
|
+
messages: MessageRecord[];
|
|
196
|
+
shadowAgreement: ShadowAgreement;
|
|
197
|
+
} {
|
|
198
|
+
const relations: SemanticRelationRecord[] = [];
|
|
199
|
+
const messages: MessageRecord[] = [];
|
|
200
|
+
references.slice(0, PREVIEW_SUGGESTION_PLANS.length).forEach((reference, index) => {
|
|
201
|
+
const plan = PREVIEW_SUGGESTION_PLANS[index];
|
|
202
|
+
const lineageId = reference.lineageId ?? reference.id;
|
|
203
|
+
const decidedAt = isoTimestampHoursBeforeBase(40 - index);
|
|
204
|
+
relations.push({
|
|
205
|
+
id: `preview-relevance-relation-${index}`,
|
|
206
|
+
relationState: "current",
|
|
207
|
+
predicate: "relevance_decision_is",
|
|
208
|
+
subjectKind: "reference",
|
|
209
|
+
subjectId: reference.id,
|
|
210
|
+
subjectLineageId: lineageId,
|
|
211
|
+
objectKind: "semanticNode",
|
|
212
|
+
objectId: `semantic-node-relevance-${plan.label}-v1`,
|
|
213
|
+
objectLineageId: `semantic-node-relevance-${plan.label}`,
|
|
214
|
+
subjectStateKey: `reference#${lineageId}#current`,
|
|
215
|
+
objectStateKey: `semanticNode#semantic-node-relevance-${plan.label}#current`,
|
|
216
|
+
objectSubjectStateKey: `semanticNode#semantic-node-relevance-${plan.label}#current#reference`,
|
|
217
|
+
predicateObjectStateKey: `relevance_decision_is#semanticNode#semantic-node-relevance-${plan.label}#current`,
|
|
218
|
+
subjectVersionKey: `reference#${reference.id}`,
|
|
219
|
+
objectVersionKey: `semanticNode#semantic-node-relevance-${plan.label}-v1`,
|
|
220
|
+
confidence: plan.confidence,
|
|
221
|
+
modelVersion: "4",
|
|
222
|
+
reviewRecommended: false,
|
|
223
|
+
metadata: JSON.stringify({
|
|
224
|
+
decisionId: `preview-decision-${index}`,
|
|
225
|
+
label: plan.label,
|
|
226
|
+
probabilities: { include: plan.label === "include" ? plan.confidence : 1 - plan.confidence, exclude: plan.label === "exclude" ? plan.confidence : 1 - plan.confidence },
|
|
227
|
+
version: 4,
|
|
228
|
+
decidedAt,
|
|
229
|
+
labels: ["include", "exclude"],
|
|
230
|
+
positiveLabel: "include",
|
|
231
|
+
question: "Should this source be used as evidence for the publication?",
|
|
232
|
+
cyclotronId: PREVIEW_CYCLOTRON_ID,
|
|
233
|
+
...(plan.provenance ? {
|
|
234
|
+
provenance: {
|
|
235
|
+
labelSet: ["include", "exclude"],
|
|
236
|
+
doctrineFingerprint: "3f9c2a71d80b46e5",
|
|
237
|
+
teachingFingerprint: "b71e04c9aa5d2f13",
|
|
238
|
+
teachingLabels: 37,
|
|
239
|
+
primingExamples: 6,
|
|
240
|
+
},
|
|
241
|
+
} : {}),
|
|
242
|
+
}),
|
|
243
|
+
} as SemanticRelationRecord);
|
|
244
|
+
if (plan.curation !== "none") {
|
|
245
|
+
const createdAt = isoTimestampHoursBeforeBase(30 - index);
|
|
246
|
+
messages.push({
|
|
247
|
+
id: `preview-curation-message-${index}`,
|
|
248
|
+
messageKind: "reference_curation",
|
|
249
|
+
messageDomain: "commentary",
|
|
250
|
+
status: "active",
|
|
251
|
+
summary: `${reference.title ?? lineageId}: curated`,
|
|
252
|
+
authorLabel: plan.curation === "delegated" ? "Maya Okafor" : null,
|
|
253
|
+
source: "newsroom",
|
|
254
|
+
createdAt,
|
|
255
|
+
updatedAt: createdAt,
|
|
256
|
+
metadata: JSON.stringify({
|
|
257
|
+
action: reference.curationStatus === "rejected" ? "reject" : "accept",
|
|
258
|
+
curationStatus: reference.curationStatus,
|
|
259
|
+
referenceLineageId: lineageId,
|
|
260
|
+
actorKind: plan.curation === "service" ? "service" : "human",
|
|
261
|
+
...(plan.curation === "delegated" ? {
|
|
262
|
+
attestation: "delegated",
|
|
263
|
+
relayedBy: "newsroom-reporter",
|
|
264
|
+
delegationId: "curation-delegation-9b1f6c24-5d0e-4a7b-8c31-2e6f0a1d7b45",
|
|
265
|
+
instruction: "Reject that one, it duplicates the filing from Tuesday.",
|
|
266
|
+
} : {}),
|
|
267
|
+
}),
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
});
|
|
271
|
+
return {
|
|
272
|
+
relations,
|
|
273
|
+
messages,
|
|
274
|
+
shadowAgreement: {
|
|
275
|
+
schema: SHADOW_AGREEMENT_SCHEMA,
|
|
276
|
+
comparisons: 23,
|
|
277
|
+
agreement: 0.87,
|
|
278
|
+
precision: 0.9,
|
|
279
|
+
recall: 0.82,
|
|
280
|
+
confusion: { truePositive: 9, falsePositive: 1, falseNegative: 2, trueNegative: 11 },
|
|
281
|
+
computedAt: isoTimestampHoursBeforeBase(3),
|
|
282
|
+
positiveLabel: "include",
|
|
283
|
+
loop: { enabled: true, stoppedBecause: "daily decision cap", lastSweepAt: isoTimestampHoursBeforeBase(3), maxDecisionsPerDay: 50 },
|
|
284
|
+
},
|
|
285
|
+
};
|
|
286
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The local preview stands in for the multi-tenant newsroom: choosing a publishing account in the account menu
|
|
3
|
+
* switches which publication's content the preview shows. The choice is a cookie so server and browser agree.
|
|
4
|
+
*/
|
|
5
|
+
export const LOCAL_PREVIEW_TENANT_COOKIE = "papyrus-preview-tenant";
|
|
6
|
+
export const LOCAL_PREVIEW_DEFAULT_TENANT_ID = "preview-papyrus";
|
|
7
|
+
|
|
8
|
+
export type LocalPreviewTenant = { accountId: string; name: string; role: string; fixtureHeadlineSet: number };
|
|
9
|
+
|
|
10
|
+
export const LOCAL_PREVIEW_TENANTS: readonly LocalPreviewTenant[] = [
|
|
11
|
+
{ accountId: LOCAL_PREVIEW_DEFAULT_TENANT_ID, name: "Papyrus", role: "publisher", fixtureHeadlineSet: 0 },
|
|
12
|
+
{ accountId: "preview-pilobolus", name: "Pilobolus", role: "editor", fixtureHeadlineSet: 1 },
|
|
13
|
+
];
|
|
14
|
+
|
|
15
|
+
export function previewTenantIdFromCookieHeader(cookieHeader: string | null | undefined): string {
|
|
16
|
+
const match = cookieHeader?.split(";").map(part => part.trim()).find(part => part.startsWith(`${LOCAL_PREVIEW_TENANT_COOKIE}=`));
|
|
17
|
+
const value = match ? decodeURIComponent(match.slice(LOCAL_PREVIEW_TENANT_COOKIE.length + 1)) : "";
|
|
18
|
+
return LOCAL_PREVIEW_TENANTS.some(tenant => tenant.accountId === value) ? value : LOCAL_PREVIEW_DEFAULT_TENANT_ID;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export function getLocalPreviewTenantId(): string {
|
|
22
|
+
return typeof document === "undefined" ? LOCAL_PREVIEW_DEFAULT_TENANT_ID : previewTenantIdFromCookieHeader(document.cookie);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function setLocalPreviewTenantId(accountId: string): void {
|
|
26
|
+
document.cookie = `${LOCAL_PREVIEW_TENANT_COOKIE}=${encodeURIComponent(accountId)}; path=/; max-age=31536000; samesite=lax`;
|
|
27
|
+
}
|