@prenta/core 5.5.0 → 5.7.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/CHANGELOG.md +31 -0
- package/dist/ai/governance.d.ts +6 -0
- package/dist/ai/governance.d.ts.map +1 -1
- package/dist/ai/governance.js +19 -0
- package/dist/ai/governance.js.map +1 -1
- package/dist/api/handlers.d.ts.map +1 -1
- package/dist/api/handlers.js +2 -0
- package/dist/api/handlers.js.map +1 -1
- package/dist/api/routes/redirects.d.ts.map +1 -1
- package/dist/api/routes/redirects.js +89 -93
- package/dist/api/routes/redirects.js.map +1 -1
- package/dist/api/routes/seo-autopilot.d.ts +12 -0
- package/dist/api/routes/seo-autopilot.d.ts.map +1 -0
- package/dist/api/routes/seo-autopilot.js +257 -0
- package/dist/api/routes/seo-autopilot.js.map +1 -0
- package/dist/api/routes/seo.d.ts.map +1 -1
- package/dist/api/routes/seo.js +195 -360
- package/dist/api/routes/seo.js.map +1 -1
- package/dist/db/model-types.d.ts +26 -0
- package/dist/db/model-types.d.ts.map +1 -1
- package/dist/generated/browser.d.ts +8 -0
- package/dist/generated/client.d.ts +8 -0
- package/dist/generated/internal/class.d.ts +11 -0
- package/dist/generated/internal/class.js +4 -4
- package/dist/generated/internal/prismaNamespace.d.ts +120 -1
- package/dist/generated/internal/prismaNamespace.js +42 -0
- package/dist/generated/internal/prismaNamespaceBrowser.d.ts +44 -0
- package/dist/generated/internal/prismaNamespaceBrowser.js +42 -0
- package/dist/generated/models/SeoAutoApply.d.ts +1495 -0
- package/dist/generated/models/SeoAutoApply.js +1 -0
- package/dist/generated/models.d.ts +1 -0
- package/dist/redirects/recovery.d.ts +15 -9
- package/dist/redirects/recovery.d.ts.map +1 -1
- package/dist/redirects/recovery.js +15 -61
- package/dist/redirects/recovery.js.map +1 -1
- package/dist/redirects/suggestion-accept.d.ts +29 -0
- package/dist/redirects/suggestion-accept.d.ts.map +1 -0
- package/dist/redirects/suggestion-accept.js +108 -0
- package/dist/redirects/suggestion-accept.js.map +1 -0
- package/dist/seo/audit-runner.d.ts.map +1 -1
- package/dist/seo/audit-runner.js +0 -2
- package/dist/seo/audit-runner.js.map +1 -1
- package/dist/seo/autopilot-gate.d.ts +101 -0
- package/dist/seo/autopilot-gate.d.ts.map +1 -0
- package/dist/seo/autopilot-gate.js +272 -0
- package/dist/seo/autopilot-gate.js.map +1 -0
- package/dist/seo/autopilot-ledger.d.ts +74 -0
- package/dist/seo/autopilot-ledger.d.ts.map +1 -0
- package/dist/seo/autopilot-ledger.js +118 -0
- package/dist/seo/autopilot-ledger.js.map +1 -0
- package/dist/seo/autopilot-outcome.d.ts +93 -0
- package/dist/seo/autopilot-outcome.d.ts.map +1 -0
- package/dist/seo/autopilot-outcome.js +206 -0
- package/dist/seo/autopilot-outcome.js.map +1 -0
- package/dist/seo/autopilot-policy.d.ts +169 -0
- package/dist/seo/autopilot-policy.d.ts.map +1 -0
- package/dist/seo/autopilot-policy.js +267 -0
- package/dist/seo/autopilot-policy.js.map +1 -0
- package/dist/seo/autopilot-revert.d.ts +40 -0
- package/dist/seo/autopilot-revert.d.ts.map +1 -0
- package/dist/seo/autopilot-revert.js +155 -0
- package/dist/seo/autopilot-revert.js.map +1 -0
- package/dist/seo/autopilot.d.ts +91 -0
- package/dist/seo/autopilot.d.ts.map +1 -0
- package/dist/seo/autopilot.js +1060 -0
- package/dist/seo/autopilot.js.map +1 -0
- package/dist/seo/config-store.d.ts +35 -2
- package/dist/seo/config-store.d.ts.map +1 -1
- package/dist/seo/config-store.js +58 -1
- package/dist/seo/config-store.js.map +1 -1
- package/dist/seo/deep-equal.d.ts +12 -0
- package/dist/seo/deep-equal.d.ts.map +1 -0
- package/dist/seo/deep-equal.js +42 -0
- package/dist/seo/deep-equal.js.map +1 -0
- package/dist/seo/digest.d.ts +27 -0
- package/dist/seo/digest.d.ts.map +1 -1
- package/dist/seo/digest.js +77 -1
- package/dist/seo/digest.js.map +1 -1
- package/dist/seo/index.d.ts +20 -2
- package/dist/seo/index.d.ts.map +1 -1
- package/dist/seo/index.js +12 -1
- package/dist/seo/index.js.map +1 -1
- package/dist/seo/issue-fix-ai.d.ts +40 -1
- package/dist/seo/issue-fix-ai.d.ts.map +1 -1
- package/dist/seo/issue-fix-ai.js +53 -1
- package/dist/seo/issue-fix-ai.js.map +1 -1
- package/dist/seo/issue-fix-apply.d.ts +104 -0
- package/dist/seo/issue-fix-apply.d.ts.map +1 -0
- package/dist/seo/issue-fix-apply.js +406 -0
- package/dist/seo/issue-fix-apply.js.map +1 -0
- package/dist/seo/issue-fix.d.ts +9 -0
- package/dist/seo/issue-fix.d.ts.map +1 -1
- package/dist/seo/issue-fix.js +1 -0
- package/dist/seo/issue-fix.js.map +1 -1
- package/dist/seo/proposals-bulk-apply.d.ts +52 -0
- package/dist/seo/proposals-bulk-apply.d.ts.map +1 -0
- package/dist/seo/proposals-bulk-apply.js +183 -0
- package/dist/seo/proposals-bulk-apply.js.map +1 -0
- package/dist/seo/proposals-generate.d.ts +45 -0
- package/dist/seo/proposals-generate.d.ts.map +1 -0
- package/dist/seo/proposals-generate.js +144 -0
- package/dist/seo/proposals-generate.js.map +1 -0
- package/dist/seo/proposals.d.ts +125 -0
- package/dist/seo/proposals.d.ts.map +1 -0
- package/dist/seo/proposals.js +270 -0
- package/dist/seo/proposals.js.map +1 -0
- package/package.json +1 -1
- package/prisma/migrations/0017_seo_auto_applies/migration.sql +35 -0
- package/prisma/schema.prisma +43 -0
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
import { getFeatureGovernance } from '../ai/governance.js';
|
|
2
|
+
import { buildFixFingerprint } from './issue-fix.js';
|
|
3
|
+
import { pendingFixFromMetadata, stickyFingerprintsFromMetadata, } from './issue-fix-ai.js';
|
|
4
|
+
import { INSERT_LINK_FIX_TYPES, buildInsertLinkFingerprint } from './issue-fix-link-insert.js';
|
|
5
|
+
import { getSeoOverrides, resolveSeoModuleSettings } from './config-store.js';
|
|
6
|
+
export const ISSUE_FIX_FEATURE_KEY = 'ai.features.seoMetaGeneration';
|
|
7
|
+
export const REDIRECT_FEATURE_KEY = 'ai.features.seo404Recovery';
|
|
8
|
+
const AUTOPILOT_APPLIED_WINDOW_MS = 7 * 24 * 60 * 60 * 1000;
|
|
9
|
+
/**
|
|
10
|
+
* Count the autopilot changes that are live right now and landed in the last
|
|
11
|
+
* 7 days. Only `applied` counts — `verify_failed` rows were undone at apply
|
|
12
|
+
* time and reverted rows are no longer live. Degrades to 0 when the ledger
|
|
13
|
+
* model is absent or the count fails: this is a status strip, not a ledger.
|
|
14
|
+
*/
|
|
15
|
+
async function countAutopilotAppliedLast7d(db) {
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- optional-model guard
|
|
17
|
+
const d = db;
|
|
18
|
+
if (typeof d?.seoAutoApply?.count !== 'function')
|
|
19
|
+
return 0;
|
|
20
|
+
try {
|
|
21
|
+
const status = 'applied';
|
|
22
|
+
const n = await d.seoAutoApply.count({
|
|
23
|
+
where: { status, appliedAt: { gte: new Date(Date.now() - AUTOPILOT_APPLIED_WINDOW_MS) } },
|
|
24
|
+
});
|
|
25
|
+
return typeof n === 'number' && Number.isFinite(n) ? n : 0;
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
return 0;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
const SEVERITY_RANK = { critical: 0, warning: 1, info: 2 };
|
|
32
|
+
function severityOf(value) {
|
|
33
|
+
return value === 'critical' || value === 'warning' || value === 'info' ? value : null;
|
|
34
|
+
}
|
|
35
|
+
function toDate(value) {
|
|
36
|
+
return value instanceof Date ? value : new Date(String(value));
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Open/recurring issues (any fixability) plus the documents they reference, in
|
|
40
|
+
* two queries. "Reference" covers the issue entity AND, for cached insert-link
|
|
41
|
+
* fixes, the SOURCE document named by `pendingFix.patch.sourceEntityId` — that
|
|
42
|
+
* is the document the fix writes to and the one its fingerprint is built from,
|
|
43
|
+
* so without it `liveFingerprintFor` could never detect a stale link proposal.
|
|
44
|
+
*/
|
|
45
|
+
export async function loadOpenFixableIssuesWithDocs(db) {
|
|
46
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- optional-model guard
|
|
47
|
+
const d = db;
|
|
48
|
+
const issues = d?.seoIssue?.findMany
|
|
49
|
+
? await d.seoIssue.findMany({
|
|
50
|
+
where: { status: { in: ['open', 'recurring'] } },
|
|
51
|
+
orderBy: { lastDetectedAt: 'desc' },
|
|
52
|
+
take: 500,
|
|
53
|
+
})
|
|
54
|
+
: [];
|
|
55
|
+
const docIds = new Set();
|
|
56
|
+
for (const issue of issues) {
|
|
57
|
+
if (typeof issue.entityId === 'string' && issue.entityId.length > 0) {
|
|
58
|
+
docIds.add(issue.entityId);
|
|
59
|
+
}
|
|
60
|
+
const sourceId = pendingFixFromMetadata(issue.metadata)?.patch.sourceEntityId;
|
|
61
|
+
if (typeof sourceId === 'string' && sourceId.length > 0)
|
|
62
|
+
docIds.add(sourceId);
|
|
63
|
+
}
|
|
64
|
+
const docsById = new Map();
|
|
65
|
+
if (docIds.size > 0 && d?.document?.findMany) {
|
|
66
|
+
const docs = (await d.document.findMany({
|
|
67
|
+
where: { id: { in: [...docIds] }, deletedAt: null },
|
|
68
|
+
select: { id: true, updatedAt: true, title: true, slug: true, collection: true },
|
|
69
|
+
}));
|
|
70
|
+
for (const doc of docs) {
|
|
71
|
+
docsById.set(String(doc.id), {
|
|
72
|
+
id: String(doc.id),
|
|
73
|
+
updatedAt: toDate(doc.updatedAt),
|
|
74
|
+
title: typeof doc.title === 'string' ? doc.title : null,
|
|
75
|
+
slug: typeof doc.slug === 'string' ? doc.slug : null,
|
|
76
|
+
collection: String(doc.collection ?? ''),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return { issues, docsById };
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* The fingerprint a fresh suggestion for this issue would carry right now.
|
|
84
|
+
* Insert-link fixes are fingerprinted on the SOURCE document (the one that
|
|
85
|
+
* gets written), so their live value needs the cache to know the source.
|
|
86
|
+
* `loadOpenFixableIssuesWithDocs` loads sources alongside entities; if one is
|
|
87
|
+
* still missing (deleted since generation) we fall back to the cached value
|
|
88
|
+
* rather than guessing.
|
|
89
|
+
*/
|
|
90
|
+
export function liveFingerprintFor(issue, cache, docsById) {
|
|
91
|
+
const entityId = typeof issue.entityId === 'string' ? issue.entityId : null;
|
|
92
|
+
if (!entityId)
|
|
93
|
+
return null;
|
|
94
|
+
if (cache &&
|
|
95
|
+
(INSERT_LINK_FIX_TYPES.has(issue.type) || cache.patch.fixStrategy === 'insert-link')) {
|
|
96
|
+
const sourceId = typeof cache.patch.sourceEntityId === 'string' ? cache.patch.sourceEntityId : null;
|
|
97
|
+
const source = sourceId ? docsById.get(sourceId) : undefined;
|
|
98
|
+
if (!source)
|
|
99
|
+
return cache.fingerprint;
|
|
100
|
+
return buildInsertLinkFingerprint(sourceId, typeof cache.patch.targetEntityId === 'string' ? cache.patch.targetEntityId : entityId, typeof cache.patch.anchorText === 'string' ? cache.patch.anchorText : '', source.updatedAt);
|
|
101
|
+
}
|
|
102
|
+
const doc = docsById.get(entityId);
|
|
103
|
+
if (!doc)
|
|
104
|
+
return null;
|
|
105
|
+
return buildFixFingerprint(entityId, issue.type, doc.updatedAt);
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* The marker `dismiss-fix` stores. After a dismissal there is no cache, so
|
|
109
|
+
* `selectEligibleForGeneration` compares the marker against the ENTITY-based
|
|
110
|
+
* live fingerprint. Insert-link caches are fingerprinted on the source
|
|
111
|
+
* document instead, so for them we store the entity-based value the no-cache
|
|
112
|
+
* path will compute; for every other type the cached fingerprint already is
|
|
113
|
+
* that value.
|
|
114
|
+
*/
|
|
115
|
+
export function dismissalFingerprintFor(issue, cache, entityUpdatedAt) {
|
|
116
|
+
const isInsertLink = INSERT_LINK_FIX_TYPES.has(issue.type) || cache.patch.fixStrategy === 'insert-link';
|
|
117
|
+
if (!isInsertLink || !entityUpdatedAt || typeof issue.entityId !== 'string') {
|
|
118
|
+
return cache.fingerprint;
|
|
119
|
+
}
|
|
120
|
+
return buildFixFingerprint(issue.entityId, issue.type, entityUpdatedAt);
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Issues batch generation should attempt: fixable, open, document present,
|
|
124
|
+
* no LIVE cache (a stale one counts as none), and neither sticky marker
|
|
125
|
+
* equals the live fingerprint. Ordered critical → warning → info.
|
|
126
|
+
*/
|
|
127
|
+
export function selectEligibleForGeneration(issues, docsById) {
|
|
128
|
+
const out = [];
|
|
129
|
+
for (const issue of issues) {
|
|
130
|
+
if (issue.fixable !== true)
|
|
131
|
+
continue;
|
|
132
|
+
if (issue.status !== 'open' && issue.status !== 'recurring')
|
|
133
|
+
continue;
|
|
134
|
+
const cache = pendingFixFromMetadata(issue.metadata);
|
|
135
|
+
const live = liveFingerprintFor(issue, cache, docsById);
|
|
136
|
+
if (!live)
|
|
137
|
+
continue;
|
|
138
|
+
if (cache && cache.fingerprint === live)
|
|
139
|
+
continue;
|
|
140
|
+
const sticky = stickyFingerprintsFromMetadata(issue.metadata);
|
|
141
|
+
if (sticky.dismissed === live || sticky.generateSkipped === live)
|
|
142
|
+
continue;
|
|
143
|
+
out.push({ issue, liveFingerprint: live });
|
|
144
|
+
}
|
|
145
|
+
out.sort((a, b) => (SEVERITY_RANK[String(a.issue.severity)] ?? 99) -
|
|
146
|
+
(SEVERITY_RANK[String(b.issue.severity)] ?? 99));
|
|
147
|
+
return out;
|
|
148
|
+
}
|
|
149
|
+
function issueFixProposal(issue, cache, docsById) {
|
|
150
|
+
const live = liveFingerprintFor(issue, cache, docsById);
|
|
151
|
+
const doc = typeof issue.entityId === 'string' ? docsById.get(issue.entityId) : undefined;
|
|
152
|
+
return {
|
|
153
|
+
id: `issue:${issue.id}`,
|
|
154
|
+
kind: 'issue-fix',
|
|
155
|
+
title: String(issue.title ?? issue.type),
|
|
156
|
+
entity: {
|
|
157
|
+
type: typeof issue.entityType === 'string' ? issue.entityType : null,
|
|
158
|
+
id: typeof issue.entityId === 'string' ? issue.entityId : null,
|
|
159
|
+
title: (typeof issue.entityTitle === 'string' ? issue.entityTitle : null) ?? doc?.title ?? null,
|
|
160
|
+
url: typeof issue.url === 'string' ? issue.url : null,
|
|
161
|
+
},
|
|
162
|
+
changes: cache.changes,
|
|
163
|
+
justification: cache.justification,
|
|
164
|
+
source: cache.source,
|
|
165
|
+
confidence: null,
|
|
166
|
+
brandAlignment: cache.brandAlignment,
|
|
167
|
+
generatedAt: cache.generatedAt,
|
|
168
|
+
fingerprint: cache.fingerprint,
|
|
169
|
+
stale: live !== null && live !== cache.fingerprint,
|
|
170
|
+
issueType: String(issue.type),
|
|
171
|
+
severity: severityOf(issue.severity),
|
|
172
|
+
issueId: String(issue.id),
|
|
173
|
+
suggestionId: null,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
function redirectProposal(s) {
|
|
177
|
+
const fromPath = String(s.fromPath ?? '');
|
|
178
|
+
const toPath = String(s.toPath ?? '');
|
|
179
|
+
return {
|
|
180
|
+
id: `redirect:${String(s.id)}`,
|
|
181
|
+
kind: 'redirect',
|
|
182
|
+
title: `Redirect ${fromPath} → ${toPath}`,
|
|
183
|
+
entity: { type: null, id: null, title: null, url: fromPath },
|
|
184
|
+
changes: [{ field: 'redirect', label: 'Redirect', before: fromPath, after: toPath }],
|
|
185
|
+
justification: typeof s.reason === 'string' ? s.reason : '',
|
|
186
|
+
source: 'ai-404-recovery',
|
|
187
|
+
// Stored as a 0-100 Int; the UI contract is a 0-1 fraction.
|
|
188
|
+
confidence: Number(s.confidence ?? 0) / 100,
|
|
189
|
+
brandAlignment: null,
|
|
190
|
+
generatedAt: s.createdAt ? toDate(s.createdAt).toISOString() : null,
|
|
191
|
+
fingerprint: null,
|
|
192
|
+
stale: false,
|
|
193
|
+
issueType: null,
|
|
194
|
+
severity: null,
|
|
195
|
+
issueId: null,
|
|
196
|
+
suggestionId: String(s.id),
|
|
197
|
+
};
|
|
198
|
+
}
|
|
199
|
+
export async function listSeoProposals(db) {
|
|
200
|
+
const { issues, docsById } = await loadOpenFixableIssuesWithDocs(db);
|
|
201
|
+
const fixes = [];
|
|
202
|
+
for (const issue of issues) {
|
|
203
|
+
const cache = pendingFixFromMetadata(issue.metadata);
|
|
204
|
+
if (!cache)
|
|
205
|
+
continue;
|
|
206
|
+
fixes.push(issueFixProposal(issue, cache, docsById));
|
|
207
|
+
}
|
|
208
|
+
fixes.sort((a, b) => {
|
|
209
|
+
const band = (SEVERITY_RANK[String(a.severity)] ?? 99) - (SEVERITY_RANK[String(b.severity)] ?? 99);
|
|
210
|
+
if (band !== 0)
|
|
211
|
+
return band;
|
|
212
|
+
return Number(a.stale) - Number(b.stale);
|
|
213
|
+
});
|
|
214
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- optional-model guard
|
|
215
|
+
const d = db;
|
|
216
|
+
const redirectRows = d?.redirectSuggestion?.findMany
|
|
217
|
+
? await d.redirectSuggestion.findMany({
|
|
218
|
+
where: { status: 'open' },
|
|
219
|
+
orderBy: { confidence: 'desc' },
|
|
220
|
+
})
|
|
221
|
+
: [];
|
|
222
|
+
// Sort defensively: the DB already orders by confidence, but the shape of the
|
|
223
|
+
// payload should not depend on that (fakes in tests do not honor orderBy).
|
|
224
|
+
const redirects = redirectRows
|
|
225
|
+
.map(redirectProposal)
|
|
226
|
+
.sort((a, b) => (b.confidence ?? 0) - (a.confidence ?? 0));
|
|
227
|
+
const [issueGov, redirectGov, overrides, appliedLast7d] = await Promise.all([
|
|
228
|
+
getFeatureGovernance(db, ISSUE_FIX_FEATURE_KEY),
|
|
229
|
+
getFeatureGovernance(db, REDIRECT_FEATURE_KEY),
|
|
230
|
+
// `getSeoOverrides` already returns null on any read failure.
|
|
231
|
+
getSeoOverrides(db),
|
|
232
|
+
countAutopilotAppliedLast7d(db),
|
|
233
|
+
]);
|
|
234
|
+
const settings = resolveSeoModuleSettings(overrides);
|
|
235
|
+
const stale = fixes.filter((p) => p.stale).length;
|
|
236
|
+
return {
|
|
237
|
+
proposals: [...fixes, ...redirects],
|
|
238
|
+
counts: {
|
|
239
|
+
issueFixes: fixes.length - stale,
|
|
240
|
+
redirects: redirects.length,
|
|
241
|
+
stale,
|
|
242
|
+
eligibleForGeneration: selectEligibleForGeneration(issues, docsById).length,
|
|
243
|
+
},
|
|
244
|
+
governance: {
|
|
245
|
+
issueFixBulk: issueGov.allowBulkApply,
|
|
246
|
+
redirectBulk: redirectGov.allowBulkApply,
|
|
247
|
+
},
|
|
248
|
+
autopilot: {
|
|
249
|
+
enabled: settings.autopilot.enabled,
|
|
250
|
+
pausedReason: settings.autopilot.pausedAt ? settings.autopilot.pausedReason : null,
|
|
251
|
+
lastRunAt: settings.autopilotLastRunAt,
|
|
252
|
+
appliedLast7d,
|
|
253
|
+
},
|
|
254
|
+
};
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Cheap counts for the overview badge: every cached fix (stale ones are still
|
|
258
|
+
* rows in the inbox) + open redirect suggestions. Callers pass the open issues
|
|
259
|
+
* they already loaded so this adds at most one `count` query.
|
|
260
|
+
*/
|
|
261
|
+
export async function countPendingProposals(db, openIssues) {
|
|
262
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any -- optional-model guard
|
|
263
|
+
const d = db;
|
|
264
|
+
const redirects = d?.redirectSuggestion?.count
|
|
265
|
+
? ((await d.redirectSuggestion.count({ where: { status: 'open' } })) ?? 0)
|
|
266
|
+
: 0;
|
|
267
|
+
const issueFixes = openIssues.filter((i) => pendingFixFromMetadata(i.metadata) !== null).length;
|
|
268
|
+
return { issueFixes, redirects };
|
|
269
|
+
}
|
|
270
|
+
//# sourceMappingURL=proposals.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"proposals.js","sourceRoot":"","sources":["../../src/seo/proposals.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
-- SEO Autopilot ledger (spec: 2026-09-07-auto-apply-policy-engine-design.md §4).
|
|
2
|
+
-- One row per unattended write. Additive; no backfill.
|
|
3
|
+
|
|
4
|
+
CREATE TABLE "prenta_seo_auto_applies" (
|
|
5
|
+
"id" TEXT NOT NULL,
|
|
6
|
+
"kind" TEXT NOT NULL,
|
|
7
|
+
"issueId" TEXT,
|
|
8
|
+
"issueType" TEXT NOT NULL,
|
|
9
|
+
"entityId" TEXT,
|
|
10
|
+
"collection" TEXT,
|
|
11
|
+
"path" TEXT,
|
|
12
|
+
"redirectId" TEXT,
|
|
13
|
+
"before" JSONB,
|
|
14
|
+
"after" JSONB NOT NULL,
|
|
15
|
+
"gate" JSONB NOT NULL,
|
|
16
|
+
"status" TEXT NOT NULL,
|
|
17
|
+
"outcome" TEXT NOT NULL DEFAULT 'pending',
|
|
18
|
+
"outcomeMetrics" JSONB,
|
|
19
|
+
"outcomeCheckedAt" TIMESTAMP(3),
|
|
20
|
+
"versionId" TEXT,
|
|
21
|
+
"appliedById" TEXT NOT NULL,
|
|
22
|
+
"appliedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
23
|
+
"revertedAt" TIMESTAMP(3),
|
|
24
|
+
"revertedById" TEXT,
|
|
25
|
+
"revertReason" TEXT,
|
|
26
|
+
"createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
27
|
+
"updatedAt" TIMESTAMP(3) NOT NULL,
|
|
28
|
+
|
|
29
|
+
CONSTRAINT "prenta_seo_auto_applies_pkey" PRIMARY KEY ("id")
|
|
30
|
+
);
|
|
31
|
+
|
|
32
|
+
CREATE INDEX "prenta_seo_auto_applies_status_appliedAt_idx" ON "prenta_seo_auto_applies"("status", "appliedAt");
|
|
33
|
+
CREATE INDEX "prenta_seo_auto_applies_entityId_idx" ON "prenta_seo_auto_applies"("entityId");
|
|
34
|
+
CREATE INDEX "prenta_seo_auto_applies_outcome_appliedAt_idx" ON "prenta_seo_auto_applies"("outcome", "appliedAt");
|
|
35
|
+
CREATE INDEX "prenta_seo_auto_applies_issueId_idx" ON "prenta_seo_auto_applies"("issueId");
|
package/prisma/schema.prisma
CHANGED
|
@@ -640,6 +640,49 @@ model RedirectSuggestion {
|
|
|
640
640
|
@@map("prenta_redirect_suggestions")
|
|
641
641
|
}
|
|
642
642
|
|
|
643
|
+
/// One row per change SEO Autopilot wrote (spec 2026-09-07-auto-apply-policy-engine §4).
|
|
644
|
+
/// `before`/`after` hold the field-level diff for issue fixes, or the created
|
|
645
|
+
/// redirect (after) for redirects. `before` is null for redirects: autopilot
|
|
646
|
+
/// never overwrites an existing redirect. Rows are never deleted; revert flips `status`.
|
|
647
|
+
model SeoAutoApply {
|
|
648
|
+
id String @id @default(cuid())
|
|
649
|
+
// 'issue-fix' | 'redirect'
|
|
650
|
+
kind String
|
|
651
|
+
issueId String?
|
|
652
|
+
// AutopilotIssueType
|
|
653
|
+
issueType String
|
|
654
|
+
entityId String?
|
|
655
|
+
collection String?
|
|
656
|
+
// Public path at apply time — outcome-watch key into prenta_search_perf_daily.
|
|
657
|
+
path String?
|
|
658
|
+
redirectId String?
|
|
659
|
+
before Json?
|
|
660
|
+
after Json
|
|
661
|
+
// { source, brandAlignment, confidence, checks }
|
|
662
|
+
gate Json
|
|
663
|
+
// 'applied' | 'verify_failed' | 'reverted' | 'outcome_reverted'
|
|
664
|
+
status String
|
|
665
|
+
// 'pending' | 'held' | 'regressed' | 'insufficient_data' | 'skipped'
|
|
666
|
+
outcome String @default("pending")
|
|
667
|
+
outcomeMetrics Json?
|
|
668
|
+
outcomeCheckedAt DateTime?
|
|
669
|
+
versionId String?
|
|
670
|
+
appliedById String
|
|
671
|
+
appliedAt DateTime @default(now())
|
|
672
|
+
revertedAt DateTime?
|
|
673
|
+
revertedById String?
|
|
674
|
+
// 'verify_failed' | 'outcome_regression' | 'manual'
|
|
675
|
+
revertReason String?
|
|
676
|
+
createdAt DateTime @default(now())
|
|
677
|
+
updatedAt DateTime @updatedAt
|
|
678
|
+
|
|
679
|
+
@@index([status, appliedAt])
|
|
680
|
+
@@index([entityId])
|
|
681
|
+
@@index([outcome, appliedAt])
|
|
682
|
+
@@index([issueId])
|
|
683
|
+
@@map("prenta_seo_auto_applies")
|
|
684
|
+
}
|
|
685
|
+
|
|
643
686
|
// A versioned snapshot of a form's field schema. Saving a form's schema in
|
|
644
687
|
// the admin creates a new row here; submissions reference the version they
|
|
645
688
|
// were captured against so historical entries always render with the fields
|