@agent-native/core-corpus 0.210.2 → 0.211.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/corpus/README.md +1 -1
- package/corpus/templates/analytics/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/analytics/.agents/skills/dashboard-management/SKILL.md +41 -2
- package/corpus/templates/analytics/.agents/skills/data-querying/SKILL.md +4 -2
- package/corpus/templates/analytics/CHANGELOG.md +18 -0
- package/corpus/templates/analytics/actions/build-data-index.ts +11 -2
- package/corpus/templates/analytics/actions/configure-content-collection.ts +29 -4
- package/corpus/templates/analytics/actions/configure-first-party-events-model.ts +9 -7
- package/corpus/templates/analytics/actions/create-analytics-public-key.ts +4 -0
- package/corpus/templates/analytics/actions/estimate-content-signups-cost.ts +72 -0
- package/corpus/templates/analytics/actions/estimate-query-cost.ts +16 -134
- package/corpus/templates/analytics/actions/list-sigma-workbooks.ts +123 -42
- package/corpus/templates/analytics/actions/migrate-sigma-workbook.ts +20 -17
- package/corpus/templates/analytics/actions/revoke-analytics-public-key.ts +4 -0
- package/corpus/templates/analytics/app/components/dashboard/SigmaMigrationDialog.tsx +246 -680
- package/corpus/templates/analytics/app/components/layout/Sidebar.tsx +1 -1
- package/corpus/templates/analytics/app/i18n/en-US.ts +54 -55
- package/corpus/templates/analytics/app/pages/DashboardOverview.tsx +0 -1
- package/corpus/templates/analytics/app/pages/DataSources.tsx +56 -0
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/filter-vars.ts +4 -3
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/index.tsx +26 -8
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/interpolate.ts +3 -1
- package/corpus/templates/analytics/app/pages/adhoc/sql-dashboard/panel-cost-notice.ts +172 -0
- package/corpus/templates/analytics/app/pages/data-sources/SearchConsoleDetail.tsx +441 -0
- package/corpus/templates/analytics/changelog/2026-10-15-a-content-dashboard-lists-every-blog-article-with-its-author.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-15-data-sources-has-a-google-search-console-card-that-shows-the.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-16-the-content-dashboard-can-show-signups-per-blog-article.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-19-creating-or-revoking-a-public-analytics-key-now-asks-you-to-.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-19-search-sigma-workbooks-by-name-and-open-ones-a-teammate-alre.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-blog-signup-attribution-excludes-signups-before-the-first-vi.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-collapsed-analytics-navigation-starts-at-the-top.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-content-keeps-successful-coverage-dates-and-missing-day-gaps.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-private-github-repositories-can-be-indexed-with-your-connect.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-search-console-preserves-backfill-and-paused-collection.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-search-console-refreshes-historical-coverage-when-properties.md +6 -0
- package/corpus/templates/analytics/changelog/2026-10-20-search-console-setup-keeps-saved-properties-readable-and-sup.md +6 -0
- package/corpus/templates/analytics/seeds/dashboards/content-view.json +182 -0
- package/corpus/templates/analytics/server/db/schema.ts +1 -0
- package/corpus/templates/analytics/server/lib/bi-migration/external-dashboards-store.ts +56 -18
- package/corpus/templates/analytics/server/lib/bi-migration/unfinished-import.ts +64 -0
- package/corpus/templates/analytics/server/lib/builtin-dashboard-pass.ts +17 -2
- package/corpus/templates/analytics/server/lib/content-metrics/collect.ts +2 -0
- package/corpus/templates/analytics/server/lib/content-metrics/config.ts +92 -3
- package/corpus/templates/analytics/server/lib/content-metrics/legacy-content-view.json +172 -0
- package/corpus/templates/analytics/server/lib/content-metrics/migration.ts +2 -0
- package/corpus/templates/analytics/server/lib/content-metrics/steps/daily-metrics.ts +8 -3
- package/corpus/templates/analytics/server/lib/content-metrics/steps/search-console.ts +16 -3
- package/corpus/templates/analytics/server/lib/content-metrics/steps/signups.ts +240 -0
- package/corpus/templates/analytics/server/lib/content-metrics/store.ts +5 -0
- package/corpus/templates/analytics/server/lib/dashboard-catalog.ts +21 -0
- package/corpus/templates/analytics/server/lib/dashboard-panel-verification.ts +300 -5
- package/corpus/templates/analytics/server/lib/dashboard-seeds.ts +2 -0
- package/corpus/templates/analytics/server/lib/dashboards-store.ts +28 -0
- package/corpus/templates/analytics/server/lib/first-party-analytics-backend.ts +128 -24
- package/corpus/templates/analytics/server/lib/google-analytics.ts +56 -16
- package/corpus/templates/analytics/server/lib/index-build-github.ts +5 -0
- package/corpus/templates/analytics/server/lib/provider-credentials.ts +47 -2
- package/corpus/templates/analytics/server/lib/query-cost-estimate.ts +184 -5
- package/corpus/templates/analytics/server/plugins/db.ts +9 -1
- package/corpus/templates/analytics/shared/builtin-dashboard-ids.ts +2 -0
- package/corpus/templates/analytics/shared/search-console-property.ts +78 -0
- package/corpus/templates/assets/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/brain/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/calendar/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/calendar/actions/delete-event.ts +1 -3
- package/corpus/templates/calendar/actions/delete-events.ts +1 -2
- package/corpus/templates/calendar/actions/event-action-helpers.ts +0 -4
- package/corpus/templates/calendar/actions/update-event.ts +2 -6
- package/corpus/templates/chat/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/clips/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/clips/CHANGELOG.md +11 -24
- package/corpus/templates/clips/actions/create-recording-upload.ts +5 -1
- package/corpus/templates/clips/app/components/player/chapter-list.tsx +7 -6
- package/corpus/templates/clips/app/components/player/comment-composer.tsx +5 -1
- package/corpus/templates/clips/app/components/player/player-controls.tsx +1 -0
- package/corpus/templates/clips/app/components/player/scrubber.tsx +4 -2
- package/corpus/templates/clips/app/i18n/en-US.ts +3 -3
- package/corpus/templates/clips/app/lib/extension-auth.ts +70 -0
- package/corpus/templates/clips/app/root.tsx +22 -10
- package/corpus/templates/clips/app/routes/_app.notifications.tsx +3 -0
- package/corpus/templates/clips/app/routes/import.tsx +115 -110
- package/corpus/templates/clips/changelog/2026-10-16-agent-uploads-can-use-free-clips-storage.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-19-agents-see-a-clip-s-frames-as-soon-as-it-finishes-processing.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-19-chapter-list-times-now-match-the-player-s-clock-and-each-cha.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-19-editing-a-comment-puts-the-cursor-after-its-text-and-reply-o.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-19-youtube-import-followups.md +6 -0
- package/corpus/templates/clips/changelog/2026-10-20-locally-saved-desktop-recordings-now-appear-in-the-recovery-.md +6 -0
- package/corpus/templates/clips/desktop/src/app.tsx +29 -186
- package/corpus/templates/clips/desktop/src/components/RecordingRecovery.tsx +171 -16
- package/corpus/templates/clips/desktop/src/lib/record-first.ts +9 -32
- package/corpus/templates/clips/desktop/src/lib/recording-recovery.ts +30 -1
- package/corpus/templates/clips/server/lib/agent-frame-request.ts +5 -2
- package/corpus/templates/clips/server/lib/recording-upload-ticket.ts +8 -0
- package/corpus/templates/clips/server/routes/api/uploads/[recordingId]/chunk.post.ts +2 -0
- package/corpus/templates/content/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/content/CHANGELOG.md +12 -0
- package/corpus/templates/content/actions/_content-database-personal-view.ts +5 -15
- package/corpus/templates/content/actions/_database-navigation.ts +3 -3
- package/corpus/templates/content/actions/_database-utils.ts +10 -22
- package/corpus/templates/content/actions/_database-view-setup.ts +16 -76
- package/corpus/templates/content/actions/create-comment-ai-suggestion.ts +93 -44
- package/corpus/templates/content/actions/export-document.ts +2 -14
- package/corpus/templates/content/actions/suggest-document-edit.ts +5 -7
- package/corpus/templates/content/actions/update-content-database-view.ts +2 -14
- package/corpus/templates/content/app/components/editor/CommentsSidebar.tsx +86 -30
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +64 -28
- package/corpus/templates/content/app/components/editor/DocumentProperties.tsx +6 -46
- package/corpus/templates/content/app/components/editor/PageDraftRecovery.tsx +72 -7
- package/corpus/templates/content/app/components/editor/TableHoverControls.tsx +292 -103
- package/corpus/templates/content/app/components/editor/ThreadSuggestion.tsx +147 -40
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +108 -11
- package/corpus/templates/content/app/components/editor/database/ContentTable.tsx +2 -1
- package/corpus/templates/content/app/components/editor/database/DatabaseView.tsx +150 -166
- package/corpus/templates/content/app/components/editor/database/database-cell-comments.tsx +515 -0
- package/corpus/templates/content/app/components/editor/database/database-item-comments.tsx +5 -43
- package/corpus/templates/content/app/components/editor/database/filter-sort.ts +8 -783
- package/corpus/templates/content/app/components/editor/live-editor-session.ts +5 -1
- package/corpus/templates/content/app/components/editor/page-draft-journal.ts +61 -1
- package/corpus/templates/content/app/components/editor/property-comments.tsx +35 -4
- package/corpus/templates/content/app/components/editor/property-date-format.ts +45 -0
- package/corpus/templates/content/app/components/editor/thread-suggestions.ts +30 -0
- package/corpus/templates/content/app/components/editor/useDocumentReconcileRecovery.ts +23 -4
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +36 -10
- package/corpus/templates/content/app/global.css +23 -0
- package/corpus/templates/content/app/i18n-data.ts +1 -0
- package/corpus/templates/content/changelog/2026-10-16-opening-a-page-in-a-new-tab-while-another-tab-is-still-savin.md +6 -0
- package/corpus/templates/content/changelog/2026-10-16-typing-steadily-in-one-tab-no-longer-makes-another-open-tab-.md +6 -0
- package/corpus/templates/content/changelog/2026-10-19-database-views-can-filter-the-same-property-more-than-once-n.md +6 -0
- package/corpus/templates/content/changelog/2026-10-19-edits-made-from-a-block-s-menu-or-a-table-s-row-and-column-m.md +6 -0
- package/corpus/templates/content/changelog/2026-10-19-starting-a-comment-puts-the-cursor-in-the-comment-box-ready-.md +6 -0
- package/corpus/templates/content/changelog/2026-10-19-table-row-and-column-options-now-appear-for-the-row-or-colum.md +6 -0
- package/corpus/templates/content/docs/product/capabilities/content.comment.page-owned.md +5 -1
- package/corpus/templates/content/docs/product/capabilities/content.revision.suggestions.md +3 -1
- package/corpus/templates/content/e2e/helpers.ts +10 -0
- package/corpus/templates/content/shared/api.ts +18 -12
- package/corpus/templates/content/shared/database-query.ts +107 -13
- package/corpus/templates/content/shared/suggestion-diff.ts +35 -0
- package/corpus/templates/crm/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/design/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/design/CHANGELOG.md +2 -0
- package/corpus/templates/design/actions/delete-file.ts +1 -4
- package/corpus/templates/design/app/components/design/LayersPanel.tsx +10 -1
- package/corpus/templates/design/app/components/visual-editor/ReviewCanvasPins.tsx +24 -4
- package/corpus/templates/design/app/pages/design-editor/commands/mode-change.ts +14 -9
- package/corpus/templates/design/app/pages/design-editor/domains/use-editor-modes.ts +23 -2
- package/corpus/templates/design/app/pages/design-editor/tool-state.ts +29 -0
- package/corpus/templates/design/changelog/2026-10-19-reply-and-edit-on-a-comment-in-the-sidebar-or-on-a-canvas-pi.md +6 -0
- package/corpus/templates/design/changelog/2026-10-20-comments-return-to-the-mode-they-came-from-and-layers-search.md +6 -0
- package/corpus/templates/design/e2e/global-setup.ts +185 -85
- package/corpus/templates/dispatch/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/dispatch/CHANGELOG.md +4 -0
- package/corpus/templates/dispatch/actions/apply-workspace-connection-setup.ts +4 -0
- package/corpus/templates/dispatch/actions/delete-workspace-connection.ts +4 -0
- package/corpus/templates/dispatch/actions/set-workspace-connection-grant.ts +4 -0
- package/corpus/templates/dispatch/actions/upsert-workspace-connection.ts +4 -0
- package/corpus/templates/dispatch/changelog/2026-10-19-vault-secrets-grants-link-tokens-app-access-and-workspace-co.md +6 -0
- package/corpus/templates/factory/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/factory/.agents/skills/review-latest-feedback/SKILL.md +105 -33
- package/corpus/templates/factory/.agents/skills/review-latest-feedback/references/channel-inventory.md +128 -74
- package/corpus/templates/factory/.agents/skills/review-latest-feedback/references/slack-reaction-replies.md +39 -11
- package/corpus/templates/forms/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/mail/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/mail/.agents/skills/contacts-and-crm/SKILL.md +22 -17
- package/corpus/templates/mail/AGENTS.md +1 -1
- package/corpus/templates/mail/CHANGELOG.md +14 -0
- package/corpus/templates/mail/actions/provider-api-request.ts +3 -3
- package/corpus/templates/mail/app/components/email/IntegrationsSidebar.tsx +211 -54
- package/corpus/templates/mail/app/hooks/use-inbox-threads.ts +26 -2
- package/corpus/templates/mail/app/i18n/en-US.ts +5 -1
- package/corpus/templates/mail/app/lib/gong-connection.ts +27 -0
- package/corpus/templates/mail/app/lib/thread-cache.ts +110 -9
- package/corpus/templates/mail/app/root.tsx +10 -2
- package/corpus/templates/mail/changelog/2026-10-20-connected-gong-settings-now-open-the-shared-integration-mana.md +6 -0
- package/corpus/templates/mail/changelog/2026-10-20-gong-connection-details-now-match-the-call-data-available-in.md +6 -0
- package/corpus/templates/mail/changelog/2026-10-20-improve-gong-setup-with-a-click-to-connect-flow-and-permissi.md +6 -0
- package/corpus/templates/mail/changelog/2026-10-20-mail-lets-you-retry-when-it-cannot-check-your-gong-access.md +6 -0
- package/corpus/templates/mail/changelog/2026-10-20-mail-thread-cache-stays-isolated-when-mailbox-scope-changes.md +6 -0
- package/corpus/templates/mail/changelog/2026-10-20-the-inbox-and-recently-opened-threads-now-open-instantly-fro.md +6 -0
- package/corpus/templates/mail/server/handlers/emails.ts +9 -0
- package/corpus/templates/plan/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/plan/CHANGELOG.md +4 -0
- package/corpus/templates/plan/app/pages/PlansPage.tsx +173 -33
- package/corpus/templates/plan/changelog/2026-10-19-opening-a-comment-thread-puts-the-cursor-in-the-reply-box-an.md +6 -0
- package/corpus/templates/slides/.agents/skills/actions/references/action-fields.md +5 -3
- package/corpus/templates/slides/app/components/editor/EditorToolbar.tsx +6 -42
- package/corpus/templates/slides/app/components/editor/FloatingToolbar.tsx +3 -7
- package/corpus/templates/slides/app/components/editor/SlideEditor.tsx +5 -14
- package/corpus/templates/slides/app/i18n/en-US.ts +0 -2
- package/corpus/templates/slides/app/pages/DeckEditor.tsx +27 -33
- package/corpus/templates/slides/scripts/edit-fidelity/run.ts +1 -3
- package/corpus/templates/tasks/.agents/skills/actions/references/action-fields.md +5 -3
- package/package.json +1 -1
|
@@ -28,6 +28,46 @@ const STATE_PATH = path.join(AUTH_DIR, "state.json");
|
|
|
28
28
|
const SEED_PATH = path.join(AUTH_DIR, "seed.json");
|
|
29
29
|
const BROWSER_CHANNEL = process.env.E2E_BROWSER_CHANNEL;
|
|
30
30
|
const LOOPBACK_INSTANCE_HEADER = "x-agent-native-loopback-instance";
|
|
31
|
+
const GLOBAL_SETUP_BUDGET_MS = 11 * 60_000;
|
|
32
|
+
const SEED_BUDGET_MS = 2 * 60_000;
|
|
33
|
+
const HOME_WARMUP_BUDGET_MS = 6 * 60_000;
|
|
34
|
+
const HOME_RECOVERY_DETECTION_BUDGET_MS = 30_000;
|
|
35
|
+
const HOME_RECOVERY_RETRY_BUDGET_MS = 2 * 60_000;
|
|
36
|
+
const HOME_INITIAL_NAVIGATION_BUDGET_MS =
|
|
37
|
+
HOME_WARMUP_BUDGET_MS -
|
|
38
|
+
HOME_RECOVERY_DETECTION_BUDGET_MS -
|
|
39
|
+
HOME_RECOVERY_RETRY_BUDGET_MS;
|
|
40
|
+
const EDITOR_WARMUP_BUDGET_MS = 3 * 60_000;
|
|
41
|
+
|
|
42
|
+
async function withDeadline<T>(
|
|
43
|
+
label: string,
|
|
44
|
+
deadline: number,
|
|
45
|
+
action: () => Promise<T>,
|
|
46
|
+
): Promise<T> {
|
|
47
|
+
const remainingMs = deadline - Date.now();
|
|
48
|
+
if (remainingMs <= 0) throw new Error(`${label} budget expired`);
|
|
49
|
+
|
|
50
|
+
let timeout: ReturnType<typeof setTimeout> | undefined;
|
|
51
|
+
try {
|
|
52
|
+
return await Promise.race([
|
|
53
|
+
action(),
|
|
54
|
+
new Promise<never>((_, reject) => {
|
|
55
|
+
timeout = setTimeout(
|
|
56
|
+
() => reject(new Error(`${label} budget expired`)),
|
|
57
|
+
remainingMs,
|
|
58
|
+
);
|
|
59
|
+
}),
|
|
60
|
+
]);
|
|
61
|
+
} finally {
|
|
62
|
+
if (timeout) clearTimeout(timeout);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function timeoutBefore(deadline: number, requestedMs: number): number {
|
|
67
|
+
const remainingMs = deadline - Date.now();
|
|
68
|
+
if (remainingMs <= 0) throw new Error("the warmup budget expired");
|
|
69
|
+
return Math.min(requestedMs, remainingMs);
|
|
70
|
+
}
|
|
31
71
|
|
|
32
72
|
export async function startLoopbackProvider(port: number): Promise<void> {
|
|
33
73
|
const runRoot = designE2eRunRoot(path.resolve(import.meta.dirname, ".."));
|
|
@@ -331,6 +371,7 @@ async function seedMentionMember(
|
|
|
331
371
|
}
|
|
332
372
|
|
|
333
373
|
export default async function globalSetup(config: FullConfig) {
|
|
374
|
+
const globalSetupDeadline = Date.now() + GLOBAL_SETUP_BUDGET_MS;
|
|
334
375
|
if (process.env.E2E_AI_SIDEBAR_LOOPBACK === "1") {
|
|
335
376
|
await startLoopbackProvider(config.metadata.sidebarLoopbackPort as number);
|
|
336
377
|
await startHttpsAttachmentStorage(
|
|
@@ -348,79 +389,165 @@ export default async function globalSetup(config: FullConfig) {
|
|
|
348
389
|
const context = await browser.newContext();
|
|
349
390
|
|
|
350
391
|
try {
|
|
351
|
-
const
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
data: {
|
|
355
|
-
email: E2E_EMAIL,
|
|
356
|
-
password: E2E_PASSWORD,
|
|
357
|
-
},
|
|
358
|
-
headers: { "Content-Type": "application/json" },
|
|
359
|
-
},
|
|
392
|
+
const seedDeadline = Math.min(
|
|
393
|
+
Date.now() + SEED_BUDGET_MS,
|
|
394
|
+
globalSetupDeadline - HOME_WARMUP_BUDGET_MS - EDITOR_WARMUP_BUDGET_MS,
|
|
360
395
|
);
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
)
|
|
365
|
-
|
|
396
|
+
const designId = await withDeadline(
|
|
397
|
+
"Design E2E seeding",
|
|
398
|
+
seedDeadline,
|
|
399
|
+
async () => {
|
|
400
|
+
const registration = await context.request.post(
|
|
401
|
+
`${baseURL}/_agent-native/auth/register`,
|
|
402
|
+
{
|
|
403
|
+
data: {
|
|
404
|
+
email: E2E_EMAIL,
|
|
405
|
+
password: E2E_PASSWORD,
|
|
406
|
+
},
|
|
407
|
+
headers: { "Content-Type": "application/json" },
|
|
408
|
+
},
|
|
409
|
+
);
|
|
410
|
+
if (!registration.ok() && registration.status() !== 409) {
|
|
411
|
+
throw new Error(
|
|
412
|
+
`registration failed: ${registration.status()} ${await registration.text()}`,
|
|
413
|
+
);
|
|
414
|
+
}
|
|
366
415
|
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
416
|
+
const login = await context.request.post(
|
|
417
|
+
`${baseURL}/_agent-native/auth/login`,
|
|
418
|
+
{
|
|
419
|
+
data: {
|
|
420
|
+
email: E2E_EMAIL,
|
|
421
|
+
password: E2E_PASSWORD,
|
|
422
|
+
},
|
|
423
|
+
headers: { "Content-Type": "application/json" },
|
|
424
|
+
},
|
|
425
|
+
);
|
|
426
|
+
if (!login.ok()) {
|
|
427
|
+
throw new Error(
|
|
428
|
+
`login failed: ${login.status()} ${await login.text()}`,
|
|
429
|
+
);
|
|
430
|
+
}
|
|
380
431
|
|
|
381
|
-
|
|
432
|
+
await context.storageState({ path: STATE_PATH });
|
|
382
433
|
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
434
|
+
const created = await postAction(
|
|
435
|
+
context.request,
|
|
436
|
+
baseURL,
|
|
437
|
+
"create-design",
|
|
438
|
+
{
|
|
439
|
+
title: SEED_TITLE,
|
|
440
|
+
projectType: "prototype",
|
|
441
|
+
},
|
|
442
|
+
);
|
|
443
|
+
const createdDesignId: string =
|
|
444
|
+
created?.id ?? created?.data?.id ?? created?.design?.id;
|
|
445
|
+
if (!createdDesignId) {
|
|
446
|
+
throw new Error(
|
|
447
|
+
`create-design did not return an id: ${JSON.stringify(created)}`,
|
|
448
|
+
);
|
|
449
|
+
}
|
|
450
|
+
await seedMentionMember(browser, context, baseURL);
|
|
451
|
+
await postAction(context.request, baseURL, "create-file", {
|
|
452
|
+
designId: createdDesignId,
|
|
453
|
+
filename: "index.html",
|
|
454
|
+
content: FIXTURE_HTML,
|
|
455
|
+
fileType: "html",
|
|
456
|
+
});
|
|
457
|
+
await postAction(context.request, baseURL, "index-components", {
|
|
458
|
+
designId: createdDesignId,
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
await writeFile(
|
|
462
|
+
SEED_PATH,
|
|
463
|
+
JSON.stringify({ designId: createdDesignId }, null, 2),
|
|
464
|
+
);
|
|
465
|
+
// eslint-disable-next-line no-console
|
|
466
|
+
console.log(`[e2e] seeded design ${createdDesignId} for ${E2E_EMAIL}`);
|
|
467
|
+
return createdDesignId;
|
|
390
468
|
},
|
|
391
469
|
);
|
|
392
|
-
const designId: string =
|
|
393
|
-
created?.id ?? created?.data?.id ?? created?.design?.id;
|
|
394
|
-
if (!designId) {
|
|
395
|
-
throw new Error(
|
|
396
|
-
`create-design did not return an id: ${JSON.stringify(created)}`,
|
|
397
|
-
);
|
|
398
|
-
}
|
|
399
|
-
await seedMentionMember(browser, context, baseURL);
|
|
400
|
-
await postAction(context.request, baseURL, "create-file", {
|
|
401
|
-
designId,
|
|
402
|
-
filename: "index.html",
|
|
403
|
-
content: FIXTURE_HTML,
|
|
404
|
-
fileType: "html",
|
|
405
|
-
});
|
|
406
|
-
await postAction(context.request, baseURL, "index-components", {
|
|
407
|
-
designId,
|
|
408
|
-
});
|
|
409
|
-
|
|
410
|
-
await writeFile(SEED_PATH, JSON.stringify({ designId }, null, 2));
|
|
411
|
-
// eslint-disable-next-line no-console
|
|
412
|
-
console.log(`[e2e] seeded design ${designId} for ${E2E_EMAIL}`);
|
|
413
470
|
|
|
471
|
+
const homeDeadline = Math.min(
|
|
472
|
+
Date.now() + HOME_WARMUP_BUDGET_MS,
|
|
473
|
+
globalSetupDeadline - EDITOR_WARMUP_BUDGET_MS,
|
|
474
|
+
);
|
|
475
|
+
const homeRecoveryDeadline = homeDeadline - HOME_RECOVERY_RETRY_BUDGET_MS;
|
|
476
|
+
const homeInitialNavigationDeadline =
|
|
477
|
+
homeRecoveryDeadline - HOME_RECOVERY_DETECTION_BUDGET_MS;
|
|
414
478
|
const warmupPage = await context.newPage();
|
|
415
479
|
try {
|
|
480
|
+
const homeURL = `${baseURL}/home`;
|
|
481
|
+
const homeComposer = warmupPage.locator("[data-design-home-composer]");
|
|
482
|
+
let homeComposerWarmed = false;
|
|
483
|
+
try {
|
|
484
|
+
await warmupPage.goto(homeURL, {
|
|
485
|
+
waitUntil: "domcontentloaded",
|
|
486
|
+
timeout: timeoutBefore(
|
|
487
|
+
homeInitialNavigationDeadline,
|
|
488
|
+
HOME_INITIAL_NAVIGATION_BUDGET_MS,
|
|
489
|
+
),
|
|
490
|
+
});
|
|
491
|
+
const homeRecoveryDetectionDeadline = Math.min(
|
|
492
|
+
homeRecoveryDeadline,
|
|
493
|
+
Date.now() + HOME_RECOVERY_DETECTION_BUDGET_MS,
|
|
494
|
+
);
|
|
495
|
+
await homeComposer.waitFor({
|
|
496
|
+
state: "visible",
|
|
497
|
+
timeout: timeoutBefore(
|
|
498
|
+
homeRecoveryDetectionDeadline,
|
|
499
|
+
HOME_RECOVERY_DETECTION_BUDGET_MS,
|
|
500
|
+
),
|
|
501
|
+
});
|
|
502
|
+
homeComposerWarmed = true;
|
|
503
|
+
} catch (error) {
|
|
504
|
+
const hasViteRecoveryMarker = new URL(
|
|
505
|
+
warmupPage.url(),
|
|
506
|
+
).searchParams.has("__an_vite_dev_recovery");
|
|
507
|
+
if (hasViteRecoveryMarker) {
|
|
508
|
+
try {
|
|
509
|
+
await warmupPage.goto(homeURL, {
|
|
510
|
+
waitUntil: "domcontentloaded",
|
|
511
|
+
timeout: timeoutBefore(homeDeadline, HOME_WARMUP_BUDGET_MS),
|
|
512
|
+
});
|
|
513
|
+
await homeComposer.waitFor({
|
|
514
|
+
state: "visible",
|
|
515
|
+
timeout: timeoutBefore(homeDeadline, HOME_WARMUP_BUDGET_MS),
|
|
516
|
+
});
|
|
517
|
+
homeComposerWarmed = true;
|
|
518
|
+
} catch (recoveryError) {
|
|
519
|
+
// eslint-disable-next-line no-console
|
|
520
|
+
console.warn(
|
|
521
|
+
`[e2e] home warmup did not finish after Vite recovery (${(recoveryError as Error).message.split("\n")[0]}); ` +
|
|
522
|
+
"the first test touching /home will pay the compile.",
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
} else {
|
|
526
|
+
// Home tests remain responsible for reporting any warmup failure.
|
|
527
|
+
// eslint-disable-next-line no-console
|
|
528
|
+
console.warn(
|
|
529
|
+
`[e2e] home warmup did not finish (${(error as Error).message.split("\n")[0]}); ` +
|
|
530
|
+
"the first test touching /home will pay the compile.",
|
|
531
|
+
);
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
if (homeComposerWarmed) {
|
|
535
|
+
// eslint-disable-next-line no-console
|
|
536
|
+
console.log("[e2e] home composer warm");
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
const editorDeadline = Math.min(
|
|
540
|
+
Date.now() + EDITOR_WARMUP_BUDGET_MS,
|
|
541
|
+
globalSetupDeadline,
|
|
542
|
+
);
|
|
416
543
|
try {
|
|
417
544
|
await warmupPage.goto(`${baseURL}/design/${designId}`, {
|
|
418
545
|
waitUntil: "domcontentloaded",
|
|
419
|
-
timeout: 180_000,
|
|
546
|
+
timeout: timeoutBefore(editorDeadline, 180_000),
|
|
420
547
|
});
|
|
421
548
|
await warmupPage
|
|
422
549
|
.getByRole("button", { name: "Move", exact: true })
|
|
423
|
-
.waitFor({ timeout: 180_000 });
|
|
550
|
+
.waitFor({ timeout: timeoutBefore(editorDeadline, 180_000) });
|
|
424
551
|
// eslint-disable-next-line no-console
|
|
425
552
|
console.log("[e2e] editor warm");
|
|
426
553
|
} catch (error) {
|
|
@@ -432,33 +559,6 @@ export default async function globalSetup(config: FullConfig) {
|
|
|
432
559
|
"the first test will pay the compile.",
|
|
433
560
|
);
|
|
434
561
|
}
|
|
435
|
-
|
|
436
|
-
if (process.env.E2E_AI_SIDEBAR_LOOPBACK === "1") {
|
|
437
|
-
const homeURL = `${baseURL}/home`;
|
|
438
|
-
const homeComposer = warmupPage.locator("[data-design-home-composer]");
|
|
439
|
-
await warmupPage.goto(homeURL, {
|
|
440
|
-
waitUntil: "domcontentloaded",
|
|
441
|
-
timeout: 180_000,
|
|
442
|
-
});
|
|
443
|
-
try {
|
|
444
|
-
await homeComposer.waitFor({ state: "visible", timeout: 30_000 });
|
|
445
|
-
} catch (error) {
|
|
446
|
-
if (
|
|
447
|
-
!new URL(warmupPage.url()).searchParams.has(
|
|
448
|
-
"__an_vite_dev_recovery",
|
|
449
|
-
)
|
|
450
|
-
) {
|
|
451
|
-
throw error;
|
|
452
|
-
}
|
|
453
|
-
await warmupPage.goto(homeURL, {
|
|
454
|
-
waitUntil: "domcontentloaded",
|
|
455
|
-
timeout: 180_000,
|
|
456
|
-
});
|
|
457
|
-
await homeComposer.waitFor({ state: "visible", timeout: 30_000 });
|
|
458
|
-
}
|
|
459
|
-
// eslint-disable-next-line no-console
|
|
460
|
-
console.log("[e2e] home composer warm");
|
|
461
|
-
}
|
|
462
562
|
} finally {
|
|
463
563
|
await warmupPage.close();
|
|
464
564
|
}
|
|
@@ -95,9 +95,9 @@ MCP clients see no `outputSchema` by default. Set `mcpOutputSchema: true` (requi
|
|
|
95
95
|
|
|
96
96
|
`authorize` decides whether the caller may run the action at all. It wraps
|
|
97
97
|
`run`, so it holds at **all six dispatch sites** — agent tool, HTTP, frontend
|
|
98
|
-
hook, MCP, A2A, CLI
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
hook, MCP, A2A, CLI. Use `authorize` for "not everyone may do this"; use
|
|
99
|
+
`needsApproval` to ask a human to bless one call a permitted caller is already
|
|
100
|
+
allowed to make.
|
|
101
101
|
|
|
102
102
|
```ts
|
|
103
103
|
import { coachAccess } from "../lib/access.js"; // defineAppRoles(...)
|
|
@@ -140,4 +140,6 @@ export default defineAction({
|
|
|
140
140
|
|
|
141
141
|
When the gate is truthy and the call isn't yet approved, the loop emits an `approval_required` event and **stops the turn — `run()` never executes**. A predicate gates conditionally (e.g. only external recipients) and **fails closed**: a throw is treated as "approval required". It receives its own copy of the prepared snapshot of the validated arguments, defaults applied, and the approval binds to the snapshot's hash, so an approved call runs only with the arguments that were approved (a Date binds by its time). The human approves via the chat UI's Approve affordance, which re-issues the turn with the call's `approvalKey`, and only then does the action run.
|
|
142
142
|
|
|
143
|
+
Approval is given in the app, so HTTP never grants it. The HTTP action route and the generated Workers entry run a gated call only for the proven app UI (a frontend call from the signed-in user, same origin, holding the short-lived UI capability; `isProvenUiRequest`, the proof `uiOnly` uses) or the app's MCP directory widget. Every other HTTP caller (bearer or API key, MCP OAuth token, org service token, A2A, extension iframe, mobile app) gets `409 approval_required`, and a service identity can never approve. The shared client presents the capability with every write from a signed-in session (minted once per user, reused for nine minutes, never for reads or public pages), so a gated button works on its first request. A call without proof gets `403 ui_capability_required`; the client then re-mints and retries once, which is only the fallback for an expired or rejected capability. Every surface evaluates the gate through `preparedCallNeedsApproval`, so a throwing predicate asks there too. A caller that must run unattended needs a different action, never a bypass of the gate.
|
|
144
|
+
|
|
143
145
|
**Keep approvals rare** — the default is off and almost every action should leave it off. The canonical example is Mail's `send-email` (`needsApproval: true`). See the `security` skill and the Human Approval doc.
|
|
@@ -9,6 +9,10 @@ time from the command menu (Cmd+K → "What's new").
|
|
|
9
9
|
|
|
10
10
|
- Apps no longer keep server functions and databases warm by default; set `AGENT_NATIVE_ENABLE_KEEP_WARM=1` to opt in.
|
|
11
11
|
|
|
12
|
+
### Security
|
|
13
|
+
|
|
14
|
+
- Vault secrets, grants, link tokens, app access, and workspace connections now ask you to approve each change the agent or another app makes.
|
|
15
|
+
|
|
12
16
|
## 2026-10-14
|
|
13
17
|
|
|
14
18
|
### Fixed
|
|
@@ -118,6 +118,10 @@ export default defineAction({
|
|
|
118
118
|
.optional()
|
|
119
119
|
.describe("Workspace user group IDs allowed to use the connection."),
|
|
120
120
|
}),
|
|
121
|
+
// It creates, changes, or revokes a credential or who may use one: every
|
|
122
|
+
// call needs the user's approval, and "always allow" is not offered.
|
|
123
|
+
needsApproval: true,
|
|
124
|
+
allowPersistentApproval: false,
|
|
121
125
|
run: async (args, ctx) => {
|
|
122
126
|
const provider = getWorkspaceConnectionProvider(args.provider);
|
|
123
127
|
if (!provider) {
|
|
@@ -27,6 +27,10 @@ export default defineAction({
|
|
|
27
27
|
schema: z.object({
|
|
28
28
|
id: z.string().describe("Workspace connection ID to delete."),
|
|
29
29
|
}),
|
|
30
|
+
// It creates, changes, or revokes a credential or who may use one: every
|
|
31
|
+
// call needs the user's approval, and "always allow" is not offered.
|
|
32
|
+
needsApproval: true,
|
|
33
|
+
allowPersistentApproval: false,
|
|
30
34
|
run: async ({ id }, ctx) => {
|
|
31
35
|
const connection = await getWorkspaceConnection(id);
|
|
32
36
|
if (!connection) {
|
|
@@ -73,6 +73,10 @@ export default defineAction({
|
|
|
73
73
|
"Known workspace app IDs. Used when converting an all-app connection into selected-app grants.",
|
|
74
74
|
),
|
|
75
75
|
}),
|
|
76
|
+
// It creates, changes, or revokes a credential or who may use one: every
|
|
77
|
+
// call needs the user's approval, and "always allow" is not offered.
|
|
78
|
+
needsApproval: true,
|
|
79
|
+
allowPersistentApproval: false,
|
|
76
80
|
run: async (args, ctx) => {
|
|
77
81
|
const connection = await getWorkspaceConnection(args.connectionId);
|
|
78
82
|
if (!connection) {
|
|
@@ -209,6 +209,10 @@ export default defineAction({
|
|
|
209
209
|
),
|
|
210
210
|
lastError: z.string().nullable().optional(),
|
|
211
211
|
}),
|
|
212
|
+
// It creates, changes, or revokes a credential or who may use one: every
|
|
213
|
+
// call needs the user's approval, and "always allow" is not offered.
|
|
214
|
+
needsApproval: true,
|
|
215
|
+
allowPersistentApproval: false,
|
|
212
216
|
run: async (args, ctx) => {
|
|
213
217
|
await assertWorkspaceConnectionManager(ctx, args.allowedApps);
|
|
214
218
|
const provider = getWorkspaceConnectionProvider(args.provider);
|
|
@@ -95,9 +95,9 @@ MCP clients see no `outputSchema` by default. Set `mcpOutputSchema: true` (requi
|
|
|
95
95
|
|
|
96
96
|
`authorize` decides whether the caller may run the action at all. It wraps
|
|
97
97
|
`run`, so it holds at **all six dispatch sites** — agent tool, HTTP, frontend
|
|
98
|
-
hook, MCP, A2A, CLI
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
hook, MCP, A2A, CLI. Use `authorize` for "not everyone may do this"; use
|
|
99
|
+
`needsApproval` to ask a human to bless one call a permitted caller is already
|
|
100
|
+
allowed to make.
|
|
101
101
|
|
|
102
102
|
```ts
|
|
103
103
|
import { coachAccess } from "../lib/access.js"; // defineAppRoles(...)
|
|
@@ -140,4 +140,6 @@ export default defineAction({
|
|
|
140
140
|
|
|
141
141
|
When the gate is truthy and the call isn't yet approved, the loop emits an `approval_required` event and **stops the turn — `run()` never executes**. A predicate gates conditionally (e.g. only external recipients) and **fails closed**: a throw is treated as "approval required". It receives its own copy of the prepared snapshot of the validated arguments, defaults applied, and the approval binds to the snapshot's hash, so an approved call runs only with the arguments that were approved (a Date binds by its time). The human approves via the chat UI's Approve affordance, which re-issues the turn with the call's `approvalKey`, and only then does the action run.
|
|
142
142
|
|
|
143
|
+
Approval is given in the app, so HTTP never grants it. The HTTP action route and the generated Workers entry run a gated call only for the proven app UI (a frontend call from the signed-in user, same origin, holding the short-lived UI capability; `isProvenUiRequest`, the proof `uiOnly` uses) or the app's MCP directory widget. Every other HTTP caller (bearer or API key, MCP OAuth token, org service token, A2A, extension iframe, mobile app) gets `409 approval_required`, and a service identity can never approve. The shared client presents the capability with every write from a signed-in session (minted once per user, reused for nine minutes, never for reads or public pages), so a gated button works on its first request. A call without proof gets `403 ui_capability_required`; the client then re-mints and retries once, which is only the fallback for an expired or rejected capability. Every surface evaluates the gate through `preparedCallNeedsApproval`, so a throwing predicate asks there too. A caller that must run unattended needs a different action, never a bypass of the gate.
|
|
144
|
+
|
|
143
145
|
**Keep approvals rare** — the default is off and almost every action should leave it off. The canonical example is Mail's `send-email` (`needsApproval: true`). See the `security` skill and the Human Approval doc.
|
|
@@ -21,7 +21,25 @@ Four phases, in order. Phase 0 comes before any investigation, not after.
|
|
|
21
21
|
2. **Fix** what the evidence actually proves, at the owning boundary.
|
|
22
22
|
3. **Reply** with useful questions, then recap.
|
|
23
23
|
|
|
24
|
-
Output is fixes. For every report root this task owns, keep one concise, current same-actor status reply after the latest human message. It names every verified `✅` scope and its evidence, every open scope, and each `🎫` action and owner (or why a mark does not apply). Add and read back root `✅` as soon as direct post-change proof verifies a scope, and root `🎫` as soon as another person or team must act. An `👀` alone never completes the status. Preserve all reaction history, and follow [`slack-reaction-replies.md`](references/slack-reaction-replies.md). Do not reply on person-assigned or `:mag:` items.
|
|
24
|
+
Output is fixes. For every unheld in-scope report root this task owns, keep one concise, current same-actor status reply after the latest human message. It names every verified `✅` scope and its evidence, every open scope, and each `🎫` action and owner (or why a mark does not apply). Add and read back root `✅` as soon as direct post-change proof verifies a scope, and root `🎫` as soon as another person or team must act. An `👀` alone never completes the status. Preserve all reaction history, and follow [`slack-reaction-replies.md`](references/slack-reaction-replies.md). Do not reply on held, person-assigned, or `:mag:` items.
|
|
25
|
+
|
|
26
|
+
### Read the root marks as history; use the reply for current status
|
|
27
|
+
|
|
28
|
+
- `👀` records this workflow's claim. It does not mean the report is still open.
|
|
29
|
+
- `✅` records a scope that passed direct post-change proof. Add it as soon as
|
|
30
|
+
any scope passes, even when another scope remains open; name the verified
|
|
31
|
+
scope and evidence in the status reply.
|
|
32
|
+
- `🎫` records a distinct action another person must take, including a reporter
|
|
33
|
+
request. Keep the reaction as history after that action is completed,
|
|
34
|
+
answered, or abandoned; the current reply must say whether the action is
|
|
35
|
+
still open or historical.
|
|
36
|
+
- Every owned root needs one current same-actor reply after the latest human
|
|
37
|
+
reply. Reuse it while the disposition and evidence remain current; do not
|
|
38
|
+
send a duplicate progress reply on every sweep. The reply names verified
|
|
39
|
+
scopes, open scopes, human actions and owners, and why either `✅` or `🎫` is
|
|
40
|
+
absent. An eye-only root is correct when no scope is verified and no separate
|
|
41
|
+
human action is pending; say whether the next step is this workflow's
|
|
42
|
+
investigation or no further action.
|
|
25
43
|
|
|
26
44
|
## Slack channels and ownership
|
|
27
45
|
|
|
@@ -34,44 +52,93 @@ channel. For a permalink, its channel ID identifies only where the exact linked
|
|
|
34
52
|
root must appear; it does not limit the cutoff or channel scope. For an
|
|
35
53
|
explicitly narrowed sweep, verify that the active request names every selected
|
|
36
54
|
channel, then record its exact text and IDs in the separate scope-confirmation
|
|
37
|
-
file.
|
|
55
|
+
file. A request can select exactly two channels by linking one report in the
|
|
56
|
+
first and directly asking for “the same in” the second, without saying “only.”
|
|
57
|
+
The checker permits one adjacent-transposition typo in that target only when it
|
|
58
|
+
uniquely matches a selected canonical channel; it rejects incidental or quoted
|
|
59
|
+
mentions and prefixes. See [`channel-inventory.md`](references/channel-inventory.md)
|
|
60
|
+
for the scope gate and its confirmation requirements. `#dev-agent-native-feedback`
|
|
61
|
+
is not excluded.
|
|
38
62
|
|
|
39
63
|
In every scoped channel, run the complete bot-inclusive `slack_search_public`
|
|
40
64
|
scan with only the channel filter and that channel's inclusive lower bound.
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
65
|
+
Freeze one `windowEndTs` at sweep start and use it for every channel and
|
|
66
|
+
owned-mark search. Messages newer than it belong to the next scheduled sweep;
|
|
67
|
+
never move the boundary to chase arrivals. Exhaust every cursor;
|
|
68
|
+
`slack_read_channel` cannot replace this inventory. Resolve
|
|
69
|
+
each row to its true parent and read the root reactions before opening the thread.
|
|
70
|
+
If another actor owns any workflow mark (`👀`, `✅`, or `🎫`), or this actor owns
|
|
71
|
+
`:mag:`, apply the held-thread boundary below: do not read the thread, react, or
|
|
72
|
+
reply. Otherwise read the report's full thread and attachments before
|
|
73
|
+
classifying or claiming it. Follow
|
|
44
74
|
[`channel-inventory.md`](references/channel-inventory.md) for exact query
|
|
45
|
-
arguments, raw-page receipts, cap recovery, root classification,
|
|
46
|
-
searches for this actor's `👀`, `✅`, and `🎫`,
|
|
75
|
+
arguments, raw-page receipts, cap recovery, root classification, fixed-window
|
|
76
|
+
source scans, bounded ownership searches for this actor's `👀`, `✅`, and `🎫`,
|
|
77
|
+
and checker requirements. Owned-mark searches use Slack's exact syntax
|
|
78
|
+
`hasmy::eyes:`, `hasmy::white_check_mark:`, or `hasmy::ticket:` with the channel
|
|
79
|
+
filter and the same inclusive `after` and `before` as that channel's source
|
|
80
|
+
scan. Do not widen a finite catch-up request into an all-history audit. A root
|
|
81
|
+
older than `after` is in scope only when an in-window reply links to it. A
|
|
82
|
+
missing trailing colon is not a valid ownership query.
|
|
47
83
|
|
|
48
84
|
Only the invoking Slack actor's root marks are owned here. Another task's eye
|
|
49
85
|
plus its current open status remains theirs; an explicit handoff naming this
|
|
50
86
|
task and scope is required to transfer investigation. An eye is a permanent
|
|
51
87
|
claim, not proof of open or completed work. A mark on a reply never claims or
|
|
52
|
-
checks off its parent. Before exit, reconcile every
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
88
|
+
checks off its parent. Before exit, reconcile every in-window root carrying
|
|
89
|
+
this actor's `👀`, `✅`, or `🎫` found by the bounded searches. Also reconcile an
|
|
90
|
+
older root when an in-window reply links to it. For every owned root, compare
|
|
91
|
+
the latest human reply, current status, verified scopes, open scopes, human
|
|
92
|
+
actions, and exact root reactions.
|
|
93
|
+
Never let a high eye count hide a verified scope without `✅` or a missing
|
|
94
|
+
current status reply.
|
|
95
|
+
|
|
96
|
+
**Held-thread boundary:** A direct root reaction read showing another person's
|
|
97
|
+
`👀`, `✅`, or `🎫` means the thread belongs to that workflow; this actor's
|
|
98
|
+
`:mag:` means this actor opted out. Record the exact root, reaction owners, and
|
|
99
|
+
one reason (`foreign_workflow_mark` or `mag_opt_out`) in `ownershipSkip`, with
|
|
100
|
+
`Skipped` disposition. When this actor owns both `:mag:` and a workflow mark,
|
|
101
|
+
use `mag_opt_out` and preserve the exact root reaction-owner record as history.
|
|
102
|
+
Do not open/read that thread, inspect reply reactions, add or remove reactions,
|
|
103
|
+
or post a reply. Apply the same skip to channel reply
|
|
104
|
+
rows linked to that root. A linked pre-window root without source text remains
|
|
105
|
+
`unclassified`; do not infer its original report from the reply. This explicit
|
|
106
|
+
exception keeps ownership boundaries intact while allowing the rest of the
|
|
107
|
+
window to be reconciled. A foreign `👀` without current status is still held;
|
|
108
|
+
do not read it to decide whether its owner is active.
|
|
57
109
|
|
|
58
110
|
Only Steve's `:mag:` and `:yes-party:` count (`U0GCV21HC`). When Steve invokes,
|
|
59
|
-
exhaust `hasmy::mag:` and `hasmy::yes-party:` searches
|
|
60
|
-
|
|
61
|
-
|
|
111
|
+
exhaust `hasmy::mag:` and `hasmy::yes-party:` searches within each channel's
|
|
112
|
+
same inclusive cutoff; otherwise inspect root reactions in the requested
|
|
113
|
+
window. `:mag:` opts out and wins over `:yes-party:`. The latter authorizes only
|
|
114
|
+
an otherwise review-only item; it never overrides an assignment or another
|
|
62
115
|
task's claim.
|
|
63
116
|
|
|
64
117
|
## Phase 0: claim and reconcile
|
|
65
118
|
|
|
66
119
|
Resolve known duplicates before claiming. Check ownership, then add and read
|
|
67
120
|
back this task's root `👀` before investigating an eligible unowned report.
|
|
68
|
-
Reconcile
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
121
|
+
Reconcile only owned roots inside this invocation's fixed window and older
|
|
122
|
+
roots explicitly linked by an in-window reply. Never expand a finite sweep to
|
|
123
|
+
all historical roots just because this actor owns an old mark; widen the range
|
|
124
|
+
only when the invocation explicitly requests it or hands off exact roots. Finish
|
|
125
|
+
this bounded reconciliation before discovering new questions. Keep one
|
|
126
|
+
concise canonical status by the same actor after the latest human message; read
|
|
127
|
+
back the visible reply and root reactions after each write. Use
|
|
72
128
|
[`slack-reaction-replies.md`](references/slack-reaction-replies.md) for exact
|
|
73
129
|
status and reaction rules.
|
|
74
130
|
|
|
131
|
+
**Bounded exit gate:** enumerate the complete candidate set from the fixed
|
|
132
|
+
source window, the three matching owned-mark searches, and older roots linked
|
|
133
|
+
by in-window replies. For every unheld owned root, compare the latest human
|
|
134
|
+
message, current status reply, verified scopes, open scopes, and root marks.
|
|
135
|
+
Add and read back `✅` for each newly verified scope and `🎫` for each distinct
|
|
136
|
+
pending human action; the status reply must follow the latest human message and
|
|
137
|
+
name current evidence, remaining work, and owners. An `👀` with a verified
|
|
138
|
+
scope missing `✅`, a pending human action missing `🎫`, or no current status is
|
|
139
|
+
unfinished. Finish the complete bounded set once, then stop; do not chase
|
|
140
|
+
unrelated historical marks.
|
|
141
|
+
|
|
75
142
|
Split reports into distinct scopes. Add root `✅` as soon as any named scope
|
|
76
143
|
passes direct post-change proof, even while its PR is open; say **Verified
|
|
77
144
|
locally** and that merge is pending. Use **Fixed** only after merge and direct
|
|
@@ -97,11 +164,14 @@ action and owner. Use **Skipped** only for non-defects.
|
|
|
97
164
|
|
|
98
165
|
Phase 0 handles new human replies on owned roots first; never hide a newer
|
|
99
166
|
reporter reply behind a mark. Then find prior unanswered questions using the
|
|
100
|
-
exact bot disclosure and the saved channel reply cursor
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
167
|
+
exact bot disclosure and the saved channel reply cursor, bounded by this
|
|
168
|
+
invocation's fixed window. On a first run, use the same default five-day window;
|
|
169
|
+
never scan all available history to initialize the cursor. Exhaust pages, use a
|
|
170
|
+
one-day overlap for date-only cursors, resolve every hit to its parent, and read
|
|
171
|
+
the full thread after checking reactions. An older root is eligible only when
|
|
172
|
+
an in-window reply links to it. Follow
|
|
173
|
+
[`slack-reaction-replies.md`](references/slack-reaction-replies.md) for exact
|
|
174
|
+
searches, legacy identity handling, and answer criteria.
|
|
105
175
|
|
|
106
176
|
A valid answer is a human post after the question, without this workflow's
|
|
107
177
|
disclosure, that addresses the question. Rebuild the evidence and attempt the
|
|
@@ -316,15 +386,17 @@ send one concise disposition covering done, remaining, and next action/owner.
|
|
|
316
386
|
Do not reply again to announce merge, publication, or live beta; answer direct
|
|
317
387
|
reporter follow-ups. End Slack replies with `this was sent from a bot.`.
|
|
318
388
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
actor
|
|
323
|
-
reply
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
389
|
+
Reporter-facing replies are one or two sentences: the change or question, its limit, and the next step. Do not explain workflow mechanics, ledgers, or external tracker tickets. Never send **Open**/**pending** or a symptom recap. Ask for reporter input only when it changes the next step; never request supplied evidence or internal access. Keep hashes, branches, CI, publisher, and run details in the recap.
|
|
390
|
+
|
|
391
|
+
Every root this actor marks with `👀`, `✅`, or `🎫` also needs the current
|
|
392
|
+
same-actor status reply required by the Slack reaction reference. One concise
|
|
393
|
+
reply may serve both purposes when it answers the reporter and records the
|
|
394
|
+
current disposition, verified/open scopes, owner/action, and any reason a mark
|
|
395
|
+
is absent. This status requirement takes precedence over the reporter-facing
|
|
396
|
+
brevity rule only for those facts; do not explain ledger mechanics or external
|
|
397
|
+
tracker tickets. A Slack `🎫` reaction tracks a distinct human action, not an
|
|
398
|
+
external ticket, and remains as history after the action closes. Never imply
|
|
399
|
+
that an external ticket was created or assigned.
|
|
328
400
|
|
|
329
401
|
Keep publisher and beta checks in the internal recap after the disposition;
|
|
330
402
|
they do not trigger a second reporter reply.
|