@captain_z/zsk 1.8.2 → 1.8.4
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/dist/bin.js +4 -0
- package/dist/bin.js.map +1 -1
- package/dist/commands/add-flow.d.ts +3 -7
- package/dist/commands/add-flow.js +7 -59
- package/dist/commands/add-flow.js.map +1 -1
- package/dist/commands/add.js +25 -104
- package/dist/commands/add.js.map +1 -1
- package/dist/commands/check.js +184 -8
- package/dist/commands/check.js.map +1 -1
- package/dist/commands/gate.d.ts +2 -0
- package/dist/commands/gate.js +2 -0
- package/dist/commands/gate.js.map +1 -1
- package/dist/core/prepare-sync.d.ts +2 -31
- package/dist/core/prepare-sync.js +119 -136
- package/dist/core/prepare-sync.js.map +1 -1
- package/dist/core/profile-bundle-installation.d.ts +55 -0
- package/dist/core/profile-bundle-installation.js +170 -0
- package/dist/core/profile-bundle-installation.js.map +1 -0
- package/dist/core/source-snapshot-adapters.d.ts +59 -0
- package/dist/core/source-snapshot-adapters.js +82 -0
- package/dist/core/source-snapshot-adapters.js.map +1 -0
- package/dist/core/staffing-plan.d.ts +7 -1
- package/dist/core/staffing-plan.js +186 -29
- package/dist/core/staffing-plan.js.map +1 -1
- package/dist/core/stage-clarity-verification.d.ts +31 -0
- package/dist/core/stage-clarity-verification.js +313 -0
- package/dist/core/stage-clarity-verification.js.map +1 -0
- package/dist/core/stage-quality-artifacts.d.ts +15 -0
- package/dist/core/stage-quality-artifacts.js +421 -0
- package/dist/core/stage-quality-artifacts.js.map +1 -0
- package/dist/core/stage-quality-contracts.d.ts +86 -0
- package/dist/core/stage-quality-contracts.js +2 -0
- package/dist/core/stage-quality-contracts.js.map +1 -0
- package/dist/core/stage-quality-criteria.d.ts +9 -0
- package/dist/core/stage-quality-criteria.js +323 -0
- package/dist/core/stage-quality-criteria.js.map +1 -0
- package/dist/core/stage-quality-rendering.d.ts +13 -0
- package/dist/core/stage-quality-rendering.js +122 -0
- package/dist/core/stage-quality-rendering.js.map +1 -0
- package/dist/core/stage-quality.d.ts +5 -52
- package/dist/core/stage-quality.js +40 -432
- package/dist/core/stage-quality.js.map +1 -1
- package/dist/core/template-registry.js +6 -0
- package/dist/core/template-registry.js.map +1 -1
- package/package.json +2 -2
- package/templates/module/frontend-module/design.md +25 -3
- package/templates/module/frontend-module/proposal.md +8 -0
- package/templates/module/frontend-module/spec.md +16 -0
- package/templates/module/frontend-module/tasks.md +23 -7
- package/templates/project-init/.zsk/config.yaml +33 -0
- package/templates/project-init/.zsk/docs/PROJECT-CONFIG.md +43 -0
- package/templates/project-init/.zsk/docs/SYSTEM-SPEC.md +23 -1
- package/templates/project-init/.zsk/raws/index.md +19 -0
- package/templates/project-init/.zsk/raws/prepare/design/index.md +18 -0
- package/templates/project-init/.zsk/raws/prepare/index.md +33 -0
- package/templates/project-init/.zsk/raws/prepare/ux/index.md +19 -0
- package/templates/project-init/.zsk/roles.yaml +11 -11
|
@@ -5,6 +5,7 @@ import { dirname, extname, isAbsolute, join, relative, resolve } from "node:path
|
|
|
5
5
|
import { DESIGN_LOCAL_FILE_ORIGIN_KEYS, JIRA_LOCAL_FILE_ORIGIN_KEYS, flattenProjectSources, resolveSourceSnapshot, } from "./config.js";
|
|
6
6
|
import { inferSourceOrigin } from "./origin-detection.js";
|
|
7
7
|
import { describeResource, updateRawIndexes, updateRawManifest } from "./raw-manifest.js";
|
|
8
|
+
import { acquireSourceSnapshot, chooseSourceSnapshotStrategy, createSourceSnapshotContext, } from "./source-snapshot-adapters.js";
|
|
8
9
|
import { getWorkspacePath } from "./workspace-layout.js";
|
|
9
10
|
export function resolvePrepareSyncArtifacts(target, config, runId) {
|
|
10
11
|
const dir = resolve(target, getWorkspacePath(config, "evidenceRoot"), "prepare", runId);
|
|
@@ -254,140 +255,119 @@ async function checkAuthEntry(entry, authStatePath, opts) {
|
|
|
254
255
|
}
|
|
255
256
|
}
|
|
256
257
|
async function syncEntry(target, config, entry, opts) {
|
|
257
|
-
const
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
258
|
+
const context = await createSourceSnapshotContext(target, config, entry, opts, hashIfExists);
|
|
259
|
+
return acquireSourceSnapshot(context, {
|
|
260
|
+
dryRun: skippedDryRunSnapshot,
|
|
261
|
+
local: syncLocalSnapshot,
|
|
262
|
+
provider: syncProviderSnapshot,
|
|
263
|
+
repository: syncRepositorySnapshot,
|
|
264
|
+
url: syncUrlSnapshot,
|
|
265
|
+
providerManaged: blockedProviderManagedSnapshot,
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
function skippedDryRunSnapshot(context) {
|
|
269
|
+
return {
|
|
270
|
+
...context.base,
|
|
271
|
+
strategy: chooseSourceSnapshotStrategy(context.origin.method),
|
|
272
|
+
status: "skipped",
|
|
273
|
+
changed: false,
|
|
274
|
+
reason: "dry-run requested; no snapshot was written",
|
|
275
|
+
validation: { dryRun: "pass" },
|
|
271
276
|
};
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
reason: "dry-run requested; no snapshot was written",
|
|
279
|
-
validation: { dryRun: "pass" },
|
|
280
|
-
};
|
|
277
|
+
}
|
|
278
|
+
async function syncLocalSnapshot(context) {
|
|
279
|
+
const { base, entry, source, snapshotPath, previousSnapshotHash, target } = context;
|
|
280
|
+
const sourcePath = source.origin?.path ?? source.path;
|
|
281
|
+
if (!sourcePath) {
|
|
282
|
+
return sourceGap(base, "configured local origin has no path");
|
|
281
283
|
}
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
await
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
284
|
+
const originPath = resolve(target, sourcePath);
|
|
285
|
+
if (!(await exists(originPath))) {
|
|
286
|
+
return sourceGap(base, "configured local origin path does not exist");
|
|
287
|
+
}
|
|
288
|
+
await mkdir(dirname(snapshotPath), { recursive: true });
|
|
289
|
+
if (shouldPreserveMachineReadable(source, originPath, snapshotPath)) {
|
|
290
|
+
await copyFile(originPath, snapshotPath);
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
await writeFile(snapshotPath, await renderStructuredLocalMarkdown(target, entry, originPath), "utf8");
|
|
294
|
+
}
|
|
295
|
+
const snapshotHash = await sha256(snapshotPath);
|
|
296
|
+
return {
|
|
297
|
+
...base,
|
|
298
|
+
strategy: "local-copy-structured-markdown",
|
|
299
|
+
status: "materialized",
|
|
300
|
+
snapshotHash,
|
|
301
|
+
changed: previousSnapshotHash !== snapshotHash,
|
|
302
|
+
reason: "local origin copied or materialized into configured snapshot",
|
|
303
|
+
validation: { sourceExists: "pass", bodyContent: "pass", snapshotWritten: "pass" },
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
async function syncProviderSnapshot(context, providerAdapter) {
|
|
307
|
+
const { target, config, entry, snapshotPath, previousSnapshotHash, opts, origin } = context;
|
|
308
|
+
const authStatePath = resolveSelectedAuthStatePath(target, config, opts);
|
|
309
|
+
const adapterResult = await runProviderAdapter(providerAdapter, target, entry, snapshotPath, previousSnapshotHash, authStatePath, opts);
|
|
310
|
+
if (!adapterResult)
|
|
311
|
+
return null;
|
|
312
|
+
if (adapterResult.status === "blocked-auth" && opts.browser && origin.method === "url") {
|
|
313
|
+
const browserResult = await fetchUrlSnapshotWithBrowser(target, entry, snapshotPath, previousSnapshotHash, authStatePath);
|
|
299
314
|
return {
|
|
300
|
-
...
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
snapshotHash,
|
|
304
|
-
changed: previousSnapshotHash !== snapshotHash,
|
|
305
|
-
reason: "local origin copied or materialized into configured snapshot",
|
|
306
|
-
validation: { sourceExists: "pass", bodyContent: "pass", snapshotWritten: "pass" },
|
|
315
|
+
...browserResult,
|
|
316
|
+
adapter: `${providerAdapter}-browser-fallback`,
|
|
317
|
+
metadata: cleanMetadata({ providerAdapter, fallbackFrom: adapterResult.strategy }),
|
|
307
318
|
};
|
|
308
319
|
}
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
320
|
+
return adapterResult;
|
|
321
|
+
}
|
|
322
|
+
async function syncRepositorySnapshot(context) {
|
|
323
|
+
const { base, entry, origin, snapshotPath, previousSnapshotHash } = context;
|
|
324
|
+
await mkdir(dirname(snapshotPath), { recursive: true });
|
|
325
|
+
const content = renderRepositoryMetadata(entry, origin.ref ?? "");
|
|
326
|
+
await writeFile(snapshotPath, content, "utf8");
|
|
327
|
+
const snapshotHash = await sha256(snapshotPath);
|
|
328
|
+
return {
|
|
329
|
+
...base,
|
|
330
|
+
strategy: "repository-metadata-only",
|
|
331
|
+
status: "metadata-only",
|
|
332
|
+
snapshotHash,
|
|
333
|
+
changed: previousSnapshotHash !== snapshotHash,
|
|
334
|
+
reason: "repository origin recorded as metadata; configure contract paths or checkout policy before broad acquisition",
|
|
335
|
+
validation: { repositoryNotCrawled: "pass", snapshotWritten: "pass" },
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
async function syncUrlSnapshot(context) {
|
|
339
|
+
const { base, entry, source, origin, target, config, opts, snapshotPath, previousSnapshotHash } = context;
|
|
340
|
+
if (!opts.allowNetwork) {
|
|
330
341
|
return {
|
|
331
342
|
...base,
|
|
332
|
-
strategy: "
|
|
333
|
-
status: "
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
validation: { repositoryNotCrawled: "pass", snapshotWritten: "pass" },
|
|
343
|
+
strategy: "playwright-auth-or-direct-fetch",
|
|
344
|
+
status: "blocked-auth",
|
|
345
|
+
changed: false,
|
|
346
|
+
reason: "remote URL requires --allow-network or a materialized snapshot; Playwright auth helper can create reusable storageState first",
|
|
347
|
+
validation: { networkAllowed: "fail", previousSnapshotPreserved: previousSnapshotHash ? "pass" : "skipped" },
|
|
338
348
|
};
|
|
339
349
|
}
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
changed: false,
|
|
347
|
-
reason: "remote URL requires --allow-network or a materialized snapshot; Playwright auth helper can create reusable storageState first",
|
|
348
|
-
validation: { networkAllowed: "fail", previousSnapshotPreserved: previousSnapshotHash ? "pass" : "skipped" },
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
|
-
const authStatePath = resolveSelectedAuthStatePath(target, config, opts);
|
|
352
|
-
if (opts.browser) {
|
|
353
|
-
if (origin.ref && await hasDirectRuntimeAuthForSource(source, origin.ref, authStatePath)) {
|
|
354
|
-
const direct = await fetchUrlSnapshot(target, entry, snapshotPath, previousSnapshotHash, authStatePath);
|
|
355
|
-
if (direct.status === "materialized")
|
|
356
|
-
return direct;
|
|
357
|
-
}
|
|
358
|
-
return fetchUrlSnapshotWithBrowser(target, entry, snapshotPath, previousSnapshotHash, authStatePath);
|
|
350
|
+
const authStatePath = resolveSelectedAuthStatePath(target, config, opts);
|
|
351
|
+
if (opts.browser) {
|
|
352
|
+
if (origin.ref && await hasDirectRuntimeAuthForSource(source, origin.ref, authStatePath)) {
|
|
353
|
+
const direct = await fetchUrlSnapshot(target, entry, snapshotPath, previousSnapshotHash, authStatePath);
|
|
354
|
+
if (direct.status === "materialized")
|
|
355
|
+
return direct;
|
|
359
356
|
}
|
|
360
|
-
return
|
|
357
|
+
return fetchUrlSnapshotWithBrowser(target, entry, snapshotPath, previousSnapshotHash, authStatePath);
|
|
361
358
|
}
|
|
359
|
+
return fetchUrlSnapshot(target, entry, snapshotPath, previousSnapshotHash, authStatePath);
|
|
360
|
+
}
|
|
361
|
+
function blockedProviderManagedSnapshot(context) {
|
|
362
362
|
return {
|
|
363
|
-
...base,
|
|
363
|
+
...context.base,
|
|
364
364
|
strategy: "confirm-acquisition-method",
|
|
365
365
|
status: "blocked-auth",
|
|
366
366
|
changed: false,
|
|
367
367
|
reason: "provider-managed origin needs an explicit acquisition method or exported snapshot",
|
|
368
|
-
validation: { acquisitionMethodConfirmed: "fail", previousSnapshotPreserved: previousSnapshotHash ? "pass" : "skipped" },
|
|
368
|
+
validation: { acquisitionMethodConfirmed: "fail", previousSnapshotPreserved: context.previousSnapshotHash ? "pass" : "skipped" },
|
|
369
369
|
};
|
|
370
370
|
}
|
|
371
|
-
function selectProviderAdapter(source, origin) {
|
|
372
|
-
const keys = [
|
|
373
|
-
origin.provider,
|
|
374
|
-
origin.kind,
|
|
375
|
-
source.origin?.provider,
|
|
376
|
-
source.origin?.kind,
|
|
377
|
-
source.kind,
|
|
378
|
-
source.type,
|
|
379
|
-
].map((value) => normalizeAdapterKey(typeof value === "string" ? value : undefined));
|
|
380
|
-
if (keys.some((value) => value.includes("confluence")))
|
|
381
|
-
return "confluence";
|
|
382
|
-
if (keys.some((value) => value.includes("jira")))
|
|
383
|
-
return "jira";
|
|
384
|
-
if (keys.some((value) => value.includes("gitlab")))
|
|
385
|
-
return "gitlab";
|
|
386
|
-
if (keys.some((value) => ["figma", "modao", "mastergo", "design", "design-asset", "design-source"].includes(value))) {
|
|
387
|
-
return "design-source";
|
|
388
|
-
}
|
|
389
|
-
return undefined;
|
|
390
|
-
}
|
|
391
371
|
async function runProviderAdapter(adapter, target, entry, snapshotPath, previousSnapshotHash, authStatePath, opts) {
|
|
392
372
|
if (adapter === "confluence") {
|
|
393
373
|
return runConfluenceAdapter(target, entry, snapshotPath, previousSnapshotHash, authStatePath, opts);
|
|
@@ -560,10 +540,11 @@ async function runDesignAdapter(target, entry, snapshotPath, previousSnapshotHas
|
|
|
560
540
|
});
|
|
561
541
|
}
|
|
562
542
|
const url = firstOriginString(entry.source, "apiUrl", "exportUrl", "url") ?? base.origin;
|
|
563
|
-
if (!url)
|
|
564
|
-
return
|
|
543
|
+
if (!url) {
|
|
544
|
+
return sourceGap(base, "design source needs origin.url, origin.apiUrl, origin.exportUrl, or a local exportPath/assetPath/filePath before acquisition", designProviderGapMetadata("missing-provider-locator"));
|
|
545
|
+
}
|
|
565
546
|
if (!opts.allowNetwork) {
|
|
566
|
-
return blockedAdapter(base, opts.browser ? "design-browser-render" : "design-api-or-browser", "design source requires --allow-network with token/export URL or --browser storageState acquisition");
|
|
547
|
+
return blockedAdapter(base, opts.browser ? "design-browser-render" : "design-api-or-browser", "design source requires --allow-network with token/export URL or --browser storageState acquisition", designProviderGapMetadata("network-not-allowed"));
|
|
567
548
|
}
|
|
568
549
|
const directRuntimeAuth = await hasDirectRuntimeAuthForSource(entry.source, url, authStatePath);
|
|
569
550
|
if (opts.browser && !directRuntimeAuth) {
|
|
@@ -578,18 +559,18 @@ async function runDesignAdapter(target, entry, snapshotPath, previousSnapshotHas
|
|
|
578
559
|
if (!fetched.ok) {
|
|
579
560
|
if (opts.browser)
|
|
580
561
|
return fetchUrlSnapshotWithBrowser(target, entry, snapshotPath, previousSnapshotHash, authStatePath);
|
|
581
|
-
return blockedAdapter(base, "design-api-or-export", fetched.reason);
|
|
562
|
+
return blockedAdapter(base, "design-api-or-export", fetched.reason, designProviderGapMetadata("provider-fetch-failed"));
|
|
582
563
|
}
|
|
583
564
|
if (looksLikeLoginPage(fetched.body, fetched.resolvedOrigin)) {
|
|
584
565
|
if (opts.browser)
|
|
585
566
|
return fetchUrlSnapshotWithBrowser(target, entry, snapshotPath, previousSnapshotHash, authStatePath);
|
|
586
|
-
return blockedAdapter(base, "design-api-or-export", "design adapter resolved to login/chrome content; previous snapshot was preserved");
|
|
567
|
+
return blockedAdapter(base, "design-api-or-export", "design adapter resolved to login/chrome content; previous snapshot was preserved", designProviderGapMetadata("provider-auth-blocked"));
|
|
587
568
|
}
|
|
588
569
|
const content = renderDesignMarkdown(fetched.body, fetched.resolvedOrigin, fetched.contentType);
|
|
589
570
|
if (!hasMaterialContent(content)) {
|
|
590
571
|
if (opts.browser)
|
|
591
572
|
return fetchUrlSnapshotWithBrowser(target, entry, snapshotPath, previousSnapshotHash, authStatePath);
|
|
592
|
-
return blockedAdapter(base, "design-api-or-export", "design adapter produced no material design content; previous snapshot was preserved");
|
|
573
|
+
return blockedAdapter(base, "design-api-or-export", "design adapter produced no material design content; previous snapshot was preserved", designProviderGapMetadata("provider-content-incomplete"));
|
|
593
574
|
}
|
|
594
575
|
return writeAdapterSnapshot(target, entry, snapshotPath, previousSnapshotHash, {
|
|
595
576
|
adapter: "design-source",
|
|
@@ -1023,6 +1004,11 @@ function renderDesignMarkdown(body, origin, contentType) {
|
|
|
1023
1004
|
"",
|
|
1024
1005
|
fencedBody(JSON.stringify(parsed, null, 2), "application/json"),
|
|
1025
1006
|
"",
|
|
1007
|
+
"## Reconstruction Guidance",
|
|
1008
|
+
"",
|
|
1009
|
+
"- Semantic category ownership, assets/raw files, reconstruction.json, and known gaps must be reviewed before treating this as complete UE reconstruction evidence.",
|
|
1010
|
+
"- If provider raw payloads or assets are unavailable, record a provider gap and fallback risk in downstream evidence.",
|
|
1011
|
+
"",
|
|
1026
1012
|
].filter((line) => typeof line === "string").join("\n");
|
|
1027
1013
|
}
|
|
1028
1014
|
function designNodeNames(value) {
|
|
@@ -1035,6 +1021,13 @@ function designNodeNames(value) {
|
|
|
1035
1021
|
const nested = Array.isArray(children) ? children.flatMap(designNodeNames) : [];
|
|
1036
1022
|
return current ? [current, ...nested] : nested;
|
|
1037
1023
|
}
|
|
1024
|
+
function designProviderGapMetadata(reason) {
|
|
1025
|
+
return {
|
|
1026
|
+
providerGap: reason,
|
|
1027
|
+
fallbackEvidenceRequired: true,
|
|
1028
|
+
reconstructionRisk: "semantic UE reconstruction needs provider raw payload, assets/raw files, reconstruction.json, and known gaps before downstream design use",
|
|
1029
|
+
};
|
|
1030
|
+
}
|
|
1038
1031
|
function parseCsvRows(value) {
|
|
1039
1032
|
const rows = parseCsv(value).filter((row) => row.some((cell) => cell.trim().length > 0));
|
|
1040
1033
|
const headers = rows.shift() ?? [];
|
|
@@ -1246,9 +1239,6 @@ function escapeTable(value) {
|
|
|
1246
1239
|
function isRecord(value) {
|
|
1247
1240
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1248
1241
|
}
|
|
1249
|
-
function normalizeAdapterKey(value) {
|
|
1250
|
-
return value?.trim().toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") ?? "";
|
|
1251
|
-
}
|
|
1252
1242
|
function renderRepositoryMetadata(entry, repository) {
|
|
1253
1243
|
return [
|
|
1254
1244
|
"---",
|
|
@@ -1317,15 +1307,6 @@ function shouldPreserveMachineReadable(source, originPath, snapshotPath) {
|
|
|
1317
1307
|
[".json", ".yaml", ".yml"].includes(ext) ||
|
|
1318
1308
|
[".json", ".yaml", ".yml"].includes(snapshotExt));
|
|
1319
1309
|
}
|
|
1320
|
-
function chooseStrategy(method) {
|
|
1321
|
-
if (method === "local")
|
|
1322
|
-
return "local-copy-structured-markdown";
|
|
1323
|
-
if (method === "repository")
|
|
1324
|
-
return "repository-metadata-only";
|
|
1325
|
-
if (method === "url")
|
|
1326
|
-
return "playwright-auth-or-direct-fetch";
|
|
1327
|
-
return "confirm-acquisition-method";
|
|
1328
|
-
}
|
|
1329
1310
|
function renderDownstreamImpact(results) {
|
|
1330
1311
|
const changed = results.filter((result) => result.changed);
|
|
1331
1312
|
const blocked = results.filter((result) => ["blocked-auth", "source-gap", "failed"].includes(result.status));
|
|
@@ -1375,13 +1356,14 @@ function hasMaterialContent(value) {
|
|
|
1375
1356
|
const body = value.replace(/^---[\s\S]*?---/, "").replace(/[#*\-[\]()`\s]/g, "");
|
|
1376
1357
|
return body.length >= 20;
|
|
1377
1358
|
}
|
|
1378
|
-
function sourceGap(base, reason) {
|
|
1359
|
+
function sourceGap(base, reason, metadata) {
|
|
1379
1360
|
return {
|
|
1380
1361
|
...base,
|
|
1381
1362
|
strategy: "source-gap",
|
|
1382
1363
|
status: "source-gap",
|
|
1383
1364
|
changed: false,
|
|
1384
1365
|
reason,
|
|
1366
|
+
metadata: cleanMetadata(metadata ?? {}),
|
|
1385
1367
|
validation: { sourceAvailable: "fail" },
|
|
1386
1368
|
};
|
|
1387
1369
|
}
|
|
@@ -1395,13 +1377,14 @@ function blocked(base, reason) {
|
|
|
1395
1377
|
validation: { bodyContent: "fail", previousSnapshotPreserved: base.previousSnapshotHash ? "pass" : "skipped" },
|
|
1396
1378
|
};
|
|
1397
1379
|
}
|
|
1398
|
-
function blockedAdapter(base, strategy, reason) {
|
|
1380
|
+
function blockedAdapter(base, strategy, reason, metadata) {
|
|
1399
1381
|
return {
|
|
1400
1382
|
...base,
|
|
1401
1383
|
strategy,
|
|
1402
1384
|
status: "blocked-auth",
|
|
1403
1385
|
changed: false,
|
|
1404
1386
|
reason,
|
|
1387
|
+
metadata: cleanMetadata(metadata ?? {}),
|
|
1405
1388
|
validation: { bodyContent: "fail", previousSnapshotPreserved: base.previousSnapshotHash ? "pass" : "skipped" },
|
|
1406
1389
|
};
|
|
1407
1390
|
}
|