@agent-native/core-corpus 0.216.0 → 0.217.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 (149) hide show
  1. package/corpus/README.md +1 -1
  2. package/corpus/templates/analytics/.agents/skills/session-replay/SKILL.md +61 -1
  3. package/corpus/templates/analytics/AGENTS.md +1 -1
  4. package/corpus/templates/analytics/CHANGELOG.md +1 -0
  5. package/corpus/templates/analytics/actions/get-session-replay-summary.ts +9 -65
  6. package/corpus/templates/analytics/actions/list-event-catalog.ts +1 -1
  7. package/corpus/templates/analytics/actions/list-session-outputs.ts +40 -0
  8. package/corpus/templates/analytics/actions/list-session-recordings.ts +17 -4
  9. package/corpus/templates/analytics/actions/view-screen.ts +29 -3
  10. package/corpus/templates/analytics/app/i18n/en-US.ts +27 -0
  11. package/corpus/templates/analytics/app/pages/sessions/EventCatalogPage.tsx +18 -1
  12. package/corpus/templates/analytics/app/pages/sessions/SessionDetailPage.tsx +3 -1
  13. package/corpus/templates/analytics/app/pages/sessions/SessionOutputs.tsx +197 -0
  14. package/corpus/templates/analytics/app/pages/sessions/SessionsTriagePage.tsx +114 -4
  15. package/corpus/templates/analytics/changelog/2026-10-15-sessions-can-find-people-who-made-a-slides-deck-but-didn-t-p.md +6 -0
  16. package/corpus/templates/analytics/changelog/2026-10-16-the-activation-funnel-counts-visitors-who-decline-analytics-.md +6 -0
  17. package/corpus/templates/analytics/server/db/schema.ts +67 -0
  18. package/corpus/templates/analytics/server/lib/canonical-first-party-dashboard-repair.ts +9 -0
  19. package/corpus/templates/analytics/server/lib/first-party-analytics.ts +28 -22
  20. package/corpus/templates/analytics/server/lib/first-party-metric-catalog.ts +36 -1
  21. package/corpus/templates/analytics/server/lib/output-outcomes.ts +712 -0
  22. package/corpus/templates/analytics/server/lib/replay-origins.ts +33 -0
  23. package/corpus/templates/analytics/server/lib/session-event-index.ts +20 -0
  24. package/corpus/templates/analytics/server/lib/session-performance.ts +6 -40
  25. package/corpus/templates/analytics/server/lib/session-recording-associations.ts +30 -1
  26. package/corpus/templates/analytics/server/lib/session-replay-triage.ts +99 -0
  27. package/corpus/templates/analytics/server/lib/session-replay.ts +45 -31
  28. package/corpus/templates/analytics/server/lib/sessions-triage-lab.ts +8 -2
  29. package/corpus/templates/analytics/server/plugins/db.ts +46 -0
  30. package/corpus/templates/analytics/shared/output-use.ts +205 -0
  31. package/corpus/templates/analytics/shared/session-events.ts +6 -0
  32. package/corpus/templates/calendar/CHANGELOG.md +6 -0
  33. package/corpus/templates/calendar/actions/create-event.ts +2 -4
  34. package/corpus/templates/calendar/actions/update-event.ts +35 -6
  35. package/corpus/templates/calendar/changelog/2026-10-21-solo-events-avoid-notification-approval.md +6 -0
  36. package/corpus/templates/community/qr/package.json +1 -1
  37. package/corpus/templates/content/.agents/skills/document-editing/references/databases.md +6 -3
  38. package/corpus/templates/content/CHANGELOG.md +8 -0
  39. package/corpus/templates/content/actions/_browser-document-save-attempt.ts +23 -1
  40. package/corpus/templates/content/actions/_builder-cms-blob-custody.ts +467 -0
  41. package/corpus/templates/content/actions/_builder-cms-execution-lease.ts +17 -0
  42. package/corpus/templates/content/actions/_builder-cms-intent-lookup.ts +5 -0
  43. package/corpus/templates/content/actions/_builder-cms-read-client.ts +881 -21
  44. package/corpus/templates/content/actions/_builder-cms-write-adapter.ts +28 -2
  45. package/corpus/templates/content/actions/_builder-cms-write-client.ts +109 -11
  46. package/corpus/templates/content/actions/_builder-docs-client.ts +1 -0
  47. package/corpus/templates/content/actions/_builder-private-blob.test-fixture.ts +90 -0
  48. package/corpus/templates/content/actions/_builder-source-execution-guard.ts +48 -0
  49. package/corpus/templates/content/actions/_content-save-outcomes.ts +5 -0
  50. package/corpus/templates/content/actions/_database-source-utils.ts +290 -8
  51. package/corpus/templates/content/actions/_database-utils.ts +93 -7
  52. package/corpus/templates/content/actions/_database-view-setup.ts +21 -2
  53. package/corpus/templates/content/actions/_delete-content-space.ts +8 -1
  54. package/corpus/templates/content/actions/_github-source-freshness.ts +30 -4
  55. package/corpus/templates/content/actions/add-content-database-source-field-property.ts +23 -6
  56. package/corpus/templates/content/actions/cancel-prepared-builder-source-update.ts +110 -26
  57. package/corpus/templates/content/actions/change-content-database-source-role.ts +51 -7
  58. package/corpus/templates/content/actions/delete-document.ts +41 -3
  59. package/corpus/templates/content/actions/disconnect-content-database-source.ts +68 -2
  60. package/corpus/templates/content/actions/disconnect-local-folder-source.ts +33 -2
  61. package/corpus/templates/content/actions/execute-builder-source-execution.ts +900 -140
  62. package/corpus/templates/content/actions/get-document.ts +2 -0
  63. package/corpus/templates/content/actions/permanently-delete-document.ts +6 -0
  64. package/corpus/templates/content/actions/prepare-builder-source-execution.ts +155 -13
  65. package/corpus/templates/content/actions/prepare-builder-source-review.ts +299 -73
  66. package/corpus/templates/content/actions/set-live-body-switch.ts +45 -0
  67. package/corpus/templates/content/actions/update-document.ts +139 -20
  68. package/corpus/templates/content/actions/validate-builder-source-execution.ts +135 -26
  69. package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +23 -0
  70. package/corpus/templates/content/app/components/editor/database/GitHubSourceStatusLine.tsx +27 -2
  71. package/corpus/templates/content/app/components/editor/database/ListItemLayout.tsx +32 -8
  72. package/corpus/templates/content/app/components/editor/database/ListItemRow.tsx +27 -2
  73. package/corpus/templates/content/app/components/editor/database-sources/BuilderSourceReviewDialog.tsx +1 -0
  74. package/corpus/templates/content/app/hooks/use-content-database.ts +40 -2
  75. package/corpus/templates/content/app/hooks/use-documents.ts +14 -0
  76. package/corpus/templates/content/app/i18n-data.ts +1 -0
  77. package/corpus/templates/content/changelog/2026-10-17-builder-content-avoids-retrying-a-write-when-builder-accepte.md +6 -0
  78. package/corpus/templates/content/changelog/2026-10-18-link-a-new-collection-to-a-github-folder-from-its-sources.md +1 -1
  79. package/corpus/templates/content/changelog/2026-10-20-builder-content-sync-keeps-read-and-write-status-accurate-wh.md +6 -0
  80. package/corpus/templates/content/changelog/2026-10-21-list-rows-can-show-each-pages-first-lines.md +6 -0
  81. package/corpus/templates/content/changelog/2026-10-22-a-collection-linked-to-a-big-github-folder-fills-faster-and-p.md +6 -0
  82. package/corpus/templates/content/docs/product/capabilities/content.source.adapters.md +29 -0
  83. package/corpus/templates/content/parity/matrix.md +42 -42
  84. package/corpus/templates/content/parity/matrix.ts +3 -1
  85. package/corpus/templates/content/server/db/schema.ts +20 -0
  86. package/corpus/templates/content/server/lib/content-trash-purge.ts +15 -1
  87. package/corpus/templates/content/server/lib/live-body-writer.ts +138 -26
  88. package/corpus/templates/content/server/live-copy/binding.ts +31 -0
  89. package/corpus/templates/content/server/live-copy/bounded-task.d.ts +6 -0
  90. package/corpus/templates/content/server/live-copy/exec-db.ts +1 -0
  91. package/corpus/templates/content/server/live-copy/holds.ts +59 -0
  92. package/corpus/templates/content/server/live-copy/persist.ts +656 -0
  93. package/corpus/templates/content/server/live-copy/switch.ts +136 -0
  94. package/corpus/templates/content/server/live-copy/task-names.ts +4 -0
  95. package/corpus/templates/content/server/live-copy/tasks.ts +183 -0
  96. package/corpus/templates/content/server/live-copy/write-cause.ts +85 -0
  97. package/corpus/templates/content/server/live-copy/write.ts +85 -0
  98. package/corpus/templates/content/server/plugins/builder-private-payload-cleanup.ts +22 -0
  99. package/corpus/templates/content/server/plugins/collab.ts +3 -0
  100. package/corpus/templates/content/server/plugins/db.ts +28 -0
  101. package/corpus/templates/content/shared/api.ts +30 -1
  102. package/corpus/templates/content/shared/database-item-layout.ts +54 -22
  103. package/corpus/templates/content/shared/document-intent-merge.ts +16 -9
  104. package/corpus/templates/content/shared/og-description.ts +258 -36
  105. package/corpus/templates/design/.agents/skills/design-templates/SKILL.md +5 -1
  106. package/corpus/templates/design/.generated/bridge/shader-runtime.generated.ts +25 -4
  107. package/corpus/templates/design/CHANGELOG.md +2 -0
  108. package/corpus/templates/design/actions/get-getting-started.ts +53 -0
  109. package/corpus/templates/design/app/components/design/DesignCanvas.tsx +10 -0
  110. package/corpus/templates/design/app/components/design/bridge/native-shader-runtime.bridge.ts +132 -79
  111. package/corpus/templates/design/app/components/design/bridge/native-source-provider.ts +268 -130
  112. package/corpus/templates/design/app/components/design/bridge/native-worker-scene-host.ts +122 -42
  113. package/corpus/templates/design/app/components/design/bridge/shader-runtime.bridge.ts +41 -4
  114. package/corpus/templates/design/app/components/design/design-canvas/text-content-change-result.ts +46 -12
  115. package/corpus/templates/design/app/components/home/DesignHomeLibrary.tsx +15 -8
  116. package/corpus/templates/design/app/components/layout/GettingStarted.tsx +50 -0
  117. package/corpus/templates/design/app/components/layout/Sidebar.tsx +3 -0
  118. package/corpus/templates/design/app/components/templates/DesignTemplateGrid.tsx +163 -0
  119. package/corpus/templates/design/app/components/templates/DesignTemplateLibrary.tsx +65 -78
  120. package/corpus/templates/design/app/components/templates/TemplatePreview.tsx +6 -2
  121. package/corpus/templates/design/app/global.css +6 -0
  122. package/corpus/templates/design/app/i18n/en-US.ts +17 -0
  123. package/corpus/templates/design/app/lib/canvas-load-outcome.ts +147 -0
  124. package/corpus/templates/design/app/lib/canvas-outcome-tracking.ts +531 -0
  125. package/corpus/templates/design/app/lib/canvas-style-kind.ts +50 -0
  126. package/corpus/templates/design/app/pages/Index.tsx +10 -3
  127. package/corpus/templates/design/app/pages/design-editor/commands/commit-visual-styles.ts +86 -29
  128. package/corpus/templates/design/app/pages/design-editor/commands/save-file-content.ts +38 -3
  129. package/corpus/templates/design/app/pages/design-editor/domains/use-editor-core.ts +9 -0
  130. package/corpus/templates/design/app/pages/design-editor/domains/use-editor-files-and-saving.ts +2 -0
  131. package/corpus/templates/design/changelog/2026-10-21-a-getting-started-checklist-at-the-bottom-of-the-sidebar-tra.md +6 -0
  132. package/corpus/templates/design/changelog/2026-10-21-templates-now-open-a-read-only-preview-from-the-card-and-the.md +6 -0
  133. package/corpus/templates/design/changelog/2026-10-22-collapsed-shader-layers-no-longer-interrupt-other-shaders-on.md +6 -0
  134. package/corpus/templates/design/changelog/2026-10-22-shader-fills-keep-rendering-when-other-screen-content-cannot.md +6 -0
  135. package/corpus/templates/design/scripts/design-export-validation/export-baseline.json +24 -0
  136. package/corpus/templates/design/server/lib/getting-started.ts +267 -0
  137. package/corpus/templates/design/server/plugins/getting-started.ts +5 -0
  138. package/corpus/templates/design/shared/design-template-presets/analytics-dashboard.ts +127 -0
  139. package/corpus/templates/design/shared/design-template-presets/design-studio.ts +98 -0
  140. package/corpus/templates/design/shared/design-template-presets/messaging-app.ts +101 -0
  141. package/corpus/templates/design/shared/design-template-presets/project-tasks.ts +120 -0
  142. package/corpus/templates/design/shared/design-template-presets.ts +8 -0
  143. package/corpus/templates/forms/server/plugins/agent-chat.ts +1 -1
  144. package/corpus/templates/slides/CHANGELOG.md +6 -0
  145. package/corpus/templates/slides/actions/delete-deck.ts +7 -0
  146. package/corpus/templates/slides/changelog/2026-10-21-connected-deck-deletion-requires-approval.md +6 -0
  147. package/corpus/templates/slides/scripts/edit-fidelity/client-action.ts +61 -0
  148. package/corpus/templates/slides/scripts/edit-fidelity/run.ts +10 -0
  149. package/package.json +1 -1
