@agent-native/core 0.164.12 → 0.164.17
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/netlify.toml +4 -0
- package/corpus/templates/clips/actions/request-transcript.ts +10 -6
- package/corpus/templates/content/actions/_database-source-utils.ts +25 -2
- package/corpus/templates/content/actions/_database-utils.ts +2 -0
- package/corpus/templates/content/actions/_local-folder-source.ts +186 -2
- package/corpus/templates/content/actions/connect-local-folder-source.ts +23 -8
- package/corpus/templates/content/actions/disconnect-local-folder-source.ts +3 -3
- package/corpus/templates/content/actions/resolve-local-folder-conflict.ts +2 -2
- package/corpus/templates/content/actions/sync-local-folder-source.ts +130 -12
- package/corpus/templates/content/app/components/LocalFolderLiveSync.tsx +77 -0
- package/corpus/templates/content/app/components/editor/BubbleToolbar.tsx +298 -3
- package/corpus/templates/content/app/components/editor/DocumentEditor.tsx +354 -8
- package/corpus/templates/content/app/components/editor/DocumentToolbar.tsx +23 -1
- package/corpus/templates/content/app/components/editor/VisualEditor.tsx +81 -1
- package/corpus/templates/content/app/components/editor/database/sidebar.tsx +36 -0
- package/corpus/templates/content/app/components/editor/extensions/NotionExtensions.tsx +18 -4
- package/corpus/templates/content/app/components/editor/local-source-write-state.ts +52 -0
- package/corpus/templates/content/app/components/sidebar/DocumentSidebar.tsx +244 -6
- package/corpus/templates/content/app/components/sidebar/local-source-hierarchy.ts +189 -0
- package/corpus/templates/content/app/hooks/use-create-page.ts +26 -4
- package/corpus/templates/content/app/hooks/use-documents.ts +83 -0
- package/corpus/templates/content/app/i18n-data.ts +239 -0
- package/corpus/templates/content/app/lib/desktop-content-files.ts +57 -1
- package/corpus/templates/content/app/lib/local-content-source-files.ts +210 -34
- package/corpus/templates/content/app/lib/local-folder-live-sync.ts +191 -0
- package/corpus/templates/content/app/root.tsx +2 -1
- package/corpus/templates/content/app/routes/_app.local-files.tsx +103 -38
- package/corpus/templates/content/docs/product/capabilities/content.source.file-folder.md +2 -2
- package/corpus/templates/content/docs/product/features/content.feature.bring-your-local-work.md +3 -3
- package/corpus/templates/content/docs/product/roadmap.md +2 -2
- package/corpus/templates/content/docs/solutions/2026-08-18-colored-text-highlighting-shape.md +693 -0
- package/corpus/templates/content/docs/solutions/2026-08-18-content-navigation-coherence-shape.md +482 -0
- package/corpus/templates/content/package.json +1 -0
- package/corpus/templates/content/shared/api.ts +25 -0
- package/corpus/templates/content/shared/nfm.ts +14 -3
- package/corpus/templates/factory/AGENTS.md +24 -29
- package/corpus/templates/factory/actions/get-slack-feedback-context.ts +8 -28
- package/corpus/templates/factory/actions/get-triage-config.ts +1 -0
- package/corpus/templates/factory/actions/govern-agent-native-pull-request.ts +67 -59
- package/corpus/templates/factory/actions/list-triage-items.ts +6 -1
- package/corpus/templates/factory/actions/reconcile-triage-run.ts +7 -1
- package/corpus/templates/factory/actions/save-triage-config.ts +27 -7
- package/corpus/templates/factory/actions/start-builder-for-item.ts +113 -31
- package/corpus/templates/factory/actions/view-screen.ts +12 -0
- package/corpus/templates/factory/app/components/factory/FactorySettingsView.tsx +457 -0
- package/corpus/templates/factory/app/components/factory/FactoryWorkspaceActions.tsx +53 -0
- package/corpus/templates/factory/app/components/layout/Layout.tsx +2 -0
- package/corpus/templates/factory/app/components/layout/Sidebar.tsx +3 -1
- package/corpus/templates/factory/app/hooks/use-navigation-state.ts +10 -7
- package/corpus/templates/factory/app/i18n/en-US.ts +3 -0
- package/corpus/templates/factory/app/routes/agents.tsx +22 -1
- package/corpus/templates/factory/app/routes/factory-settings.tsx +34 -0
- package/corpus/templates/factory/app/routes/factory.tsx +33 -323
- package/corpus/templates/factory/server/connectors/slack.ts +62 -0
- package/corpus/templates/factory/server/db/schema.ts +1 -0
- package/corpus/templates/factory/server/lib/slack-feedback-prompt.ts +30 -0
- package/corpus/templates/factory/server/plugins/agent-chat.ts +9 -8
- package/corpus/templates/factory/server/plugins/factory-migrations.ts +7 -0
- package/corpus/templates/factory/server/plugins/factory-scheduler-job.ts +43 -25
- package/corpus/templates/factory/server/triage/ai-services-git.ts +2 -0
- package/corpus/templates/factory/server/triage/pr-monitor.ts +2 -1
- package/corpus/templates/factory/server/triage/pr-policy.ts +197 -19
- package/corpus/templates/factory/server/triage/review-skill-alignment.ts +127 -0
- package/corpus/templates/factory/server/triage/slack-client.ts +79 -6
- package/dist/app-config/auth.d.ts +4 -0
- package/dist/app-config/auth.js +7 -0
- package/dist/app-config/schema.d.ts +3 -0
- package/dist/app-config/schema.js +2 -0
- package/dist/cli/code-agent-executor.js +6 -1
- package/dist/cli/code-agent-mcp-config.d.ts +2 -2
- package/dist/cli/code-agent-mcp-config.js +34 -9
- package/dist/client/MultiTabAssistantChat.js +28 -1
- package/dist/client/org/OrgSwitcher.js +8 -4
- package/dist/client/use-chat-threads.d.ts +1 -0
- package/dist/client/use-chat-threads.js +13 -3
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/collab/routes.d.ts +1 -1
- package/dist/mcp-client/config.d.ts +11 -0
- package/dist/mcp-client/config.js +33 -26
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +5 -5
- package/dist/progress/routes.d.ts +1 -1
- package/dist/resources/handlers.d.ts +2 -2
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/auth.js +17 -3
- package/dist/server/realtime-token.d.ts +1 -1
- package/dist/server/transcribe-voice.d.ts +1 -1
- package/dist/vite/client.js +2 -0
- package/package.json +1 -1
|
@@ -16,6 +16,15 @@ import { and, eq, isNull, lt, ne, or } from "drizzle-orm";
|
|
|
16
16
|
|
|
17
17
|
import { getDb } from "../db/index.js";
|
|
18
18
|
import { triageConfig } from "../db/schema.js";
|
|
19
|
+
import {
|
|
20
|
+
repairSlackFeedbackPrompt,
|
|
21
|
+
SLACK_HANDOFF_INSTRUCTION,
|
|
22
|
+
SLACK_MENTION_GUARD,
|
|
23
|
+
} from "../lib/slack-feedback-prompt.js";
|
|
24
|
+
import {
|
|
25
|
+
syncManagedReviewSkillAlignment,
|
|
26
|
+
type FactoryAutomationName,
|
|
27
|
+
} from "../triage/review-skill-alignment.js";
|
|
19
28
|
|
|
20
29
|
const LEGACY_JOB_PATH = "jobs/factory-observation-scheduler.md";
|
|
21
30
|
const DEFAULT_SLACK_CHANNEL_ID = "C0ATH3CCZT4";
|
|
@@ -165,8 +174,7 @@ type AutomationSeed = {
|
|
|
165
174
|
body: string;
|
|
166
175
|
};
|
|
167
176
|
|
|
168
|
-
const FACTORY_DEFAULT_MODEL =
|
|
169
|
-
process.env.FACTORY_AUTOMATION_MODEL?.trim() || "gpt-5.6-luna";
|
|
177
|
+
const FACTORY_DEFAULT_MODEL = "gpt-5.6-luna";
|
|
170
178
|
const FACTORY_DEFAULT_MAX_ITERATIONS = 32;
|
|
171
179
|
const FACTORY_DEFAULT_MAX_RUN_INPUT_TOKENS = 1_000_000;
|
|
172
180
|
const SKIP_RECORD_GUARD =
|
|
@@ -220,13 +228,9 @@ The action adds 👀 to every grouped Slack thread but posts one Builder reply i
|
|
|
220
228
|
the representative thread. Do not start one Builder thread per duplicate.
|
|
221
229
|
Separate reports only when their failure modes, surfaces, or owners differ.
|
|
222
230
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
underlying boundary across the whole cluster. Never add the reaction or tag
|
|
227
|
-
Builder for owner-managed Clips, Design, or Content work, or for a non-bug
|
|
228
|
-
report.
|
|
229
|
-
After classifying each processed item, call start-builder-for-item with clearBug true or false and a short evidence-grounded reason so the skip or dispatch is recorded.
|
|
231
|
+
${SLACK_HANDOFF_INSTRUCTION}
|
|
232
|
+
${SKIP_RECORD_GUARD}
|
|
233
|
+
${SLACK_MENTION_GUARD}
|
|
230
234
|
|
|
231
235
|
Keep each run bounded. Preserve action errors and do not claim a Builder reply,
|
|
232
236
|
PR, merge, or fix unless an action returned that state.
|
|
@@ -322,21 +326,23 @@ clear bug fix or has product or UX implications. Avoid duplicate review noise
|
|
|
322
326
|
when no commit, review, comment, or check result changed. Call
|
|
323
327
|
govern-agent-native-pull-request with the item id, repository, pull request
|
|
324
328
|
number, clearBug, productUxImplications, and a short reason. The action fetches
|
|
325
|
-
fresh CI and review evidence before approving
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
329
|
+
fresh CI and review evidence before approving. For a verified current BuilderIO
|
|
330
|
+
member, the internal-author exception means ordinary failed, pending, skipped,
|
|
331
|
+
or unknown checks and unresolved ordinary feedback do not by themselves block
|
|
332
|
+
approval; record their exact states and never call them clean. Apply the
|
|
333
|
+
verified Alice/Content, Nick/Slides, Enzo/Factory-specific, Sid/Design, and
|
|
334
|
+
docs-only owner exceptions from review-prs only after membership and an
|
|
335
|
+
explicit ultra-scary assessment. Those exceptions do not waive membership,
|
|
336
|
+
external-author, or ultra-scary gates.
|
|
337
|
+
|
|
338
|
+
Never auto-merge. Approval is the only GitHub write this workflow may request;
|
|
339
|
+
a normal open PR must never be treated as a Builder-triggered run.
|
|
340
|
+
|
|
341
|
+
Never auto-dispatch Clips, Design, or Content feedback. Those apps remain
|
|
342
|
+
fully owned by their product owners for feedback work, while the verified
|
|
343
|
+
PR-owner exceptions still apply to their own scoped PRs. Do not call GitHub
|
|
344
|
+
write actions directly or claim an approval unless the governance action
|
|
345
|
+
confirms it.
|
|
340
346
|
`,
|
|
341
347
|
},
|
|
342
348
|
{
|
|
@@ -439,6 +445,10 @@ function automationContent(
|
|
|
439
445
|
orgId: string,
|
|
440
446
|
seed: AutomationSeed,
|
|
441
447
|
): string {
|
|
448
|
+
const body = syncManagedReviewSkillAlignment(
|
|
449
|
+
seed.body.trim(),
|
|
450
|
+
seed.name as FactoryAutomationName,
|
|
451
|
+
);
|
|
442
452
|
return `---
|
|
443
453
|
schedule: "${seed.schedule}"
|
|
444
454
|
${seed.timezone ? `timezone: ${seed.timezone}\n` : ""}enabled: true
|
|
@@ -452,7 +462,7 @@ model: ${seed.model}
|
|
|
452
462
|
maxIterations: ${seed.maxIterations}
|
|
453
463
|
maxRunInputTokens: ${seed.maxRunInputTokens}
|
|
454
464
|
---
|
|
455
|
-
${
|
|
465
|
+
${body.trim()}
|
|
456
466
|
`;
|
|
457
467
|
}
|
|
458
468
|
|
|
@@ -535,6 +545,13 @@ async function ensureOrganizationAutomations(
|
|
|
535
545
|
) {
|
|
536
546
|
repaired = `${repaired.trimEnd()}\n\n${SKIP_RECORD_GUARD}\n`;
|
|
537
547
|
}
|
|
548
|
+
repaired = syncManagedReviewSkillAlignment(
|
|
549
|
+
repaired,
|
|
550
|
+
seed.name as FactoryAutomationName,
|
|
551
|
+
);
|
|
552
|
+
if (seed.name === "factory-slack-feedback") {
|
|
553
|
+
repaired = repairSlackFeedbackPrompt(repaired);
|
|
554
|
+
}
|
|
538
555
|
if (repaired === existing.content) return;
|
|
539
556
|
|
|
540
557
|
const updated = await resourcePutIfCurrent({
|
|
@@ -579,6 +596,7 @@ async function ensureDefaultTriageConfig(
|
|
|
579
596
|
slackWorkspace: "primary",
|
|
580
597
|
slackChannelId: DEFAULT_SLACK_CHANNEL_ID,
|
|
581
598
|
slackChannelName: DEFAULT_SLACK_CHANNEL_NAME,
|
|
599
|
+
builderSlackUserId: null,
|
|
582
600
|
pollingEnabled: 1,
|
|
583
601
|
githubPollingEnabled: defaultGithubPollingEnabled(),
|
|
584
602
|
sentryPollingEnabled: 0,
|
|
@@ -2,6 +2,13 @@ import type { GuardResult } from "./contracts.js";
|
|
|
2
2
|
|
|
3
3
|
export type OwnerOwnedArea = "clips" | "design" | "content";
|
|
4
4
|
|
|
5
|
+
export type PullRequestOwnerException =
|
|
6
|
+
| "alice-content"
|
|
7
|
+
| "nick-slides"
|
|
8
|
+
| "enzo-factory"
|
|
9
|
+
| "sid-design"
|
|
10
|
+
| "docs-only";
|
|
11
|
+
|
|
5
12
|
export interface PullRequestGovernanceInput {
|
|
6
13
|
author: string;
|
|
7
14
|
repository: string;
|
|
@@ -19,6 +26,7 @@ export interface PullRequestGovernanceInput {
|
|
|
19
26
|
|
|
20
27
|
export interface PullRequestGovernanceDecision {
|
|
21
28
|
ownerOwnedArea: OwnerOwnedArea | null;
|
|
29
|
+
ownerException: PullRequestOwnerException | null;
|
|
22
30
|
autoApprove: boolean;
|
|
23
31
|
autoMerge: boolean;
|
|
24
32
|
reason: string;
|
|
@@ -69,6 +77,11 @@ export function decidePullRequestGovernance(
|
|
|
69
77
|
input.summary,
|
|
70
78
|
...input.changedFiles,
|
|
71
79
|
]);
|
|
80
|
+
const ultraScary = isUltraScaryChange(input.changedFiles);
|
|
81
|
+
const ownerException = detectPullRequestOwnerException(input);
|
|
82
|
+
const verifiedOwnerException =
|
|
83
|
+
input.internalBuilderMember && !ultraScary ? ownerException : null;
|
|
84
|
+
const internalEvidenceException = input.internalBuilderMember;
|
|
72
85
|
const gates: GuardResult[] = [
|
|
73
86
|
{
|
|
74
87
|
code: "identity",
|
|
@@ -79,17 +92,27 @@ export function decidePullRequestGovernance(
|
|
|
79
92
|
},
|
|
80
93
|
{
|
|
81
94
|
code: "unknown_change",
|
|
82
|
-
passed: input.clearBug,
|
|
83
|
-
reason:
|
|
84
|
-
|
|
85
|
-
|
|
95
|
+
passed: input.clearBug || verifiedOwnerException !== null,
|
|
96
|
+
reason:
|
|
97
|
+
input.clearBug || verifiedOwnerException !== null
|
|
98
|
+
? "The automation classified this as a clear bug with a concrete failure signal."
|
|
99
|
+
: "The automation did not establish a clear bug; product requests and guesses stay manual.",
|
|
86
100
|
},
|
|
87
101
|
{
|
|
88
102
|
code: "security",
|
|
89
|
-
passed:
|
|
103
|
+
passed: !ultraScary,
|
|
104
|
+
reason: ultraScary
|
|
105
|
+
? "Security-sensitive auth, tenant-isolation, execution, payment, or deployment changes always require manual review."
|
|
106
|
+
: "The changed paths do not match the ultra-scary manual-review categories.",
|
|
107
|
+
},
|
|
108
|
+
{
|
|
109
|
+
code: "security",
|
|
110
|
+
passed: internalEvidenceException || input.checksPassed,
|
|
90
111
|
reason: input.checksPassed
|
|
91
112
|
? "All observed CI checks passed."
|
|
92
|
-
:
|
|
113
|
+
: internalEvidenceException
|
|
114
|
+
? "CI is failing, cancelled, pending, or unavailable; the verified internal-author exception does not treat that state as clean."
|
|
115
|
+
: "CI is failing, cancelled, pending, or unavailable.",
|
|
93
116
|
},
|
|
94
117
|
{
|
|
95
118
|
code: "unknown_change",
|
|
@@ -100,21 +123,26 @@ export function decidePullRequestGovernance(
|
|
|
100
123
|
},
|
|
101
124
|
{
|
|
102
125
|
code: "unknown_change",
|
|
103
|
-
passed: input.reviewFeedbackHandled,
|
|
126
|
+
passed: internalEvidenceException || input.reviewFeedbackHandled,
|
|
104
127
|
reason: input.reviewFeedbackHandled
|
|
105
128
|
? "All observed review feedback is fixed, resolved, replied to, or outdated."
|
|
106
|
-
:
|
|
129
|
+
: internalEvidenceException
|
|
130
|
+
? "Review feedback is unanswered, unresolved, truncated, or otherwise unknown; the verified internal-author exception does not treat that state as clean."
|
|
131
|
+
: "Review feedback is unanswered, unresolved, truncated, or otherwise unknown.",
|
|
107
132
|
},
|
|
108
133
|
];
|
|
109
134
|
|
|
110
|
-
if (
|
|
135
|
+
if (
|
|
136
|
+
ownerOwnedArea &&
|
|
137
|
+
!ownerExceptionCoversArea(verifiedOwnerException, ownerOwnedArea)
|
|
138
|
+
) {
|
|
111
139
|
gates.push({
|
|
112
140
|
code: "owner_owned",
|
|
113
141
|
passed: false,
|
|
114
142
|
reason: `${ownerOwnedArea} is owner-managed and is never auto-approved, auto-merged, or dispatched by this Factory.`,
|
|
115
143
|
});
|
|
116
144
|
}
|
|
117
|
-
if (input.productUxImplications) {
|
|
145
|
+
if (input.productUxImplications && verifiedOwnerException === null) {
|
|
118
146
|
gates.push({
|
|
119
147
|
code: "unknown_change",
|
|
120
148
|
passed: false,
|
|
@@ -124,21 +152,171 @@ export function decidePullRequestGovernance(
|
|
|
124
152
|
|
|
125
153
|
const baseEligible = gates.every((gate) => gate.passed);
|
|
126
154
|
const autoApprove = baseEligible;
|
|
127
|
-
const autoMerge =
|
|
128
|
-
const reason =
|
|
129
|
-
?
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
155
|
+
const autoMerge = false;
|
|
156
|
+
const reason = autoApprove
|
|
157
|
+
? verifiedOwnerException
|
|
158
|
+
? `Verified ${verifiedOwnerException} owner exception; approval is safe to automate while ordinary check and review states remain recorded.`
|
|
159
|
+
: "Clear internal bug fix with verified membership; approval is safe to automate while ordinary check and review states remain recorded."
|
|
160
|
+
: gates
|
|
161
|
+
.filter((gate) => !gate.passed)
|
|
162
|
+
.map((gate) => gate.reason)
|
|
163
|
+
.join(" ");
|
|
136
164
|
|
|
137
165
|
return {
|
|
138
166
|
ownerOwnedArea,
|
|
167
|
+
ownerException: verifiedOwnerException,
|
|
139
168
|
autoApprove,
|
|
140
169
|
autoMerge,
|
|
141
170
|
reason,
|
|
142
171
|
guardResults: gates,
|
|
143
172
|
};
|
|
144
173
|
}
|
|
174
|
+
|
|
175
|
+
export function detectPullRequestOwnerException(
|
|
176
|
+
input: Pick<PullRequestGovernanceInput, "author" | "changedFiles">,
|
|
177
|
+
): PullRequestOwnerException | null {
|
|
178
|
+
const author = input.author.trim().toLowerCase();
|
|
179
|
+
const changedFiles = input.changedFiles.map(normalizePath);
|
|
180
|
+
|
|
181
|
+
if (author === "3mdistal" && isAppScoped(changedFiles, "content", true)) {
|
|
182
|
+
return "alice-content";
|
|
183
|
+
}
|
|
184
|
+
if (author === "nkoech123" && isAppScoped(changedFiles, "slides", true)) {
|
|
185
|
+
return "nick-slides";
|
|
186
|
+
}
|
|
187
|
+
if (author === "enzoames" && isFactoryScoped(changedFiles)) {
|
|
188
|
+
return "enzo-factory";
|
|
189
|
+
}
|
|
190
|
+
if (author === "sidmohanty11" && isAppScoped(changedFiles, "design", true)) {
|
|
191
|
+
return "sid-design";
|
|
192
|
+
}
|
|
193
|
+
if (
|
|
194
|
+
(author === "kapunahelewong" || author === "bwreid") &&
|
|
195
|
+
isDocsOnly(changedFiles)
|
|
196
|
+
) {
|
|
197
|
+
return "docs-only";
|
|
198
|
+
}
|
|
199
|
+
return null;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
export function hasCurrentPullRequestApproval(
|
|
203
|
+
reviews: readonly {
|
|
204
|
+
author: string;
|
|
205
|
+
state: string;
|
|
206
|
+
observedAt: string;
|
|
207
|
+
}[],
|
|
208
|
+
): boolean {
|
|
209
|
+
const latestByAuthor = new Map<
|
|
210
|
+
string,
|
|
211
|
+
{ state: string; observedAt: string; order: number }
|
|
212
|
+
>();
|
|
213
|
+
reviews.forEach((review, order) => {
|
|
214
|
+
const author = review.author.trim().toLowerCase();
|
|
215
|
+
if (!author) return;
|
|
216
|
+
const previous = latestByAuthor.get(author);
|
|
217
|
+
if (
|
|
218
|
+
!previous ||
|
|
219
|
+
Date.parse(review.observedAt) > Date.parse(previous.observedAt) ||
|
|
220
|
+
(review.observedAt === previous.observedAt && order > previous.order)
|
|
221
|
+
) {
|
|
222
|
+
latestByAuthor.set(author, {
|
|
223
|
+
state: review.state,
|
|
224
|
+
observedAt: review.observedAt,
|
|
225
|
+
order,
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
return [...latestByAuthor.values()].some(
|
|
230
|
+
(review) => review.state === "approved",
|
|
231
|
+
);
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export function isDocsOnly(changedFiles: readonly string[]): boolean {
|
|
235
|
+
return (
|
|
236
|
+
changedFiles.length > 0 &&
|
|
237
|
+
changedFiles.every((file) => {
|
|
238
|
+
const normalized = normalizePath(file);
|
|
239
|
+
return (
|
|
240
|
+
normalized.startsWith("docs/") ||
|
|
241
|
+
normalized.startsWith("packages/docs/") ||
|
|
242
|
+
normalized.startsWith("packages/core/docs/") ||
|
|
243
|
+
normalized.startsWith(".agents/skills/") ||
|
|
244
|
+
normalized.endsWith("/agents.md") ||
|
|
245
|
+
normalized.endsWith("/claude.md") ||
|
|
246
|
+
normalized === "agents.md" ||
|
|
247
|
+
normalized === "claude.md" ||
|
|
248
|
+
(normalized.startsWith(".changeset/") && normalized.endsWith(".md")) ||
|
|
249
|
+
(normalized.endsWith(".mdx") &&
|
|
250
|
+
(normalized.startsWith("docs/") ||
|
|
251
|
+
normalized.startsWith(".agents/skills/") ||
|
|
252
|
+
normalized.includes("/docs/")))
|
|
253
|
+
);
|
|
254
|
+
})
|
|
255
|
+
);
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
export function isUltraScaryChange(changedFiles: readonly string[]): boolean {
|
|
259
|
+
return changedFiles.some((file) => {
|
|
260
|
+
const normalized = normalizePath(file);
|
|
261
|
+
return (
|
|
262
|
+
normalized.startsWith(".github/workflows/") ||
|
|
263
|
+
/(^|\/)(auth|authentication|identity|credentials?|secrets?|sessions?|permissions?|tenant|tenants|isolation|security|execution|sandbox|payments?|billing|deploy|deployment|netlify|publish|release|migrations?)(\/|[-_.]|$)/.test(
|
|
264
|
+
normalized,
|
|
265
|
+
)
|
|
266
|
+
);
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function ownerExceptionCoversArea(
|
|
271
|
+
exception: PullRequestOwnerException | null,
|
|
272
|
+
area: OwnerOwnedArea,
|
|
273
|
+
): boolean {
|
|
274
|
+
return (
|
|
275
|
+
(exception === "alice-content" && area === "content") ||
|
|
276
|
+
(exception === "sid-design" && area === "design")
|
|
277
|
+
);
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
function isAppScoped(
|
|
281
|
+
changedFiles: readonly string[],
|
|
282
|
+
app: "content" | "slides" | "design",
|
|
283
|
+
allowSharedSupport: boolean,
|
|
284
|
+
): boolean {
|
|
285
|
+
const appPrefix = `templates/${app}/`;
|
|
286
|
+
const hasAppPath = changedFiles.some((file) => file.startsWith(appPrefix));
|
|
287
|
+
if (!hasAppPath) return false;
|
|
288
|
+
return changedFiles.every(
|
|
289
|
+
(file) =>
|
|
290
|
+
file.startsWith(appPrefix) ||
|
|
291
|
+
(allowSharedSupport && isSharedSupportPath(file)) ||
|
|
292
|
+
isChangeset(file),
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function isFactoryScoped(changedFiles: readonly string[]): boolean {
|
|
297
|
+
const hasFactoryPath = changedFiles.some((file) =>
|
|
298
|
+
file.startsWith("templates/factory/"),
|
|
299
|
+
);
|
|
300
|
+
return (
|
|
301
|
+
hasFactoryPath &&
|
|
302
|
+
changedFiles.every(
|
|
303
|
+
(file) => file.startsWith("templates/factory/") || isChangeset(file),
|
|
304
|
+
)
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
function isSharedSupportPath(file: string): boolean {
|
|
309
|
+
return (
|
|
310
|
+
file.startsWith("packages/core/") ||
|
|
311
|
+
file.startsWith("packages/desktop-app/") ||
|
|
312
|
+
file.startsWith("packages/code-agents-ui/")
|
|
313
|
+
);
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
function isChangeset(file: string): boolean {
|
|
317
|
+
return file.startsWith(".changeset/") && file.endsWith(".md");
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
function normalizePath(file: string): string {
|
|
321
|
+
return file.trim().split("\\").join("/").toLowerCase();
|
|
322
|
+
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
const ALIGNMENT_START = "<!-- factory-skill-alignment:start -->";
|
|
2
|
+
const ALIGNMENT_END = "<!-- factory-skill-alignment:end -->";
|
|
3
|
+
|
|
4
|
+
const FEEDBACK_ALIGNMENT = `## Current review-latest-feedback contract
|
|
5
|
+
|
|
6
|
+
The repository's \`.agents/skills/review-latest-feedback/SKILL.md\` is
|
|
7
|
+
authoritative. Re-read it in the Builder worktree before acting. Its current
|
|
8
|
+
contract is evidence-first and reply-producing:
|
|
9
|
+
|
|
10
|
+
- Recheck answered clarifications and open In progress ownership before newer
|
|
11
|
+
work. Keep the newest unhandled item as the cursor, read older duplicates
|
|
12
|
+
into one verified cluster, and leave an auditable disposition for every
|
|
13
|
+
inspected Slack, GitHub issue, and Sentry item.
|
|
14
|
+
- Read the complete parent/thread, reactions, attachments, linked artifacts,
|
|
15
|
+
and newer follow-ups before asking anything or dispatching. An unavailable
|
|
16
|
+
source is unavailable evidence, never a no-results result.
|
|
17
|
+
- Keep one specific unanswered clarification per thread. Re-read the whole
|
|
18
|
+
thread when the requested detail is answered, try the fix first, and use the
|
|
19
|
+
task-scoped clarification ledger with a stable scheduler identity for
|
|
20
|
+
recurring rechecks. Do not claim scheduled coverage without stable durable
|
|
21
|
+
state.
|
|
22
|
+
- Check the existing Slack 👀 marker and owner before any write. Preserve an
|
|
23
|
+
existing marker; if reactions cannot be read, do not guess or add one. Do
|
|
24
|
+
not react to or dispatch Design UX/interaction work (Sid) or any Content
|
|
25
|
+
work (Alice); record the owner instead.
|
|
26
|
+
- For an actionable repo-owned Slack item with no existing marker, 👀 is the
|
|
27
|
+
first external write. Every parent this run marks must later receive a
|
|
28
|
+
verified @agent-native Fixed, In progress, or Clarification needed reply;
|
|
29
|
+
an eye, forward, generic acknowledgement, or another person's reply is not
|
|
30
|
+
a disposition. Group only genuinely repeated symptoms and dispatch one
|
|
31
|
+
Builder thread for the cluster.
|
|
32
|
+
- Choose the smallest owning seam: local regression for one symptom, shared
|
|
33
|
+
contract for repeated cross-surface evidence, discovery/action wiring for a
|
|
34
|
+
missing capability, and release/deployment diagnosis for source-versus-live
|
|
35
|
+
mismatches. Do not turn one report into a global prompt rule.
|
|
36
|
+
- Keep source-tested, built, published/deployed, and observed-live evidence
|
|
37
|
+
separate. Do not claim a fix, PR, reply, or deployment without confirmation
|
|
38
|
+
from the relevant action or runtime evidence.
|
|
39
|
+
|
|
40
|
+
After classifying every processed item, call \`start-builder-for-item\` with
|
|
41
|
+
\`clearBug: true\` or \`false\` and a concise evidence-grounded reason so every
|
|
42
|
+
skip or dispatch is recorded.`;
|
|
43
|
+
|
|
44
|
+
const PR_ALIGNMENT = `## Current review-prs contract
|
|
45
|
+
|
|
46
|
+
The repository's \`.agents/skills/review-prs/SKILL.md\` is authoritative.
|
|
47
|
+
Before selecting work, ignore drafts and pull requests with a current,
|
|
48
|
+
non-dismissed APPROVED review, including bot approvals; do not inspect or
|
|
49
|
+
recap those excluded items. For every remaining PR, read the complete title,
|
|
50
|
+
body, links, changed-file diff (including generated and migration files), all
|
|
51
|
+
review summaries/comments/replies, actual check conclusions, and ownership
|
|
52
|
+
boundary. Verify current BuilderIO organization membership through GitHub's
|
|
53
|
+
organization API - never infer it from a name, association, branch, email, or
|
|
54
|
+
bot label.
|
|
55
|
+
|
|
56
|
+
Never approve external or unverified authors. The internal-author exception
|
|
57
|
+
allows ordinary failed, pending, skipped, or unknown checks and ordinary
|
|
58
|
+
unresolved feedback for a verified BuilderIO member; record those exact states
|
|
59
|
+
and never call them clean. The ultra-scary gate always remains manual for auth,
|
|
60
|
+
permissions, tenant isolation, secrets, destructive data loss or migrations,
|
|
61
|
+
remote code execution, SSRF, payments, deployment, or unexplained dependency
|
|
62
|
+
and infrastructure risk.
|
|
63
|
+
|
|
64
|
+
The verified owner exceptions are current and must be applied only after
|
|
65
|
+
membership and the ultra-scary assessment: Alice (\`3mdistal\`) for Content,
|
|
66
|
+
Nick (\`NKoech123\`) for Slides, Enzo (\`enzoames\`) for Factory-specific PRs,
|
|
67
|
+
and Sid (\`sidmohanty11\`) for Design. Alice and Nick may include supporting
|
|
68
|
+
shared framework/Desktop plumbing required by their app feature. The docs-only
|
|
69
|
+
exception applies to \`kapunahelewong\` and Wes (\`bwreid\`) only when every
|
|
70
|
+
changed file is documentation, localization, docs navigation/redirect, or a
|
|
71
|
+
docs-specific test. These exceptions cover ordinary UX/refactor/check/review
|
|
72
|
+
gates but never membership, external-author, or ultra-scary gates.
|
|
73
|
+
|
|
74
|
+
Approve only when the remaining standard evidence supports a clear,
|
|
75
|
+
repo-owned, narrow root-cause change with unambiguous scope. Approval is a
|
|
76
|
+
trust decision only - never auto-merge. Never claim ignored checks or feedback
|
|
77
|
+
are resolved, and record one concise disposition for every PR that entered the
|
|
78
|
+
evidence sweep.`;
|
|
79
|
+
|
|
80
|
+
export type FactoryAutomationName =
|
|
81
|
+
| "factory-slack-feedback"
|
|
82
|
+
| "factory-sentry-errors"
|
|
83
|
+
| "factory-github-issues"
|
|
84
|
+
| "factory-pr-governance"
|
|
85
|
+
| "factory-pr-babysit";
|
|
86
|
+
|
|
87
|
+
export function managedReviewSkillAlignment(
|
|
88
|
+
name: FactoryAutomationName,
|
|
89
|
+
): string | undefined {
|
|
90
|
+
if (
|
|
91
|
+
name === "factory-slack-feedback" ||
|
|
92
|
+
name === "factory-sentry-errors" ||
|
|
93
|
+
name === "factory-github-issues"
|
|
94
|
+
) {
|
|
95
|
+
return FEEDBACK_ALIGNMENT;
|
|
96
|
+
}
|
|
97
|
+
if (name === "factory-pr-governance") return PR_ALIGNMENT;
|
|
98
|
+
return undefined;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
export function syncManagedReviewSkillAlignment(
|
|
102
|
+
content: string,
|
|
103
|
+
name: FactoryAutomationName,
|
|
104
|
+
): string {
|
|
105
|
+
const alignment = managedReviewSkillAlignment(name);
|
|
106
|
+
if (!alignment) return content;
|
|
107
|
+
|
|
108
|
+
const block = `${ALIGNMENT_START}\n${alignment.trim()}\n${ALIGNMENT_END}`;
|
|
109
|
+
const existingBlock = new RegExp(
|
|
110
|
+
`${escapeRegExp(ALIGNMENT_START)}[\\s\\S]*?${escapeRegExp(ALIGNMENT_END)}`,
|
|
111
|
+
);
|
|
112
|
+
if (existingBlock.test(content)) {
|
|
113
|
+
return content.replace(existingBlock, block);
|
|
114
|
+
}
|
|
115
|
+
return `${content.trimEnd()}\n\n${block}\n`;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function managedReviewSkillAlignmentMarkers(): {
|
|
119
|
+
start: string;
|
|
120
|
+
end: string;
|
|
121
|
+
} {
|
|
122
|
+
return { start: ALIGNMENT_START, end: ALIGNMENT_END };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function escapeRegExp(value: string): string {
|
|
126
|
+
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
127
|
+
}
|