@@ -93,6 +93,7 @@ function PropertyChoices({
93
93
  selectedIds: savedIds,
94
94
  multiple,
95
95
  noneLabel,
96
+ otherChoice,
96
97
  onChange,
97
98
  }: {
98
99
  label: string;
@@ -100,6 +101,8 @@ function PropertyChoices({
100
101
  selectedIds: string[];
101
102
  multiple: boolean;
102
103
  noneLabel?: string;
104
+ /** A choice that isn't a property, listed after Nothing (Line 2's page text). */
105
+ otherChoice?: { label: string; selected: boolean; onSelect: () => void };
103
106
  onChange: (ids: string[]) => void;
104
107
  }) {
105
108
  // A saved slot can name a property this menu doesn't offer: one hidden, or
@@ -107,8 +110,11 @@ function PropertyChoices({
107
110
  // In Line 3 and End it stays saved for when it's shown again.
108
111
  const offeredIds = properties.map((property) => property.definition.id);
109
112
  const selectedIds = savedIds.filter((id) => offeredIds.includes(id));
110
- const rows = [
113
+ const rows: Array<{ id: string; name: string; other?: true }> = [
111
114
  ...(noneLabel ? [{ id: "", name: noneLabel }] : []),
115
+ ...(otherChoice
116
+ ? [{ id: "", name: otherChoice.label, other: true as const }]
117
+ : []),
112
118
  ...properties.map((property) => ({
113
119
  id: property.definition.id,
114
120
  name: property.definition.name,
@@ -121,9 +127,11 @@ function PropertyChoices({
121
127
  className="grid max-h-56 gap-0.5 overflow-y-auto"
122
128
  >
123
129
  {rows.map((row) => {
124
- const selected = row.id
125
- ? selectedIds.includes(row.id)
126
- : selectedIds.length === 0;
130
+ const selected = row.other
131
+ ? otherChoice!.selected
132
+ : row.id
133
+ ? selectedIds.includes(row.id)
134
+ : selectedIds.length === 0 && !otherChoice?.selected;
127
135
  const position = row.id ? selectedIds.indexOf(row.id) : -1;
128
136
  const full =
129
137
  multiple &&
@@ -132,12 +140,13 @@ function PropertyChoices({
132
140
  selectedIds.length >= MAX_ITEM_LAYOUT_META_PROPERTIES;
133
141
  return (
134
142
  <button
135
- key={row.id || "none"}
143
+ key={row.id || (row.other ? "other" : "none")}
136
144
  type="button"
137
145
  aria-pressed={selected}
138
146
  disabled={full}
139
147
  className="flex h-8 items-center gap-2 rounded px-2 text-left text-sm enabled:hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-50"
140
148
  onClick={() => {
149
+ if (row.other) return otherChoice!.onSelect();
141
150
  if (!multiple) return onChange(row.id ? [row.id] : []);
142
151
  onChange(
143
152
  toggleContentDatabaseItemLayoutMetaProperty(
@@ -253,22 +262,37 @@ export function ListItemLayoutSlotEditor({
253
262
  );
254
263
  }
255
264
  if (slot === "line2") {
265
+ const pageText = layout.line2.source === "page";
266
+ const { lines } = layout.line2;
256
267
  return (
257
268
  <div className="grid gap-2">
258
269
  <PropertyChoices
259
270
  label={label}
260
271
  properties={itemLayoutLine2Candidates(properties)}
261
- selectedIds={layout.line2.propertyId ? [layout.line2.propertyId] : []}
272
+ selectedIds={
273
+ !pageText && layout.line2.propertyId
274
+ ? [layout.line2.propertyId]
275
+ : []
276
+ }
262
277
  multiple={false}
263
278
  noneLabel={dbText("listRowsNothing")}
279
+ otherChoice={{
280
+ label: dbText("listRowsPageText"),
281
+ selected: pageText,
282
+ onSelect: () =>
283
+ onChange({
284
+ ...layout,
285
+ line2: { source: "page", propertyId: null, lines },
286
+ }),
287
+ }}
264
288
  onChange={([id]) =>
265
289
  onChange({
266
290
  ...layout,
267
- line2: { ...layout.line2, propertyId: id ?? null },
291
+ line2: { propertyId: id ?? null, lines },
268
292
  })
269
293
  }
270
294
  />
271
- {layout.line2.propertyId ? (
295
+ {pageText || layout.line2.propertyId ? (
272
296
  <Segmented<1 | 2 | 3>
273
297
  label={label}
274
298
  value={layout.line2.lines}
@@ -7,7 +7,10 @@ import type {
7
7
  ContentDatabaseItemLayoutSlot,
8
8
  ResolvedContentDatabaseItemLayout,
9
9
  } from "@shared/database-item-layout";
10
- import { stripMarkdownForPreview } from "@shared/og-description";
10
+ import {
11
+ buildListRowExcerpt,
12
+ stripMarkdownForPreview,
13
+ } from "@shared/og-description";
11
14
  import {
12
15
  isEmptyPropertyValue,
13
16
  type DocumentPropertyOptionColor,
@@ -93,11 +96,29 @@ function line2Text(property: DocumentProperty) {
93
96
  : value.trim();
94
97
  }
95
98
 
99
+ /**
100
+ * The first lines of a row's page. The list read sends an excerpt, and saving
101
+ * the page's body in this tab rebuilds it. Until a read brings one (right after
102
+ * Page text is picked), a body this tab already holds stands in.
103
+ */
104
+ function pageExcerpt(item: ContentDatabaseItem) {
105
+ if (item.excerpt !== undefined) return item.excerpt;
106
+ return item.document.content
107
+ ? buildListRowExcerpt({
108
+ content: item.document.content,
109
+ title: item.document.title,
110
+ })
111
+ : "";
112
+ }
113
+
96
114
  /** The fields a slot shows, which can be set but blank on a given row. */
97
115
  function slotFieldNames(
98
116
  resolved: ResolvedContentDatabaseItemLayout<DocumentProperty>,
99
117
  slot: ContentDatabaseItemLayoutSlot,
100
118
  ) {
119
+ if (slot === "line2" && resolved.line2.pageText) {
120
+ return [dbText("listRowsPageText")];
121
+ }
101
122
  const fields =
102
123
  slot === "line2"
103
124
  ? resolved.line2.property
@@ -305,7 +326,11 @@ export function DatabaseLayoutListRow({
305
326
  const line2Property = resolved.line2.property
306
327
  ? itemValue(item, resolved.line2.property)
307
328
  : null;
308
- const line2 = line2Property ? line2Text(line2Property) : "";
329
+ const line2 = resolved.line2.pageText
330
+ ? pageExcerpt(item)
331
+ : line2Property
332
+ ? line2Text(line2Property)
333
+ : "";
309
334
  const filled = (properties: DocumentProperty[]) =>
310
335
  properties
311
336
  .map((property) => itemValue(item, property))
@@ -238,6 +238,7 @@ export function builderReviewRowCanCancelPrepared(
238
238
  ) {
239
239
  const execution = row.execution;
240
240
  if (!execution) return false;
241
+ if (execution.payloadUnavailable) return false;
241
242
  if (
242
243
  execution.state !== "ready" &&
243
244
  execution.state !== "write_disabled" &&
@@ -58,6 +58,7 @@ import type {
58
58
  ValidateBuilderSourceExecutionRequest,
59
59
  DocumentPropertyRelationTarget,
60
60
  } from "@shared/api";
61
+ import { contentDatabaseViewsShowPageText } from "@shared/database-item-layout";
61
62
  import type { Query, QueryClient } from "@tanstack/react-query";
62
63
  import { useQuery, useQueryClient } from "@tanstack/react-query";
63
64
 
@@ -255,6 +256,29 @@ export function contentDatabaseItemsContainingDocumentFilter(
255
256
  };
256
257
  }
257
258
 
259
+ /**
260
+ * Collection reads whose row for a page shows its page text. The server skips
261
+ * a first paragraph that repeats the title, so a rename can change that text.
262
+ */
263
+ export function contentDatabaseExcerptContainingDocumentFilter(
264
+ documentId: string,
265
+ ) {
266
+ return {
267
+ queryKey: ["action", "get-content-database"],
268
+ predicate: (query: Query) => {
269
+ const data = query.state.data as
270
+ | Partial<ContentDatabaseResponse>
271
+ | undefined;
272
+ return (
273
+ data?.items?.some(
274
+ (item) =>
275
+ item.document.id === documentId && item.excerpt !== undefined,
276
+ ) === true
277
+ );
278
+ },
279
+ };
280
+ }
281
+
258
282
  export function invalidateContentDatabaseNavigationQueries(
259
283
  queryClient: Pick<QueryClient, "invalidateQueries">,
260
284
  scope: { databaseId?: string; parentId?: string | null } = {},
@@ -504,6 +528,20 @@ export function isContentDatabaseUnavailable(
504
528
  );
505
529
  }
506
530
 
531
+ /**
532
+ * Unpaged reads (row menus, pickers) leave out page text, so one can't stand
533
+ * in for the List's read of a collection whose List view shows it.
534
+ */
535
+ function canSeedContentDatabaseRead(
536
+ data: ContentDatabaseResponse | undefined,
537
+ ): data is ContentDatabaseResponse {
538
+ if (!data || isContentDatabaseUnavailable(data)) return false;
539
+ return (
540
+ !contentDatabaseViewsShowPageText(data.database?.viewConfig?.views ?? []) ||
541
+ data.items.every((item) => item.excerpt !== undefined)
542
+ );
543
+ }
544
+
507
545
  export function readCachedContentDatabaseResponse(
508
546
  queryClient: Pick<QueryClient, "getQueryData" | "getQueriesData">,
509
547
  documentId: string,
@@ -511,13 +549,13 @@ export function readCachedContentDatabaseResponse(
511
549
  const exact = queryClient.getQueryData<ContentDatabaseResponse>(
512
550
  contentDatabaseQueryKey(documentId),
513
551
  );
514
- if (exact && !isContentDatabaseUnavailable(exact)) return exact;
552
+ if (canSeedContentDatabaseRead(exact)) return exact;
515
553
 
516
554
  const cached = queryClient
517
555
  .getQueriesData<ContentDatabaseResponse>(
518
556
  contentDatabaseQueryFilter(documentId),
519
557
  )
520
- .find(([, data]) => data && !isContentDatabaseUnavailable(data));
558
+ .find(([, data]) => canSeedContentDatabaseRead(data));
521
559
  return cached?.[1];
522
560
  }
523
561
 
@@ -29,6 +29,7 @@ import type {
29
29
  import type { ContentSidebarSections } from "@shared/content-personal-navigation";
30
30
  import type { ContentRecentResult } from "@shared/content-personal-navigation";
31
31
  import { applyContentPersonalNavigationPatch } from "@shared/content-personal-navigation-patch";
32
+ import { buildListRowExcerpt } from "@shared/og-description";
32
33
  import type { QueryClient } from "@tanstack/react-query";
33
34
  import {
34
35
  hashKey,
@@ -88,6 +89,7 @@ import {
88
89
  } from "./use-content-action-mutation";
89
90
  import {
90
91
  contentDatabaseConstrainedQueryFilter,
92
+ contentDatabaseExcerptContainingDocumentFilter,
91
93
  contentDatabaseItemsContainingDocumentFilter,
92
94
  contentDatabaseNavigationQueryFilter,
93
95
  invalidateContentDatabaseNavigationQueries,
@@ -455,6 +457,17 @@ export function patchDocumentInDatabaseCache<
455
457
  return {
456
458
  ...item,
457
459
  document: { ...item.document, ...patch },
460
+ // A saved body, even an empty one, replaces the excerpt the read sent.
461
+ ...(patch.content !== undefined &&
462
+ "excerpt" in item &&
463
+ item.excerpt !== undefined
464
+ ? {
465
+ excerpt: buildListRowExcerpt({
466
+ content: patch.content,
467
+ title: patch.title ?? item.document.title,
468
+ }),
469
+ }
470
+ : {}),
458
471
  };
459
472
  });
460
473
  return changed ? ({ ...current, items } as T) : current;
@@ -1554,6 +1567,7 @@ export function useUpdateDocument(options?: { saveOrigin?: "recovery" }) {
1554
1567
  if (variables.title !== undefined) {
1555
1568
  invalidateContentQueries(queryClient, [
1556
1569
  contentDatabaseConstrainedQueryFilter(),
1570
+ contentDatabaseExcerptContainingDocumentFilter(variables.id),
1557
1571
  ...contentRowTargets(queryClient, [variables.id]),
1558
1572
  ["action", "search-documents"],
1559
1573
  ]);
@@ -473,6 +473,7 @@ const databaseMessages = {
473
473
  listRowsMedium: "Medium",
474
474
  listRowsBold: "Bold",
475
475
  listRowsNothing: "Nothing",
476
+ listRowsPageText: "Page text",
476
477
  listRowsOneLine: "1 line",
477
478
  listRowsLineCount: "{{count}} lines",
478
479
  listRowsNoStatus: "No status",
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-10-17
4
+ ---
5
+
6
+ Builder Content avoids retrying a write when Builder accepted it but the response could not be saved.
@@ -3,4 +3,4 @@ type: added
3
3
  date: 2026-10-18
4
4
  ---
5
5
 
6
- Make a collection from a GitHub folder in the sidebar's New menu, or fill an empty one from its Sources settings, which say up front when GitHub can't link and what to do instead. A short preview comes before linking, the collection shows where its pages come from and when they last updated, and unlinking asks first
6
+ Fill a collection from a GitHub folder through its Sources settings, which an empty collection links to from under its title and which say up front when GitHub can't link and what to do instead. A short preview comes before linking, the collection shows where its pages come from and when they last updated, with a link back to its folder in Sources, and unlinking asks first
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: fixed
3
+ date: 2026-10-20
4
+ ---
5
+
6
+ Builder content sync keeps read and write status accurate when API responses fail.
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: added
3
+ date: 2026-10-21
4
+ ---
5
+
6
+ List rows can show the first lines of each page under its title: pick **Page text** for Line 2
@@ -0,0 +1,6 @@
1
+ ---
2
+ type: improved
3
+ date: 2026-10-22
4
+ ---
5
+
6
+ A collection linked to a big GitHub folder fills faster, and pushes to other linked collections aren't held up while it does
@@ -134,6 +134,35 @@ adapters that are valuable substrate. Existing provider-specific behavior does
134
134
  not yet certify the common declaration model, every operation, or cross-source
135
135
  authorization and fidelity behavior. This Capability remains `in_progress`.
136
136
 
137
+ The bounded Builder OAuth consumer requests general content read/write scopes,
138
+ uses the existing models, query-data, and Write API routes, and binds
139
+ subsequent mutation calls to the Builder space observed during source refresh.
140
+ Focused client tests cover general OAuth precedence, legacy and Publish grant
141
+ separation, raw draft-inclusive enumeration, exact-entry reads, cross-space
142
+ rejection, and ambiguous writes without replay. This is adapter substrate, not
143
+ certification: Builder model discovery remains developer/admin-only, indexed
144
+ reads do not expose the editable autosave head, and the Write API has no
145
+ conditional write.
146
+
147
+ Existing-entry Builder writes are guarded on the Content side. Review records
148
+ the entry's Builder `lastUpdated` from the last refresh, and a later refresh
149
+ that moves it makes the review stale. Immediately before each existing-entry
150
+ write, Content reads the live `lastUpdated` through the bound OAuth content API
151
+ and blocks without writing when it differs from the reviewed value or when
152
+ either value is missing. This narrows the overwrite window but does not close
153
+ it: a Builder save that lands after that read and before Content's write
154
+ reaches Builder is still overwritten. Closing that window would need a
155
+ conditional write in Builder's API, which is out of scope. Unsaved Builder
156
+ editor state is not visible to the check. Focused execution tests cover it; it
157
+ has not been exercised against a live Builder space. New-entry creation keeps
158
+ its existing intent-reconciliation path.
159
+
160
+ Prepared requests and receipts use identity-bound private blobs with verified
161
+ readback; SQL stores compact opaque references. Exact replacement and deletion
162
+ cleanup runs after confirmed database success; ambiguous outcomes retain
163
+ evidence. A crash before pointer persistence can leave an unreachable blob; no
164
+ general garbage collector is added.
165
+
137
166
  ## Proof plan
138
167
 
139
168
  1. Validate adapter declarations and Action routing for a read-only provider,