qfai 1.7.6 → 1.7.7

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/index.cjs CHANGED
@@ -33,6 +33,8 @@ __export(src_exports, {
33
33
  ID_PREFIXES: () => ID_PREFIXES,
34
34
  RUNTIME_HEAVY_CHECKS: () => RUNTIME_HEAVY_CHECKS,
35
35
  STATIC_OBLIGATIONS: () => STATIC_OBLIGATIONS,
36
+ VALID_MODE_SOURCES: () => VALID_MODE_SOURCES,
37
+ adaptSurfaceEvidence: () => adaptSurfaceEvidence,
36
38
  autogenerateUiFidelity: () => autogenerateUiFidelity,
37
39
  buildUiFidelityScreens: () => buildUiFidelityScreens,
38
40
  checkDecisionGuardrails: () => checkDecisionGuardrails,
@@ -58,6 +60,7 @@ __export(src_exports, {
58
60
  filterDecisionGuardrailsByKeyword: () => filterDecisionGuardrailsByKeyword,
59
61
  findConfigRoot: () => findConfigRoot,
60
62
  formatGuardrailsForLlm: () => formatGuardrailsForLlm,
63
+ formatModeLog: () => formatModeLog,
61
64
  formatReportJson: () => formatReportJson,
62
65
  formatReportMarkdown: () => formatReportMarkdown,
63
66
  getConfigPath: () => getConfigPath,
@@ -65,9 +68,11 @@ __export(src_exports, {
65
68
  loadConfig: () => loadConfig,
66
69
  loadDecisionGuardrails: () => loadDecisionGuardrails,
67
70
  normalizeDecisionGuardrails: () => normalizeDecisionGuardrails,
71
+ readDiscussionRecommendation: () => readDiscussionRecommendation,
68
72
  resolveObligations: () => resolveObligations,
69
73
  resolveObligationsWithOptIn: () => resolveObligationsWithOptIn,
70
74
  resolvePath: () => resolvePath,
75
+ resolvePrecedence: () => resolvePrecedence,
71
76
  resolveToolVersion: () => resolveToolVersion,
72
77
  runMockPaths: () => runMockPaths,
73
78
  runSddPreflight: () => runSddPreflight,
@@ -540,6 +545,15 @@ function normalizeUiux(raw, configPath, issues) {
540
545
  );
541
546
  }
542
547
  }
548
+ if (raw.phase1ReleaseDate !== void 0) {
549
+ if (typeof raw.phase1ReleaseDate === "string" && !isNaN(new Date(raw.phase1ReleaseDate).getTime())) {
550
+ result.phase1ReleaseDate = raw.phase1ReleaseDate;
551
+ } else {
552
+ issues.push(
553
+ configIssue(configPath, "uiux.phase1ReleaseDate \u306F\u6709\u52B9\u306A\u65E5\u4ED8\u6587\u5B57\u5217\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002")
554
+ );
555
+ }
556
+ }
543
557
  if (raw.warning_as_error_override !== void 0) {
544
558
  if (Array.isArray(raw.warning_as_error_override) && raw.warning_as_error_override.every((v) => typeof v === "string")) {
545
559
  result.warning_as_error_override = raw.warning_as_error_override;
@@ -564,6 +578,12 @@ function normalizeUiux(raw, configPath, issues) {
564
578
  result.audit = audit;
565
579
  }
566
580
  }
581
+ if (raw.migration !== void 0) {
582
+ const migration = normalizeUiuxMigration(raw.migration, configPath, issues);
583
+ if (migration) {
584
+ result.migration = migration;
585
+ }
586
+ }
567
587
  return Object.keys(result).length > 0 ? result : void 0;
568
588
  }
569
589
  function normalizeUiuxAudit(raw, configPath, issues) {
@@ -623,6 +643,23 @@ function normalizeUiuxAudit(raw, configPath, issues) {
623
643
  }
624
644
  return Object.keys(result).length > 0 ? result : void 0;
625
645
  }
646
+ function normalizeUiuxMigration(raw, configPath, issues) {
647
+ if (!isRecord(raw)) {
648
+ issues.push(configIssue(configPath, "uiux.migration \u306F\u30AA\u30D6\u30B8\u30A7\u30AF\u30C8\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002"));
649
+ return void 0;
650
+ }
651
+ const result = {};
652
+ if (raw.strict !== void 0) {
653
+ if (typeof raw.strict === "boolean") {
654
+ result.strict = raw.strict;
655
+ } else {
656
+ issues.push(
657
+ configIssue(configPath, "uiux.migration.strict \u306F\u30D6\u30FC\u30EB\u5024\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059\u3002")
658
+ );
659
+ }
660
+ }
661
+ return Object.keys(result).length > 0 ? result : void 0;
662
+ }
626
663
  function normalizeRenderEvidence(raw, configPath, issues) {
627
664
  if (!isRecord(raw)) {
628
665
  issues.push(
@@ -3680,18 +3717,125 @@ function resolveObligationsWithOptIn(mode, optIn) {
3680
3717
  return [.../* @__PURE__ */ new Set([...base, ...validOptIns])];
3681
3718
  }
3682
3719
 
3720
+ // src/core/prototyping/discussionReader.ts
3721
+ var import_promises13 = require("fs/promises");
3722
+ var import_node_path12 = __toESM(require("path"), 1);
3723
+ var import_yaml3 = require("yaml");
3724
+ var VALID_MODES = /* @__PURE__ */ new Set(["low-cost", "standard", "full-harness"]);
3725
+ var SIDECAR_FILENAME = "prototyping.yaml";
3726
+ async function readDiscussionRecommendation(discussionPackDir) {
3727
+ const filePath = import_node_path12.default.join(discussionPackDir, SIDECAR_FILENAME);
3728
+ let raw;
3729
+ try {
3730
+ raw = await (0, import_promises13.readFile)(filePath, "utf-8");
3731
+ } catch {
3732
+ return { recommended_mode: null, rationale: null };
3733
+ }
3734
+ let doc;
3735
+ try {
3736
+ doc = (0, import_yaml3.parse)(raw);
3737
+ } catch {
3738
+ return { recommended_mode: null, rationale: null };
3739
+ }
3740
+ if (!doc || typeof doc !== "object" || Array.isArray(doc)) {
3741
+ return { recommended_mode: null, rationale: null };
3742
+ }
3743
+ const record2 = doc;
3744
+ const prototyping = record2.prototyping;
3745
+ if (!prototyping || typeof prototyping !== "object" || Array.isArray(prototyping)) {
3746
+ return { recommended_mode: null, rationale: null };
3747
+ }
3748
+ const section = prototyping;
3749
+ const rawMode = typeof section.recommended_mode === "string" ? section.recommended_mode.trim() : null;
3750
+ const rationale = typeof section.rationale === "string" ? section.rationale.trim() : null;
3751
+ const recommended_mode = rawMode && VALID_MODES.has(rawMode) ? rawMode : null;
3752
+ return { recommended_mode, rationale };
3753
+ }
3754
+
3755
+ // src/core/prototyping/precedenceResolver.ts
3756
+ var VALID_MODES2 = /* @__PURE__ */ new Set([
3757
+ "default",
3758
+ "standard",
3759
+ "low-cost",
3760
+ "full-harness"
3761
+ ]);
3762
+ function isPrototypingMode(value) {
3763
+ return VALID_MODES2.has(value);
3764
+ }
3765
+ var SYSTEM_DEFAULT_MODE = "standard";
3766
+ function resolvePrecedence(input) {
3767
+ const { cliMode, discussion } = input;
3768
+ const rawRecommended = discussion?.recommended_mode ?? null;
3769
+ const recommended_mode = rawRecommended && isPrototypingMode(rawRecommended) ? rawRecommended : null;
3770
+ if (cliMode) {
3771
+ return {
3772
+ effective_mode: cliMode,
3773
+ mode_source: "cli-override",
3774
+ recommended_mode,
3775
+ rationale: "CLI override"
3776
+ };
3777
+ }
3778
+ if (recommended_mode) {
3779
+ return {
3780
+ effective_mode: recommended_mode,
3781
+ mode_source: "discussion-recommendation",
3782
+ recommended_mode,
3783
+ rationale: discussion?.rationale ?? "discussion recommendation"
3784
+ };
3785
+ }
3786
+ return {
3787
+ effective_mode: SYSTEM_DEFAULT_MODE,
3788
+ mode_source: "default",
3789
+ recommended_mode: null,
3790
+ rationale: "system default"
3791
+ };
3792
+ }
3793
+
3794
+ // src/core/prototyping/modeLogger.ts
3795
+ var VALID_MODE_SOURCES = /* @__PURE__ */ new Set([
3796
+ "cli-override",
3797
+ "discussion-recommendation",
3798
+ "default"
3799
+ ]);
3800
+ var EVIDENCE_EXPECTATIONS = {
3801
+ "low-cost": "L1/L2",
3802
+ standard: "L2/L3",
3803
+ "full-harness": "L3/L4/L5"
3804
+ };
3805
+ function formatModeLog(resolution) {
3806
+ return {
3807
+ mode_source: resolution.mode_source,
3808
+ recommended_mode: resolution.recommended_mode,
3809
+ effective_mode: resolution.effective_mode,
3810
+ rationale: resolution.rationale,
3811
+ evidence_expectations: EVIDENCE_EXPECTATIONS[resolution.effective_mode] ?? "L2/L3"
3812
+ };
3813
+ }
3814
+
3815
+ // src/core/prototyping/surfaceAdapter.ts
3816
+ var VISUAL_SURFACES = /* @__PURE__ */ new Set(["web", "mobile", "desktop"]);
3817
+ function adaptSurfaceEvidence(input) {
3818
+ const { surfaceType, effectiveMode } = input;
3819
+ const isVisual = VISUAL_SURFACES.has(surfaceType);
3820
+ const isLowCost = effectiveMode === "low-cost";
3821
+ const static_evidence = "collected";
3822
+ const runtime_evidence = isLowCost ? "n/a" : "collected";
3823
+ const visual_review = isVisual && !isLowCost ? "collected" : "n/a";
3824
+ return { visual_review, static_evidence, runtime_evidence };
3825
+ }
3826
+
3683
3827
  // src/core/report.ts
3684
- var import_promises52 = require("fs/promises");
3685
- var import_node_path54 = __toESM(require("path"), 1);
3828
+ var import_promises55 = require("fs/promises");
3829
+ var import_node_path57 = __toESM(require("path"), 1);
3686
3830
 
3687
3831
  // src/core/contractIndex.ts
3688
- var import_promises13 = require("fs/promises");
3689
- var import_node_path12 = __toESM(require("path"), 1);
3832
+ var import_promises14 = require("fs/promises");
3833
+ var import_node_path13 = __toESM(require("path"), 1);
3690
3834
  async function buildContractIndex(root, config) {
3691
3835
  const contractsRoot = resolvePath(root, config, "contractsDir");
3692
- const uiRoot = import_node_path12.default.join(contractsRoot, "ui");
3693
- const apiRoot = import_node_path12.default.join(contractsRoot, "api");
3694
- const dbRoot = import_node_path12.default.join(contractsRoot, "db");
3836
+ const uiRoot = import_node_path13.default.join(contractsRoot, "ui");
3837
+ const apiRoot = import_node_path13.default.join(contractsRoot, "api");
3838
+ const dbRoot = import_node_path13.default.join(contractsRoot, "db");
3695
3839
  const [uiFiles, themaFiles, apiFiles, dbFiles] = await Promise.all([
3696
3840
  collectUiContractFiles(uiRoot),
3697
3841
  collectThemaContractFiles(),
@@ -3711,7 +3855,7 @@ async function buildContractIndex(root, config) {
3711
3855
  }
3712
3856
  async function indexContractFiles(files, index) {
3713
3857
  for (const file of files) {
3714
- const text = await (0, import_promises13.readFile)(file, "utf-8");
3858
+ const text = await (0, import_promises14.readFile)(file, "utf-8");
3715
3859
  extractDeclaredContractIds(text).forEach((id) => {
3716
3860
  record(index, id, file);
3717
3861
  });
@@ -3981,15 +4125,15 @@ function resolveParentTcId(tcRef) {
3981
4125
  }
3982
4126
 
3983
4127
  // src/core/paths.ts
3984
- var import_node_path13 = __toESM(require("path"), 1);
4128
+ var import_node_path14 = __toESM(require("path"), 1);
3985
4129
  function toRelativePath(root, target) {
3986
4130
  if (!target) {
3987
4131
  return target;
3988
4132
  }
3989
- if (!import_node_path13.default.isAbsolute(target)) {
4133
+ if (!import_node_path14.default.isAbsolute(target)) {
3990
4134
  return toPosixPath2(target);
3991
4135
  }
3992
- const relative = import_node_path13.default.relative(root, target);
4136
+ const relative = import_node_path14.default.relative(root, target);
3993
4137
  if (!relative) {
3994
4138
  return ".";
3995
4139
  }
@@ -4160,7 +4304,7 @@ function parseSpec(md, file) {
4160
4304
  }
4161
4305
 
4162
4306
  // src/core/deltaV1.ts
4163
- var import_yaml3 = require("yaml");
4307
+ var import_yaml4 = require("yaml");
4164
4308
  var REQUIRED_DELTA_META_KEYS = [
4165
4309
  "id",
4166
4310
  "date",
@@ -4315,7 +4459,7 @@ function parseYamlMeta(block) {
4315
4459
  return { value: null, error: null };
4316
4460
  }
4317
4461
  try {
4318
- const parsed = (0, import_yaml3.parse)(sanitizeMetaYaml(block));
4462
+ const parsed = (0, import_yaml4.parse)(sanitizeMetaYaml(block));
4319
4463
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
4320
4464
  return {
4321
4465
  value: null,
@@ -4349,7 +4493,7 @@ function parseVerificationPlan(body) {
4349
4493
  return { planHeadingLine: planHeading.line, parseError: null, items: [] };
4350
4494
  }
4351
4495
  try {
4352
- const parsed = (0, import_yaml3.parse)(yamlSource);
4496
+ const parsed = (0, import_yaml4.parse)(yamlSource);
4353
4497
  if (!Array.isArray(parsed)) {
4354
4498
  return {
4355
4499
  planHeadingLine: planHeading.line,
@@ -4436,16 +4580,16 @@ function asTagArray(value) {
4436
4580
  }
4437
4581
 
4438
4582
  // src/core/version.ts
4439
- var import_promises14 = require("fs/promises");
4440
- var import_node_path14 = __toESM(require("path"), 1);
4583
+ var import_promises15 = require("fs/promises");
4584
+ var import_node_path15 = __toESM(require("path"), 1);
4441
4585
  var import_node_url = require("url");
4442
4586
  async function resolveToolVersion() {
4443
- if ("1.7.6".length > 0) {
4444
- return "1.7.6";
4587
+ if ("1.7.7".length > 0) {
4588
+ return "1.7.7";
4445
4589
  }
4446
4590
  try {
4447
4591
  const packagePath = resolvePackageJsonPath();
4448
- const raw = await (0, import_promises14.readFile)(packagePath, "utf-8");
4592
+ const raw = await (0, import_promises15.readFile)(packagePath, "utf-8");
4449
4593
  const parsed = JSON.parse(raw);
4450
4594
  const version = typeof parsed.version === "string" ? parsed.version : "";
4451
4595
  return version.length > 0 ? version : "unknown";
@@ -4456,13 +4600,13 @@ async function resolveToolVersion() {
4456
4600
  function resolvePackageJsonPath() {
4457
4601
  const base = __filename;
4458
4602
  const basePath = base.startsWith("file:") ? (0, import_node_url.fileURLToPath)(base) : base;
4459
- return import_node_path14.default.resolve(import_node_path14.default.dirname(basePath), "../../package.json");
4603
+ return import_node_path15.default.resolve(import_node_path15.default.dirname(basePath), "../../package.json");
4460
4604
  }
4461
4605
 
4462
4606
  // src/core/waivers.ts
4463
- var import_promises15 = require("fs/promises");
4464
- var import_node_path15 = __toESM(require("path"), 1);
4465
- var import_yaml4 = require("yaml");
4607
+ var import_promises16 = require("fs/promises");
4608
+ var import_node_path16 = __toESM(require("path"), 1);
4609
+ var import_yaml5 = require("yaml");
4466
4610
 
4467
4611
  // src/core/regex.ts
4468
4612
  function escapeRegExp(value) {
@@ -4470,12 +4614,12 @@ function escapeRegExp(value) {
4470
4614
  }
4471
4615
 
4472
4616
  // src/core/waivers.ts
4473
- var WAIVER_FILE = import_node_path15.default.join(".qfai", "waivers.yml");
4474
- var UNSUPPORTED_WAIVER_FILE = import_node_path15.default.join(".qfai", "waivers.yaml");
4617
+ var WAIVER_FILE = import_node_path16.default.join(".qfai", "waivers.yml");
4618
+ var UNSUPPORTED_WAIVER_FILE = import_node_path16.default.join(".qfai", "waivers.yaml");
4475
4619
  var ISO_DATE_RE = /^\d{4}-\d{2}-\d{2}$/;
4476
4620
  var RULE_ID_RE = /^[A-Z]+-\d{3}$/;
4477
4621
  async function applyWaivers(root, findings) {
4478
- const resolvedRoot = import_node_path15.default.resolve(root);
4622
+ const resolvedRoot = import_node_path16.default.resolve(root);
4479
4623
  const ruleSeverityIndex = buildRuleSeverityIndex(findings);
4480
4624
  const loaded = await loadWaivers(resolvedRoot, ruleSeverityIndex);
4481
4625
  const applied = applyWaiversToFindings(resolvedRoot, findings, loaded.applicableWaivers);
@@ -4488,8 +4632,8 @@ async function applyWaivers(root, findings) {
4488
4632
  };
4489
4633
  }
4490
4634
  async function loadWaivers(root, ruleSeverityIndex) {
4491
- const waiverPath = import_node_path15.default.join(root, WAIVER_FILE);
4492
- const unsupportedPath = import_node_path15.default.join(root, UNSUPPORTED_WAIVER_FILE);
4635
+ const waiverPath = import_node_path16.default.join(root, WAIVER_FILE);
4636
+ const unsupportedPath = import_node_path16.default.join(root, UNSUPPORTED_WAIVER_FILE);
4493
4637
  const validationIssues = [];
4494
4638
  if (await exists6(unsupportedPath)) {
4495
4639
  validationIssues.push(
@@ -4513,7 +4657,7 @@ async function loadWaivers(root, ruleSeverityIndex) {
4513
4657
  }
4514
4658
  let rawText;
4515
4659
  try {
4516
- rawText = await (0, import_promises15.readFile)(waiverPath, "utf-8");
4660
+ rawText = await (0, import_promises16.readFile)(waiverPath, "utf-8");
4517
4661
  } catch (error) {
4518
4662
  validationIssues.push(
4519
4663
  issue(
@@ -4534,7 +4678,7 @@ async function loadWaivers(root, ruleSeverityIndex) {
4534
4678
  }
4535
4679
  let parsed;
4536
4680
  try {
4537
- parsed = (0, import_yaml4.parse)(rawText);
4681
+ parsed = (0, import_yaml5.parse)(rawText);
4538
4682
  } catch (error) {
4539
4683
  validationIssues.push(
4540
4684
  issue(
@@ -5172,7 +5316,7 @@ function toErrorMessage(error) {
5172
5316
  }
5173
5317
  async function exists6(targetPath) {
5174
5318
  try {
5175
- await (0, import_promises15.access)(targetPath);
5319
+ await (0, import_promises16.access)(targetPath);
5176
5320
  return true;
5177
5321
  } catch {
5178
5322
  return false;
@@ -5206,8 +5350,8 @@ var STATIC_RULE_SEVERITY = {
5206
5350
  };
5207
5351
 
5208
5352
  // src/core/validators/contracts.ts
5209
- var import_promises16 = require("fs/promises");
5210
- var import_node_path16 = __toESM(require("path"), 1);
5353
+ var import_promises17 = require("fs/promises");
5354
+ var import_node_path17 = __toESM(require("path"), 1);
5211
5355
  var SQL_DANGEROUS_PATTERNS = [
5212
5356
  { pattern: /\bDROP\s+TABLE\b/i, label: "DROP TABLE" },
5213
5357
  { pattern: /\bDROP\s+DATABASE\b/i, label: "DROP DATABASE" },
@@ -5220,9 +5364,9 @@ var SQL_DANGEROUS_PATTERNS = [
5220
5364
  async function validateContracts(root, config) {
5221
5365
  const issues = [];
5222
5366
  const contractsRoot = resolvePath(root, config, "contractsDir");
5223
- const uiRoot = import_node_path16.default.join(contractsRoot, "ui");
5224
- const apiRoot = import_node_path16.default.join(contractsRoot, "api");
5225
- const dbRoot = import_node_path16.default.join(contractsRoot, "db");
5367
+ const uiRoot = import_node_path17.default.join(contractsRoot, "ui");
5368
+ const apiRoot = import_node_path17.default.join(contractsRoot, "api");
5369
+ const dbRoot = import_node_path17.default.join(contractsRoot, "db");
5226
5370
  const [uiFiles, apiFiles, dbFiles] = await Promise.all([
5227
5371
  collectUiContractFiles(uiRoot),
5228
5372
  collectApiContractFiles(apiRoot),
@@ -5276,7 +5420,7 @@ async function validateContracts(root, config) {
5276
5420
  }
5277
5421
  async function validateContractFile(file, kind) {
5278
5422
  const issues = [];
5279
- const text = await (0, import_promises16.readFile)(file, "utf-8");
5423
+ const text = await (0, import_promises17.readFile)(file, "utf-8");
5280
5424
  const declaredIds = extractDeclaredContractIds(text);
5281
5425
  issues.push(...validateDeclaredContractIds(declaredIds, file, kind));
5282
5426
  if (kind === "DB") {
@@ -5397,23 +5541,23 @@ function formatError5(error) {
5397
5541
  }
5398
5542
 
5399
5543
  // src/core/validators/discussMermaid.ts
5400
- var import_promises17 = require("fs/promises");
5401
- var import_node_path17 = __toESM(require("path"), 1);
5544
+ var import_promises18 = require("fs/promises");
5545
+ var import_node_path18 = __toESM(require("path"), 1);
5402
5546
  var DISCUSSION_PACK_FLOW_FILE = "03_Story-Workshop.md";
5403
5547
  var MERMAID_START_RE = /^\s*(`{3,}|~{3,})\s*mermaid\b/i;
5404
5548
  var FLOW_OR_SEQUENCE_RE = /\b(?:sequenceDiagram|flowchart)\b/;
5405
5549
  async function validateDiscussionMermaid(root) {
5406
- const discussionRootDir = import_node_path17.default.join(root, ".qfai", "discussion");
5550
+ const discussionRootDir = import_node_path18.default.join(root, ".qfai", "discussion");
5407
5551
  const discussionPackMarkdownFiles = await collectFiles(discussionRootDir, {
5408
5552
  extensions: [".md"]
5409
5553
  });
5410
5554
  const discussionPackFiles = [];
5411
5555
  for (const file of discussionPackMarkdownFiles) {
5412
- const fileName = import_node_path17.default.basename(file);
5556
+ const fileName = import_node_path18.default.basename(file);
5413
5557
  if (fileName !== DISCUSSION_PACK_FLOW_FILE) {
5414
5558
  continue;
5415
5559
  }
5416
- const discussionDirName = import_node_path17.default.basename(import_node_path17.default.dirname(file));
5560
+ const discussionDirName = import_node_path18.default.basename(import_node_path18.default.dirname(file));
5417
5561
  const nameValidation = validatePackName("discussion", discussionDirName);
5418
5562
  if (nameValidation.isCanonical) {
5419
5563
  discussionPackFiles.push({ file, kind: "current" });
@@ -5443,7 +5587,7 @@ async function validateDiscussionMermaid(root) {
5443
5587
  );
5444
5588
  }
5445
5589
  for (const { file } of discussionPackFiles) {
5446
- const text = await (0, import_promises17.readFile)(file, "utf-8");
5590
+ const text = await (0, import_promises18.readFile)(file, "utf-8");
5447
5591
  if (containsMermaidFlowDiagram(text)) {
5448
5592
  continue;
5449
5593
  }
@@ -5493,17 +5637,17 @@ function containsMermaidFlowDiagram(text) {
5493
5637
  }
5494
5638
 
5495
5639
  // src/core/validators/assistantAssets.ts
5496
- var import_promises18 = require("fs/promises");
5497
- var import_node_path18 = __toESM(require("path"), 1);
5640
+ var import_promises19 = require("fs/promises");
5641
+ var import_node_path19 = __toESM(require("path"), 1);
5498
5642
  var DRIFT_PROTOCOL_MARKER = "[DRIFT-PROTOCOL:MANDATORY]";
5499
5643
  var REVIEWER_GATE_HEADING_PATTERN = /^###\s+Reviewer Gate\b.*$/im;
5500
5644
  var ANY_MARKDOWN_HEADING_PATTERN = /^\s*#{1,6}\s+/m;
5501
5645
  async function validateAssistantAssets(root, config) {
5502
5646
  const skillsDir = resolvePath(root, config, "skillsDir");
5503
- const assistantDir = import_node_path18.default.dirname(skillsDir);
5504
- const skillsLocalDir = import_node_path18.default.join(import_node_path18.default.dirname(skillsDir), `${import_node_path18.default.basename(skillsDir)}.local`);
5505
- const driftProtocolPath = import_node_path18.default.join(assistantDir, "instructions", "drift-protocol.md");
5506
- const testLayersPath = import_node_path18.default.join(assistantDir, "steering", "test-layers.md");
5647
+ const assistantDir = import_node_path19.default.dirname(skillsDir);
5648
+ const skillsLocalDir = import_node_path19.default.join(import_node_path19.default.dirname(skillsDir), `${import_node_path19.default.basename(skillsDir)}.local`);
5649
+ const driftProtocolPath = import_node_path19.default.join(assistantDir, "instructions", "drift-protocol.md");
5650
+ const testLayersPath = import_node_path19.default.join(assistantDir, "steering", "test-layers.md");
5507
5651
  const issues = [];
5508
5652
  if (!await exists7(driftProtocolPath)) {
5509
5653
  issues.push(
@@ -5529,7 +5673,7 @@ async function validateAssistantAssets(root, config) {
5529
5673
  }
5530
5674
  const skillFiles = await collectSkillFiles([skillsDir, skillsLocalDir]);
5531
5675
  for (const skillFile of skillFiles) {
5532
- const content = await (0, import_promises18.readFile)(skillFile, "utf-8");
5676
+ const content = await (0, import_promises19.readFile)(skillFile, "utf-8");
5533
5677
  if (!content.includes(DRIFT_PROTOCOL_MARKER)) {
5534
5678
  issues.push(
5535
5679
  issue(
@@ -5571,7 +5715,7 @@ async function validateAssistantAssets(root, config) {
5571
5715
  }
5572
5716
  async function collectSkillFiles(dirs) {
5573
5717
  const files = await Promise.all(dirs.map((dir) => collectFiles(dir)));
5574
- return files.flat().filter((filePath) => import_node_path18.default.basename(filePath) === "SKILL.md").sort((a, b) => a.localeCompare(b));
5718
+ return files.flat().filter((filePath) => import_node_path19.default.basename(filePath) === "SKILL.md").sort((a, b) => a.localeCompare(b));
5575
5719
  }
5576
5720
  function extractReviewerGateSection(content) {
5577
5721
  const headingMatch = REVIEWER_GATE_HEADING_PATTERN.exec(content);
@@ -5604,7 +5748,7 @@ function collectMissingReviewerGateTerms(section) {
5604
5748
  }
5605
5749
  async function exists7(target) {
5606
5750
  try {
5607
- await (0, import_promises18.access)(target);
5751
+ await (0, import_promises19.access)(target);
5608
5752
  return true;
5609
5753
  } catch {
5610
5754
  return false;
@@ -5612,20 +5756,20 @@ async function exists7(target) {
5612
5756
  }
5613
5757
 
5614
5758
  // src/core/skillsIntegrity.ts
5615
- var import_promises19 = require("fs/promises");
5616
- var import_node_path20 = __toESM(require("path"), 1);
5759
+ var import_promises20 = require("fs/promises");
5760
+ var import_node_path21 = __toESM(require("path"), 1);
5617
5761
 
5618
5762
  // src/shared/assets.ts
5619
5763
  var import_node_fs = require("fs");
5620
- var import_node_path19 = __toESM(require("path"), 1);
5764
+ var import_node_path20 = __toESM(require("path"), 1);
5621
5765
  var import_node_url2 = require("url");
5622
5766
  function getInitAssetsDir() {
5623
5767
  const base = __filename;
5624
5768
  const basePath = base.startsWith("file:") ? (0, import_node_url2.fileURLToPath)(base) : base;
5625
- const baseDir = import_node_path19.default.dirname(basePath);
5769
+ const baseDir = import_node_path20.default.dirname(basePath);
5626
5770
  const candidates = [
5627
- import_node_path19.default.resolve(baseDir, "../../../assets/init"),
5628
- import_node_path19.default.resolve(baseDir, "../../assets/init")
5771
+ import_node_path20.default.resolve(baseDir, "../../../assets/init"),
5772
+ import_node_path20.default.resolve(baseDir, "../../assets/init")
5629
5773
  ];
5630
5774
  for (const candidate of candidates) {
5631
5775
  if ((0, import_node_fs.existsSync)(candidate)) {
@@ -5644,10 +5788,10 @@ function getInitAssetsDir() {
5644
5788
  // src/core/skillsIntegrity.ts
5645
5789
  async function diffProjectSkillsAgainstInitAssets(root, config) {
5646
5790
  const skillsDirConfig = config.paths.skillsDir;
5647
- const skillsDir = import_node_path20.default.isAbsolute(skillsDirConfig) ? skillsDirConfig : import_node_path20.default.resolve(root, skillsDirConfig);
5791
+ const skillsDir = import_node_path21.default.isAbsolute(skillsDirConfig) ? skillsDirConfig : import_node_path21.default.resolve(root, skillsDirConfig);
5648
5792
  let templateDir;
5649
5793
  try {
5650
- const rel = import_node_path20.default.isAbsolute(skillsDirConfig) ? import_node_path20.default.relative(root, skillsDirConfig) : skillsDirConfig;
5794
+ const rel = import_node_path21.default.isAbsolute(skillsDirConfig) ? import_node_path21.default.relative(root, skillsDirConfig) : skillsDirConfig;
5651
5795
  const normalized = rel.replace(/^[\\/]+/, "");
5652
5796
  if (normalized.length === 0 || normalized.startsWith("..")) {
5653
5797
  return {
@@ -5659,7 +5803,7 @@ async function diffProjectSkillsAgainstInitAssets(root, config) {
5659
5803
  changed: []
5660
5804
  };
5661
5805
  }
5662
- templateDir = import_node_path20.default.join(getInitAssetsDir(), normalized);
5806
+ templateDir = import_node_path21.default.join(getInitAssetsDir(), normalized);
5663
5807
  } catch {
5664
5808
  return {
5665
5809
  status: "skipped_missing_assets",
@@ -5712,8 +5856,8 @@ async function diffProjectSkillsAgainstInitAssets(root, config) {
5712
5856
  }
5713
5857
  try {
5714
5858
  const [a, b] = await Promise.all([
5715
- (0, import_promises19.readFile)(templateAbs, "utf-8"),
5716
- (0, import_promises19.readFile)(projectAbs, "utf-8")
5859
+ (0, import_promises20.readFile)(templateAbs, "utf-8"),
5860
+ (0, import_promises20.readFile)(projectAbs, "utf-8")
5717
5861
  ]);
5718
5862
  if (normalizeNewlines(a) !== normalizeNewlines(b)) {
5719
5863
  changed.push(rel);
@@ -5736,7 +5880,7 @@ function normalizeNewlines(text) {
5736
5880
  return text.replace(/\r\n/g, "\n");
5737
5881
  }
5738
5882
  function toRel(base, abs) {
5739
- const rel = import_node_path20.default.relative(base, abs);
5883
+ const rel = import_node_path21.default.relative(base, abs);
5740
5884
  return rel.replace(/[\\/]+/g, "/");
5741
5885
  }
5742
5886
  function intersectKeys(a, b) {
@@ -5781,8 +5925,8 @@ async function validateSkillsIntegrity(root, config) {
5781
5925
  }
5782
5926
 
5783
5927
  // src/core/validators/ids.ts
5784
- var import_promises20 = require("fs/promises");
5785
- var import_node_path21 = __toESM(require("path"), 1);
5928
+ var import_promises21 = require("fs/promises");
5929
+ var import_node_path22 = __toESM(require("path"), 1);
5786
5930
  var SC_TAG_RE2 = /^SC-\d{4}-\d{4}$/;
5787
5931
  var AC_ID_RE2 = /\bAC-[A-Za-z0-9_-]+\b/g;
5788
5932
  var CASE_ID_RE = /\b(?:CASE|TC)-[A-Za-z0-9_-]+\b/g;
@@ -5898,7 +6042,7 @@ async function collectSpecDefinitionIds(files, out) {
5898
6042
  }
5899
6043
  async function readSafe5(file) {
5900
6044
  try {
5901
- return await (0, import_promises20.readFile)(file, "utf-8");
6045
+ return await (0, import_promises21.readFile)(file, "utf-8");
5902
6046
  } catch {
5903
6047
  return "";
5904
6048
  }
@@ -5929,23 +6073,23 @@ function recordId(out, id, file) {
5929
6073
  }
5930
6074
  function formatFileList(files, root) {
5931
6075
  return files.map((file) => {
5932
- const relative = import_node_path21.default.relative(root, file);
6076
+ const relative = import_node_path22.default.relative(root, file);
5933
6077
  return relative.length > 0 ? relative : file;
5934
6078
  }).join(", ");
5935
6079
  }
5936
6080
 
5937
6081
  // src/core/validators/reviewArtifacts.ts
5938
- var import_promises21 = require("fs/promises");
5939
- var import_node_path22 = __toESM(require("path"), 1);
6082
+ var import_promises22 = require("fs/promises");
6083
+ var import_node_path23 = __toESM(require("path"), 1);
5940
6084
  var REVIEW_PACK_DIR_RE = /^review-(\d{17})$/i;
5941
6085
  var REVIEWER_FILE_RE = /^R\d+_.+\.md$/i;
5942
6086
  var ALLOWED_TARGET_KINDS = /* @__PURE__ */ new Set(["spec", "discussion"]);
5943
6087
  var ALLOWED_ROSTER_STATUS = /* @__PURE__ */ new Set(["PASS", "FAIL", "NA"]);
5944
6088
  var ALLOWED_OVERALL_STATUS = /* @__PURE__ */ new Set(["PASS", "FAIL"]);
5945
6089
  async function validateReviewArtifacts(root) {
5946
- const reviewRoot = import_node_path22.default.join(root, ".qfai", "review");
6090
+ const reviewRoot = import_node_path23.default.join(root, ".qfai", "review");
5947
6091
  const issues = [];
5948
- const gitignorePath = import_node_path22.default.join(reviewRoot, ".gitignore");
6092
+ const gitignorePath = import_node_path23.default.join(reviewRoot, ".gitignore");
5949
6093
  if (!await isFile(gitignorePath)) {
5950
6094
  issues.push(
5951
6095
  issue(
@@ -5983,8 +6127,8 @@ async function validateReviewArtifacts(root) {
5983
6127
  }
5984
6128
  async function validateReviewPack(reviewPackDir) {
5985
6129
  const issues = [];
5986
- const reviewRequestPath = import_node_path22.default.join(reviewPackDir, "review_request.md");
5987
- const summaryPath = import_node_path22.default.join(reviewPackDir, "summary.json");
6130
+ const reviewRequestPath = import_node_path23.default.join(reviewPackDir, "review_request.md");
6131
+ const summaryPath = import_node_path23.default.join(reviewPackDir, "summary.json");
5988
6132
  if (!await isFile(reviewRequestPath)) {
5989
6133
  issues.push(
5990
6134
  issue(
@@ -6027,7 +6171,7 @@ async function validateReviewPack(reviewPackDir) {
6027
6171
  async function validateSummarySchema(summaryPath) {
6028
6172
  let parsed;
6029
6173
  try {
6030
- parsed = JSON.parse(await (0, import_promises21.readFile)(summaryPath, "utf-8"));
6174
+ parsed = JSON.parse(await (0, import_promises22.readFile)(summaryPath, "utf-8"));
6031
6175
  } catch (error) {
6032
6176
  return [
6033
6177
  issue(
@@ -6111,16 +6255,16 @@ async function validateSummarySchema(summaryPath) {
6111
6255
  async function listReviewPackDirs(reviewRoot) {
6112
6256
  let entries = [];
6113
6257
  try {
6114
- const dirEntries = await (0, import_promises21.readdir)(reviewRoot, { withFileTypes: true });
6258
+ const dirEntries = await (0, import_promises22.readdir)(reviewRoot, { withFileTypes: true });
6115
6259
  entries = dirEntries.filter((entry) => entry.isDirectory() && REVIEW_PACK_DIR_RE.test(entry.name)).map((entry) => entry.name).sort((left, right) => right.localeCompare(left));
6116
6260
  } catch {
6117
6261
  return [];
6118
6262
  }
6119
- return entries.map((name) => import_node_path22.default.join(reviewRoot, name));
6263
+ return entries.map((name) => import_node_path23.default.join(reviewRoot, name));
6120
6264
  }
6121
6265
  async function listReviewerFiles(reviewPackDir) {
6122
6266
  try {
6123
- const entries = await (0, import_promises21.readdir)(reviewPackDir, { withFileTypes: true });
6267
+ const entries = await (0, import_promises22.readdir)(reviewPackDir, { withFileTypes: true });
6124
6268
  return entries.filter((entry) => entry.isFile() && REVIEWER_FILE_RE.test(entry.name)).map((entry) => entry.name).sort((left, right) => left.localeCompare(right));
6125
6269
  } catch {
6126
6270
  return [];
@@ -6128,7 +6272,7 @@ async function listReviewerFiles(reviewPackDir) {
6128
6272
  }
6129
6273
  async function isFile(target) {
6130
6274
  try {
6131
- const stats = await (0, import_promises21.stat)(target);
6275
+ const stats = await (0, import_promises22.stat)(target);
6132
6276
  return stats.isFile();
6133
6277
  } catch {
6134
6278
  return false;
@@ -6164,8 +6308,8 @@ function formatError6(error) {
6164
6308
  }
6165
6309
 
6166
6310
  // src/core/validators/specPack.ts
6167
- var import_promises22 = require("fs/promises");
6168
- var import_node_path23 = __toESM(require("path"), 1);
6311
+ var import_promises23 = require("fs/promises");
6312
+ var import_node_path24 = __toESM(require("path"), 1);
6169
6313
  var LEDGER_REQUIRED_COLUMNS = [
6170
6314
  "trace_id",
6171
6315
  "obj_id",
@@ -6583,7 +6727,7 @@ function validateLayeredNamespace(entry, filePath, ids, prefix) {
6583
6727
  "specPack.layered.namespace",
6584
6728
  mismatched,
6585
6729
  "compatibility",
6586
- `${import_node_path23.default.basename(filePath)} \u306E ${prefix} ID \u3092 spec-${entry.specNumber} \u306B\u5408\u308F\u305B\u3066\u4FEE\u6B63\u3057\u3066\u304F\u3060\u3055\u3044\u3002`
6730
+ `${import_node_path24.default.basename(filePath)} \u306E ${prefix} ID \u3092 spec-${entry.specNumber} \u306B\u5408\u308F\u305B\u3066\u4FEE\u6B63\u3057\u3066\u304F\u3060\u3055\u3044\u3002`
6587
6731
  )
6588
6732
  ];
6589
6733
  }
@@ -6591,7 +6735,7 @@ async function collectExistingLayeredDeltaFiles(entry) {
6591
6735
  const candidates = Array.from(new Set(entry.deltaCandidates));
6592
6736
  const existing = [];
6593
6737
  for (const candidate of candidates) {
6594
- if (!/(?:^|_)delta\.md$/i.test(import_node_path23.default.basename(candidate))) {
6738
+ if (!/(?:^|_)delta\.md$/i.test(import_node_path24.default.basename(candidate))) {
6595
6739
  continue;
6596
6740
  }
6597
6741
  if (await fileExists(candidate)) {
@@ -6602,7 +6746,7 @@ async function collectExistingLayeredDeltaFiles(entry) {
6602
6746
  }
6603
6747
  async function fileExists(target) {
6604
6748
  try {
6605
- await (0, import_promises22.readFile)(target, "utf-8");
6749
+ await (0, import_promises23.readFile)(target, "utf-8");
6606
6750
  return true;
6607
6751
  } catch {
6608
6752
  return false;
@@ -6763,7 +6907,7 @@ async function validateTraceabilityLedger(entry, contractIds) {
6763
6907
  const issues = [];
6764
6908
  let ledgerText;
6765
6909
  try {
6766
- ledgerText = await (0, import_promises22.readFile)(entry.traceabilityLedgerPath, "utf-8");
6910
+ ledgerText = await (0, import_promises23.readFile)(entry.traceabilityLedgerPath, "utf-8");
6767
6911
  } catch {
6768
6912
  return issues;
6769
6913
  }
@@ -7161,10 +7305,10 @@ async function loadExistingRequiredTexts(entry, missingFiles) {
7161
7305
  }
7162
7306
  async function loadLayerPolicy(root, config) {
7163
7307
  const skillsDir = resolvePath(root, config, "skillsDir");
7164
- const assistantRoot = import_node_path23.default.dirname(skillsDir);
7165
- const policyPath = import_node_path23.default.join(assistantRoot, "steering", "test-layers.md");
7308
+ const assistantRoot = import_node_path24.default.dirname(skillsDir);
7309
+ const policyPath = import_node_path24.default.join(assistantRoot, "steering", "test-layers.md");
7166
7310
  try {
7167
- const policyText = await (0, import_promises22.readFile)(policyPath, "utf-8");
7311
+ const policyText = await (0, import_promises23.readFile)(policyPath, "utf-8");
7168
7312
  return {
7169
7313
  tags: resolveAllowedLayerTagsFromPolicy(policyText),
7170
7314
  issues: []
@@ -7272,15 +7416,15 @@ function extractMarkdownSection(text, heading) {
7272
7416
  }
7273
7417
  async function readSafe6(filePath) {
7274
7418
  try {
7275
- return await (0, import_promises22.readFile)(filePath, "utf-8");
7419
+ return await (0, import_promises23.readFile)(filePath, "utf-8");
7276
7420
  } catch {
7277
7421
  return "";
7278
7422
  }
7279
7423
  }
7280
7424
 
7281
7425
  // src/core/validators/traceability.ts
7282
- var import_promises23 = require("fs/promises");
7283
- var import_node_path24 = __toESM(require("path"), 1);
7426
+ var import_promises24 = require("fs/promises");
7427
+ var import_node_path25 = __toESM(require("path"), 1);
7284
7428
  var SC_TAG_RE3 = /^SC-\d{4}-\d{4}$/;
7285
7429
  var SC_TAG_RE_GLOBAL = /\bSC-\d{4}-\d{4}\b/g;
7286
7430
  var SPEC_TAG_RE = /^SPEC-\d{4}$/;
@@ -7297,8 +7441,8 @@ async function validateTraceability(root, config, phase) {
7297
7441
  if (layeredEntries.length === 0) {
7298
7442
  return issues;
7299
7443
  }
7300
- const policiesDir = layeredEntries[0]?.sharedDir ?? import_node_path24.default.join(specsRoot, "_policies");
7301
- const capabilitiesPath = import_node_path24.default.join(policiesDir, "03_Capabilities.md");
7444
+ const policiesDir = layeredEntries[0]?.sharedDir ?? import_node_path25.default.join(specsRoot, "_policies");
7445
+ const capabilitiesPath = import_node_path25.default.join(policiesDir, "03_Capabilities.md");
7302
7446
  const capabilitiesExists = await exists8(capabilitiesPath);
7303
7447
  const capabilitiesText = await readSafe7(capabilitiesPath);
7304
7448
  const capIds = new Set(extractIds(capabilitiesText, "CAP"));
@@ -7379,7 +7523,7 @@ async function validatePoliciesLayerDownRef(policiesDir) {
7379
7523
  "04_Business-flow.md"
7380
7524
  ];
7381
7525
  for (const fileName of targets) {
7382
- const targetPath = import_node_path24.default.join(policiesDir, fileName);
7526
+ const targetPath = import_node_path25.default.join(policiesDir, fileName);
7383
7527
  const text = await readSafe7(targetPath);
7384
7528
  if (text.length === 0) {
7385
7529
  continue;
@@ -7838,14 +7982,14 @@ function cloneGlobal3(re) {
7838
7982
  }
7839
7983
  async function readSafe7(target) {
7840
7984
  try {
7841
- return await (0, import_promises23.readFile)(target, "utf-8");
7985
+ return await (0, import_promises24.readFile)(target, "utf-8");
7842
7986
  } catch {
7843
7987
  return "";
7844
7988
  }
7845
7989
  }
7846
7990
  async function exists8(target) {
7847
7991
  try {
7848
- await (0, import_promises23.access)(target);
7992
+ await (0, import_promises24.access)(target);
7849
7993
  return true;
7850
7994
  } catch {
7851
7995
  return false;
@@ -7853,8 +7997,8 @@ async function exists8(target) {
7853
7997
  }
7854
7998
 
7855
7999
  // src/core/validators/atddCodeTraceability.ts
7856
- var import_promises24 = require("fs/promises");
7857
- var import_node_path25 = __toESM(require("path"), 1);
8000
+ var import_promises25 = require("fs/promises");
8001
+ var import_node_path26 = __toESM(require("path"), 1);
7858
8002
  async function validateAtddCodeTraceability(root, config) {
7859
8003
  const result = await evaluateAtddCodeTraceability(root, config);
7860
8004
  const issues = [];
@@ -7937,7 +8081,7 @@ async function validateAtddCodeTraceability(root, config) {
7937
8081
  "QFAI-ATDD-901",
7938
8082
  `atdd-traceability report \u306E\u51FA\u529B\u306B\u5931\u6557\u3057\u307E\u3057\u305F: ${formatError7(error)}`,
7939
8083
  "warning",
7940
- import_node_path25.default.join(resolvePath(root, config, "outDir"), "atdd-traceability"),
8084
+ import_node_path26.default.join(resolvePath(root, config, "outDir"), "atdd-traceability"),
7941
8085
  "atddCodeTraceability.report"
7942
8086
  )
7943
8087
  );
@@ -8003,8 +8147,8 @@ function buildUnknownIssues(unknown) {
8003
8147
  });
8004
8148
  }
8005
8149
  async function writeAtddTraceabilityReport(root, config, result) {
8006
- const outputDir = import_node_path25.default.join(resolvePath(root, config, "outDir"), "atdd-traceability");
8007
- await (0, import_promises24.mkdir)(outputDir, { recursive: true });
8150
+ const outputDir = import_node_path26.default.join(resolvePath(root, config, "outDir"), "atdd-traceability");
8151
+ await (0, import_promises25.mkdir)(outputDir, { recursive: true });
8008
8152
  const summary = {
8009
8153
  missing: {
8010
8154
  us: result.missing.us,
@@ -8026,13 +8170,13 @@ async function writeAtddTraceabilityReport(root, config, result) {
8026
8170
  globs: result.scan.globs
8027
8171
  }
8028
8172
  };
8029
- await (0, import_promises24.writeFile)(
8030
- import_node_path25.default.join(outputDir, "summary.json"),
8173
+ await (0, import_promises25.writeFile)(
8174
+ import_node_path26.default.join(outputDir, "summary.json"),
8031
8175
  `${JSON.stringify(summary, null, 2)}
8032
8176
  `,
8033
8177
  "utf-8"
8034
8178
  );
8035
- await (0, import_promises24.writeFile)(import_node_path25.default.join(outputDir, "summary.md"), buildSummaryMarkdown(summary), "utf-8");
8179
+ await (0, import_promises25.writeFile)(import_node_path26.default.join(outputDir, "summary.md"), buildSummaryMarkdown(summary), "utf-8");
8036
8180
  }
8037
8181
  function buildSummaryMarkdown(summary) {
8038
8182
  const lines = [];
@@ -8097,8 +8241,8 @@ function formatError7(error) {
8097
8241
  }
8098
8242
 
8099
8243
  // src/core/validators/discussionPack.ts
8100
- var import_promises25 = require("fs/promises");
8101
- var import_node_path26 = __toESM(require("path"), 1);
8244
+ var import_promises26 = require("fs/promises");
8245
+ var import_node_path27 = __toESM(require("path"), 1);
8102
8246
  async function validateDiscussionPackReadiness(root, config) {
8103
8247
  const discussionRoot = resolvePath(root, config, "discussionDir");
8104
8248
  const readiness = await inspectLatestDiscussionPack(discussionRoot);
@@ -8190,7 +8334,7 @@ async function validateDiscussionPackReadiness(root, config) {
8190
8334
  );
8191
8335
  }
8192
8336
  if (readiness.blockingOqIds.length > 0) {
8193
- const oqPath = import_node_path26.default.join(readiness.latestPackDir, "11_OQ-Register.md");
8337
+ const oqPath = import_node_path27.default.join(readiness.latestPackDir, "11_OQ-Register.md");
8194
8338
  issues.push(
8195
8339
  issue(
8196
8340
  "QFAI-DPACK-004",
@@ -8205,7 +8349,7 @@ async function validateDiscussionPackReadiness(root, config) {
8205
8349
  );
8206
8350
  }
8207
8351
  if (readiness.deferredWithoutDetails.length > 0) {
8208
- const deferredPath = import_node_path26.default.join(readiness.latestPackDir, "13_Deferred.md");
8352
+ const deferredPath = import_node_path27.default.join(readiness.latestPackDir, "13_Deferred.md");
8209
8353
  issues.push(
8210
8354
  issue(
8211
8355
  "QFAI-DPACK-007",
@@ -8219,7 +8363,7 @@ async function validateDiscussionPackReadiness(root, config) {
8219
8363
  )
8220
8364
  );
8221
8365
  }
8222
- const storyWorkshopPath = import_node_path26.default.join(readiness.latestPackDir, "03_Story-Workshop.md");
8366
+ const storyWorkshopPath = import_node_path27.default.join(readiness.latestPackDir, "03_Story-Workshop.md");
8223
8367
  const storyWorkshopText = await readSafe8(storyWorkshopPath);
8224
8368
  if (storyWorkshopText !== null && !containsMermaidBlock(storyWorkshopText)) {
8225
8369
  issues.push(
@@ -8242,32 +8386,32 @@ function containsMermaidBlock(text) {
8242
8386
  }
8243
8387
  async function readSafe8(filePath) {
8244
8388
  try {
8245
- const stats = await (0, import_promises25.stat)(filePath);
8389
+ const stats = await (0, import_promises26.stat)(filePath);
8246
8390
  if (!stats.isFile()) {
8247
8391
  return null;
8248
8392
  }
8249
- return await (0, import_promises25.readFile)(filePath, "utf-8");
8393
+ return await (0, import_promises26.readFile)(filePath, "utf-8");
8250
8394
  } catch {
8251
8395
  return null;
8252
8396
  }
8253
8397
  }
8254
8398
 
8255
8399
  // src/core/validators/discussionVisuals.ts
8256
- var import_promises26 = require("fs/promises");
8257
- var import_node_path27 = __toESM(require("path"), 1);
8400
+ var import_promises27 = require("fs/promises");
8401
+ var import_node_path28 = __toESM(require("path"), 1);
8258
8402
  var MERMAID_FENCE_RE = /^\s*(?:`{3,}|~{3,})\s*mermaid\b/im;
8259
8403
  var SCREEN_MOCK_HEADING_RE = /^\s*#{1,6}\s*screen mock(?:\s*[-\u2014]+\s*fallback)?\s*\(html\+css\)\s*$/im;
8260
8404
  var HTML_FENCE_RE = /^\s*(?:`{3,}|~{3,})\s*html\b/im;
8261
8405
  var CSS_FENCE_RE = /^\s*(?:`{3,}|~{3,})\s*css\b/im;
8262
8406
  var UI_HINT_RE = /\b(?:ui|ux|screen|screens|page|pages|layout|wireframe|mock|form|button|frontend|navigation)\b|画面|モック|遷移|フォーム|ボタン/i;
8263
8407
  async function validateDiscussionVisuals(root) {
8264
- const discussionRoot = import_node_path27.default.join(root, ".qfai", "discussion");
8408
+ const discussionRoot = import_node_path28.default.join(root, ".qfai", "discussion");
8265
8409
  const latestPackDir = await findLatestDiscussionPackDir(discussionRoot);
8266
8410
  if (!latestPackDir) {
8267
8411
  return [];
8268
8412
  }
8269
8413
  const issues = [];
8270
- const inceptionPath = import_node_path27.default.join(latestPackDir, "02_Inception-Deck.md");
8414
+ const inceptionPath = import_node_path28.default.join(latestPackDir, "02_Inception-Deck.md");
8271
8415
  const inceptionText = await readSafe9(inceptionPath);
8272
8416
  if (inceptionText !== null && !MERMAID_FENCE_RE.test(inceptionText)) {
8273
8417
  issues.push(
@@ -8283,7 +8427,7 @@ async function validateDiscussionVisuals(root) {
8283
8427
  )
8284
8428
  );
8285
8429
  }
8286
- const storyWorkshopPath = import_node_path27.default.join(latestPackDir, "03_Story-Workshop.md");
8430
+ const storyWorkshopPath = import_node_path28.default.join(latestPackDir, "03_Story-Workshop.md");
8287
8431
  const storyWorkshopText = await readSafe9(storyWorkshopPath);
8288
8432
  const storyWorkshopPlainText = storyWorkshopText === null ? null : stripFencedCodeBlocks(storyWorkshopText);
8289
8433
  if (storyWorkshopText === null || storyWorkshopPlainText === null) {
@@ -8347,14 +8491,14 @@ function escapeRegExp2(value) {
8347
8491
  }
8348
8492
  async function readSafe9(filePath) {
8349
8493
  try {
8350
- return await (0, import_promises26.readFile)(filePath, "utf-8");
8494
+ return await (0, import_promises27.readFile)(filePath, "utf-8");
8351
8495
  } catch {
8352
8496
  return null;
8353
8497
  }
8354
8498
  }
8355
8499
 
8356
8500
  // src/core/validators/densityHints.ts
8357
- var import_promises27 = require("fs/promises");
8501
+ var import_promises28 = require("fs/promises");
8358
8502
  var BR_ID_RE = /\bBR-[A-Za-z0-9-]+\b/g;
8359
8503
  var SCENARIO_RE = /^\s*Scenario(?:\s+Outline)?\s*:/gim;
8360
8504
  var TC_OR_CASE_RE = /\b(?:TC|CASE)-[A-Za-z0-9-]+\b/g;
@@ -8453,19 +8597,19 @@ function isSeparatorRow(line) {
8453
8597
  }
8454
8598
  async function readSafe10(filePath) {
8455
8599
  try {
8456
- return await (0, import_promises27.readFile)(filePath, "utf-8");
8600
+ return await (0, import_promises28.readFile)(filePath, "utf-8");
8457
8601
  } catch {
8458
8602
  return "";
8459
8603
  }
8460
8604
  }
8461
8605
 
8462
8606
  // src/core/validators/importLite.ts
8463
- var import_promises28 = require("fs/promises");
8464
- var import_node_path28 = __toESM(require("path"), 1);
8465
-
8466
- // src/core/validators/layerCoverage.ts
8467
8607
  var import_promises29 = require("fs/promises");
8468
8608
  var import_node_path29 = __toESM(require("path"), 1);
8609
+
8610
+ // src/core/validators/layerCoverage.ts
8611
+ var import_promises30 = require("fs/promises");
8612
+ var import_node_path30 = __toESM(require("path"), 1);
8469
8613
  var ID_PATTERNS = {
8470
8614
  us: /^US-\d{4}(?:-\d{4})?$/,
8471
8615
  ac: /^AC-\d{4}(?:-\d{4})?$/,
@@ -8486,7 +8630,7 @@ async function validateLayerCoverage(root, config) {
8486
8630
  if (layeredEntries.length === 0) {
8487
8631
  return issues;
8488
8632
  }
8489
- const coverageRoot = import_node_path29.default.join(resolvePath(root, config, "outDir"), "specs-coverage");
8633
+ const coverageRoot = import_node_path30.default.join(resolvePath(root, config, "outDir"), "specs-coverage");
8490
8634
  for (const entry of layeredEntries) {
8491
8635
  if (isV1421LayeredEntry(entry)) {
8492
8636
  const coverageResult = await validateV1421Coverage(entry);
@@ -8521,7 +8665,7 @@ async function validateLayerCoverage(root, config) {
8521
8665
  }
8522
8666
  async function validatePlanArtifacts(specsRoot, entries) {
8523
8667
  const issues = [];
8524
- const deprecatedPlanPath = import_node_path29.default.join(specsRoot, "plan.md");
8668
+ const deprecatedPlanPath = import_node_path30.default.join(specsRoot, "plan.md");
8525
8669
  if (await exists5(deprecatedPlanPath)) {
8526
8670
  issues.push(
8527
8671
  issue(
@@ -8537,7 +8681,7 @@ async function validatePlanArtifacts(specsRoot, entries) {
8537
8681
  );
8538
8682
  }
8539
8683
  for (const entry of entries) {
8540
- const planFileName = import_node_path29.default.basename(entry.planPath).toLowerCase();
8684
+ const planFileName = import_node_path30.default.basename(entry.planPath).toLowerCase();
8541
8685
  if (planFileName !== "10_plan.md") {
8542
8686
  continue;
8543
8687
  }
@@ -8918,8 +9062,8 @@ function buildSignalRows(prefix, counts, target) {
8918
9062
  return Array.from(counts.entries()).filter(([, count]) => count <= 1).sort((left, right) => left[0].localeCompare(right[0])).map(([id, count]) => `${prefix} signal: ${id} -> ${count} ${target}`);
8919
9063
  }
8920
9064
  async function writeCoverageReport(coverageRoot, specNumber, snapshot) {
8921
- await (0, import_promises29.mkdir)(coverageRoot, { recursive: true });
8922
- const reportPath = import_node_path29.default.join(coverageRoot, `spec-${specNumber}.md`);
9065
+ await (0, import_promises30.mkdir)(coverageRoot, { recursive: true });
9066
+ const reportPath = import_node_path30.default.join(coverageRoot, `spec-${specNumber}.md`);
8923
9067
  const lines = [];
8924
9068
  lines.push(`# Spec Coverage (spec-${specNumber})`);
8925
9069
  lines.push("");
@@ -8957,14 +9101,14 @@ async function writeCoverageReport(coverageRoot, specNumber, snapshot) {
8957
9101
  }
8958
9102
  }
8959
9103
  lines.push("");
8960
- await (0, import_promises29.writeFile)(reportPath, `${lines.join("\n")}
9104
+ await (0, import_promises30.writeFile)(reportPath, `${lines.join("\n")}
8961
9105
  `, "utf-8");
8962
9106
  }
8963
9107
  function isV1421LayeredEntry(entry) {
8964
9108
  if (entry.layeredStyle === "v1421") {
8965
9109
  return true;
8966
9110
  }
8967
- return import_node_path29.default.extname(entry.examplesPath).toLowerCase() === ".md";
9111
+ return import_node_path30.default.extname(entry.examplesPath).toLowerCase() === ".md";
8968
9112
  }
8969
9113
  function extractHeadings(text) {
8970
9114
  const headings = [];
@@ -9155,8 +9299,8 @@ async function validateExToTcCoverage(examplesPath, testCasesPath) {
9155
9299
  }
9156
9300
 
9157
9301
  // src/core/validators/layeredTraceability.ts
9158
- var import_promises30 = require("fs/promises");
9159
- var import_node_path30 = __toESM(require("path"), 1);
9302
+ var import_promises31 = require("fs/promises");
9303
+ var import_node_path31 = __toESM(require("path"), 1);
9160
9304
  var POLICIES_FILES = [
9161
9305
  "01_Objective.md",
9162
9306
  "02_Initiative.md",
@@ -9205,7 +9349,7 @@ async function validateLayeredTraceability(root, config) {
9205
9349
  }
9206
9350
  const issues = [];
9207
9351
  if (layeredV1417Entries.length > 0) {
9208
- const policiesDir = layeredV1417Entries[0]?.sharedDir ?? import_node_path30.default.join(specsRoot, "_policies");
9352
+ const policiesDir = layeredV1417Entries[0]?.sharedDir ?? import_node_path31.default.join(specsRoot, "_policies");
9209
9353
  issues.push(...await validatePoliciesDownstreamReferences(policiesDir));
9210
9354
  for (const entry of layeredV1417Entries) {
9211
9355
  issues.push(...await validateSpecRootParent(entry));
@@ -9246,7 +9390,7 @@ async function validateLayeredTraceability(root, config) {
9246
9390
  }
9247
9391
  }
9248
9392
  if (layeredV1421Entries.length > 0) {
9249
- const policiesDir = layeredV1421Entries[0]?.sharedDir ?? import_node_path30.default.join(specsRoot, "_policies");
9393
+ const policiesDir = layeredV1421Entries[0]?.sharedDir ?? import_node_path31.default.join(specsRoot, "_policies");
9250
9394
  issues.push(...await validatePoliciesScopeForV1421(policiesDir));
9251
9395
  for (const entry of layeredV1421Entries) {
9252
9396
  issues.push(...await validateDownstreamRefsForV1421(entry));
@@ -9257,7 +9401,7 @@ async function validateLayeredTraceability(root, config) {
9257
9401
  async function validatePoliciesDownstreamReferences(policiesDir) {
9258
9402
  const issues = [];
9259
9403
  for (const fileName of POLICIES_FILES) {
9260
- const filePath = import_node_path30.default.join(policiesDir, fileName);
9404
+ const filePath = import_node_path31.default.join(policiesDir, fileName);
9261
9405
  const text = await readSafe(filePath);
9262
9406
  if (text.trim().length === 0) {
9263
9407
  continue;
@@ -9330,7 +9474,7 @@ async function validateDownstreamRefsForV1421(entry) {
9330
9474
  issues.push(
9331
9475
  issue(
9332
9476
  "TRACE_DOWNSTREAM_REF",
9333
- `${import_node_path30.default.basename(check.filePath)} \u3067\u4E0B\u4F4D\u30EC\u30A4\u30E4\u30FC\u53C2\u7167\u306F\u7981\u6B62\u3067\u3059: ${refs.join(", ")}`,
9477
+ `${import_node_path31.default.basename(check.filePath)} \u3067\u4E0B\u4F4D\u30EC\u30A4\u30E4\u30FC\u53C2\u7167\u306F\u7981\u6B62\u3067\u3059: ${refs.join(", ")}`,
9334
9478
  "error",
9335
9479
  check.filePath,
9336
9480
  "layeredTraceability.downstream",
@@ -9359,14 +9503,14 @@ function resolveLayerFromId(id) {
9359
9503
  }
9360
9504
  async function collectMarkdownFiles(policiesDir) {
9361
9505
  try {
9362
- const entries = await (0, import_promises30.readdir)(policiesDir, { withFileTypes: true });
9363
- return entries.filter((entry) => entry.isFile() && import_node_path30.default.extname(entry.name).toLowerCase() === ".md").map((entry) => import_node_path30.default.join(policiesDir, entry.name)).sort((left, right) => left.localeCompare(right));
9506
+ const entries = await (0, import_promises31.readdir)(policiesDir, { withFileTypes: true });
9507
+ return entries.filter((entry) => entry.isFile() && import_node_path31.default.extname(entry.name).toLowerCase() === ".md").map((entry) => import_node_path31.default.join(policiesDir, entry.name)).sort((left, right) => left.localeCompare(right));
9364
9508
  } catch {
9365
9509
  return [];
9366
9510
  }
9367
9511
  }
9368
9512
  async function validateSpecRootParent(entry) {
9369
- const specPath = import_node_path30.default.join(entry.dir, "01_Spec.md");
9513
+ const specPath = import_node_path31.default.join(entry.dir, "01_Spec.md");
9370
9514
  const text = await readSafe(specPath);
9371
9515
  if (text.trim().length > 0 && /\bCAP-\d{4}\b/.test(text)) {
9372
9516
  return [];
@@ -9466,11 +9610,11 @@ async function validateForbiddenRefs(filePath, pattern, messagePrefix) {
9466
9610
  }
9467
9611
 
9468
9612
  // src/core/validators/legacyStatusDir.ts
9469
- var import_promises31 = require("fs/promises");
9470
- var import_node_path31 = __toESM(require("path"), 1);
9613
+ var import_promises32 = require("fs/promises");
9614
+ var import_node_path32 = __toESM(require("path"), 1);
9471
9615
  var BASELINE_STATUS_ARTIFACTS = /* @__PURE__ */ new Set(["README.md", ".gitignore"]);
9472
9616
  async function validateLegacyStatusDir(root) {
9473
- const statusDir = import_node_path31.default.join(root, ".qfai", "status");
9617
+ const statusDir = import_node_path32.default.join(root, ".qfai", "status");
9474
9618
  if (!await isDirectory(statusDir)) {
9475
9619
  return [];
9476
9620
  }
@@ -9507,7 +9651,7 @@ async function validateLegacyStatusDir(root) {
9507
9651
  }
9508
9652
  async function listStatusArtifacts(statusDir) {
9509
9653
  try {
9510
- const entries = await (0, import_promises31.readdir)(statusDir, { withFileTypes: true });
9654
+ const entries = await (0, import_promises32.readdir)(statusDir, { withFileTypes: true });
9511
9655
  return entries.map((entry) => entry.isDirectory() ? `${entry.name}/` : entry.name).sort((left, right) => left.localeCompare(right));
9512
9656
  } catch {
9513
9657
  return [];
@@ -9515,22 +9659,22 @@ async function listStatusArtifacts(statusDir) {
9515
9659
  }
9516
9660
  async function isDirectory(target) {
9517
9661
  try {
9518
- return (await (0, import_promises31.stat)(target)).isDirectory();
9662
+ return (await (0, import_promises32.stat)(target)).isDirectory();
9519
9663
  } catch {
9520
9664
  return false;
9521
9665
  }
9522
9666
  }
9523
9667
 
9524
9668
  // src/core/validators/mermaidEnforcement.ts
9525
- var import_promises32 = require("fs/promises");
9526
- var import_node_path32 = __toESM(require("path"), 1);
9669
+ var import_promises33 = require("fs/promises");
9670
+ var import_node_path33 = __toESM(require("path"), 1);
9527
9671
  var TARGETS = [
9528
9672
  { segments: [".qfai", "specs"], extensions: [".md", ".feature"] },
9529
9673
  { segments: [".qfai", "discussion"], extensions: [".md"] }
9530
9674
  ];
9531
9675
  var BUSINESS_FLOW_RELATIVE_CANDIDATES = [
9532
- import_node_path32.default.join(".qfai", "specs", "_policies", "04_Business-Flow.md"),
9533
- import_node_path32.default.join(".qfai", "specs", "_policies", "04_Business-flow.md")
9676
+ import_node_path33.default.join(".qfai", "specs", "_policies", "04_Business-Flow.md"),
9677
+ import_node_path33.default.join(".qfai", "specs", "_policies", "04_Business-flow.md")
9534
9678
  ];
9535
9679
  var MERMAID_DIRECTIVE_RE = /^\s*(?:sequenceDiagram|flowchart|erDiagram|classDiagram|stateDiagram(?:-v2)?|journey|gantt|graph\s+(?:TB|BT|RL|LR|TD))\b/i;
9536
9680
  var FLOW_OR_SEQUENCE_RE2 = /\b(?:sequenceDiagram|flowchart)\b/i;
@@ -9540,13 +9684,13 @@ async function validateMermaidEnforcement(root) {
9540
9684
  const issues = [];
9541
9685
  const scanResults = /* @__PURE__ */ new Map();
9542
9686
  for (const filePath of targetFiles) {
9543
- const text = await (0, import_promises32.readFile)(filePath, "utf-8");
9687
+ const text = await (0, import_promises33.readFile)(filePath, "utf-8");
9544
9688
  const result = scanMermaidUsage(filePath, text);
9545
9689
  scanResults.set(filePath, result);
9546
9690
  issues.push(...result.issues);
9547
9691
  }
9548
9692
  if (businessFlowPath) {
9549
- const businessFlowScan = scanResults.get(businessFlowPath) ?? scanMermaidUsage(businessFlowPath, await (0, import_promises32.readFile)(businessFlowPath, "utf-8"));
9693
+ const businessFlowScan = scanResults.get(businessFlowPath) ?? scanMermaidUsage(businessFlowPath, await (0, import_promises33.readFile)(businessFlowPath, "utf-8"));
9550
9694
  if (businessFlowScan.mermaidFenceCount === 0) {
9551
9695
  issues.push(
9552
9696
  issue(
@@ -9574,7 +9718,7 @@ async function validateMermaidEnforcement(root) {
9574
9718
  )
9575
9719
  );
9576
9720
  }
9577
- if (import_node_path32.default.basename(businessFlowPath) === "04_Business-flow.md") {
9721
+ if (import_node_path33.default.basename(businessFlowPath) === "04_Business-flow.md") {
9578
9722
  issues.push(
9579
9723
  issue(
9580
9724
  "QFAI-MMD-005",
@@ -9595,14 +9739,14 @@ async function validateMermaidEnforcement(root) {
9595
9739
  async function collectTargetFiles(root) {
9596
9740
  const files = [];
9597
9741
  for (const target of TARGETS) {
9598
- const targetDir = import_node_path32.default.join(root, ...target.segments);
9742
+ const targetDir = import_node_path33.default.join(root, ...target.segments);
9599
9743
  files.push(
9600
9744
  ...await collectFiles(targetDir, {
9601
9745
  extensions: [...target.extensions]
9602
9746
  })
9603
9747
  );
9604
9748
  }
9605
- return Array.from(new Set(files)).filter((filePath) => import_node_path32.default.basename(filePath).toLowerCase() !== "readme.md").sort((a, b) => a.localeCompare(b));
9749
+ return Array.from(new Set(files)).filter((filePath) => import_node_path33.default.basename(filePath).toLowerCase() !== "readme.md").sort((a, b) => a.localeCompare(b));
9606
9750
  }
9607
9751
  function scanMermaidUsage(filePath, text) {
9608
9752
  const lines = text.replace(/\r\n/g, "\n").split("\n");
@@ -9692,13 +9836,13 @@ function parseFenceStart(line) {
9692
9836
  };
9693
9837
  }
9694
9838
  async function collectDeprecatedBusinessFlowFeatureWarnings(root) {
9695
- const policiesDir = import_node_path32.default.join(root, ".qfai", "specs", "_policies");
9839
+ const policiesDir = import_node_path33.default.join(root, ".qfai", "specs", "_policies");
9696
9840
  const featureFiles = await collectFiles(policiesDir, {
9697
9841
  extensions: [".feature"]
9698
9842
  });
9699
9843
  const issues = [];
9700
9844
  for (const filePath of featureFiles) {
9701
- if (!/business-flow/i.test(import_node_path32.default.basename(filePath))) {
9845
+ if (!/business-flow/i.test(import_node_path33.default.basename(filePath))) {
9702
9846
  continue;
9703
9847
  }
9704
9848
  issues.push(
@@ -9718,7 +9862,7 @@ async function collectDeprecatedBusinessFlowFeatureWarnings(root) {
9718
9862
  }
9719
9863
  async function resolveBusinessFlowPath(root) {
9720
9864
  for (const relativePath of BUSINESS_FLOW_RELATIVE_CANDIDATES) {
9721
- const target = import_node_path32.default.join(root, relativePath);
9865
+ const target = import_node_path33.default.join(root, relativePath);
9722
9866
  if (await exists5(target)) {
9723
9867
  return target;
9724
9868
  }
@@ -9856,7 +10000,7 @@ function normalizeHeaderKey(column) {
9856
10000
  }
9857
10001
 
9858
10002
  // src/core/validators/orphanProhibition.ts
9859
- var import_node_path33 = __toESM(require("path"), 1);
10003
+ var import_node_path34 = __toESM(require("path"), 1);
9860
10004
  async function validateOrphanProhibition(root, config) {
9861
10005
  const specsRoot = resolvePath(root, config, "specsDir");
9862
10006
  const entries = await collectSpecEntries(specsRoot);
@@ -9867,9 +10011,9 @@ async function validateOrphanProhibition(root, config) {
9867
10011
  return [];
9868
10012
  }
9869
10013
  const issues = [];
9870
- const policiesDir = layeredEntries[0]?.sharedDir ?? import_node_path33.default.join(specsRoot, "_policies");
10014
+ const policiesDir = layeredEntries[0]?.sharedDir ?? import_node_path34.default.join(specsRoot, "_policies");
9871
10015
  const capIds = new Set(
9872
- uniqueMatches(await readSafe(import_node_path33.default.join(policiesDir, "03_Capabilities.md")), /\bCAP-\d{4}\b/g)
10016
+ uniqueMatches(await readSafe(import_node_path34.default.join(policiesDir, "03_Capabilities.md")), /\bCAP-\d{4}\b/g)
9873
10017
  );
9874
10018
  for (const entry of layeredEntries) {
9875
10019
  const usItems = collectMarkdownItems(await readSafe(entry.userStoriesPath), "US");
@@ -10019,8 +10163,8 @@ function validateExParentExists(filePath, exItems, acIds, brIds) {
10019
10163
  }
10020
10164
 
10021
10165
  // src/core/validators/prototypingEvidence.ts
10022
- var import_promises33 = require("fs/promises");
10023
- var import_node_path34 = __toESM(require("path"), 1);
10166
+ var import_promises34 = require("fs/promises");
10167
+ var import_node_path35 = __toESM(require("path"), 1);
10024
10168
  var EVIDENCE_MARKDOWN_FILE = "prototyping.md";
10025
10169
  var EVIDENCE_JSON_FILE = "prototyping.json";
10026
10170
  async function validatePrototypingEvidence(root, config) {
@@ -10029,10 +10173,10 @@ async function validatePrototypingEvidence(root, config) {
10029
10173
  if (specEntries.length === 0) {
10030
10174
  return [];
10031
10175
  }
10032
- const qfaiRoot = import_node_path34.default.dirname(specsRoot);
10033
- const evidenceRoot = import_node_path34.default.join(qfaiRoot, "evidence");
10034
- const evidenceMarkdownPath = import_node_path34.default.join(evidenceRoot, EVIDENCE_MARKDOWN_FILE);
10035
- const evidenceJsonPath = import_node_path34.default.join(evidenceRoot, EVIDENCE_JSON_FILE);
10176
+ const qfaiRoot = import_node_path35.default.dirname(specsRoot);
10177
+ const evidenceRoot = import_node_path35.default.join(qfaiRoot, "evidence");
10178
+ const evidenceMarkdownPath = import_node_path35.default.join(evidenceRoot, EVIDENCE_MARKDOWN_FILE);
10179
+ const evidenceJsonPath = import_node_path35.default.join(evidenceRoot, EVIDENCE_JSON_FILE);
10036
10180
  const [markdownRaw, jsonRaw] = await Promise.all([
10037
10181
  readSafe11(evidenceMarkdownPath),
10038
10182
  readSafe11(evidenceJsonPath)
@@ -10209,7 +10353,7 @@ function extractSpecRefs(rows) {
10209
10353
  }
10210
10354
  async function readSafe11(filePath) {
10211
10355
  try {
10212
- return await (0, import_promises33.readFile)(filePath, "utf-8");
10356
+ return await (0, import_promises34.readFile)(filePath, "utf-8");
10213
10357
  } catch {
10214
10358
  return null;
10215
10359
  }
@@ -10365,7 +10509,7 @@ async function validateUiFidelity(root, config, evidenceJsonPath, evidence) {
10365
10509
  for (const screen of uiFidelity.screens) {
10366
10510
  const contractFiles = contractIndex.idToFiles.get(screen.uiContractId);
10367
10511
  const contractFile = contractFiles ? Array.from(contractFiles).sort((left, right) => left.localeCompare(right))[0] : void 0;
10368
- const contractRefFile = contractFile ? toPosixPath3(import_node_path34.default.relative(root, contractFile)) : void 0;
10512
+ const contractRefFile = contractFile ? toPosixPath3(import_node_path35.default.relative(root, contractFile)) : void 0;
10369
10513
  if (!contractFiles || contractFiles.size === 0) {
10370
10514
  mismatches.push({
10371
10515
  contractId: screen.uiContractId,
@@ -11174,9 +11318,9 @@ async function collectMissingRenderArtifacts(root, render) {
11174
11318
  { label: "htmlPath", target: render.htmlPath }
11175
11319
  ];
11176
11320
  for (const candidate of candidates) {
11177
- const resolved = import_node_path34.default.isAbsolute(candidate.target) ? candidate.target : import_node_path34.default.resolve(root, candidate.target);
11321
+ const resolved = import_node_path35.default.isAbsolute(candidate.target) ? candidate.target : import_node_path35.default.resolve(root, candidate.target);
11178
11322
  try {
11179
- await (0, import_promises33.access)(resolved);
11323
+ await (0, import_promises34.access)(resolved);
11180
11324
  } catch {
11181
11325
  missing.push(candidate.label);
11182
11326
  }
@@ -11197,12 +11341,12 @@ function formatError9(error) {
11197
11341
  }
11198
11342
 
11199
11343
  // src/core/validators/requireIndex.ts
11200
- var import_promises34 = require("fs/promises");
11201
- var import_node_path35 = __toESM(require("path"), 1);
11202
-
11203
- // src/core/validators/repositoryHygiene.ts
11204
11344
  var import_promises35 = require("fs/promises");
11205
11345
  var import_node_path36 = __toESM(require("path"), 1);
11346
+
11347
+ // src/core/validators/repositoryHygiene.ts
11348
+ var import_promises36 = require("fs/promises");
11349
+ var import_node_path37 = __toESM(require("path"), 1);
11206
11350
  var LEGACY_DIR_RULES = [
11207
11351
  { legacy: "discussions", canonical: "discussion" },
11208
11352
  { legacy: "discuss", canonical: "discussion" },
@@ -11213,11 +11357,11 @@ var LEGACY_DIR_RULES = [
11213
11357
  ];
11214
11358
  var SUSPICIOUS_TEMPLATE_NAME_RE = /^(?:_?templates?|_?sample(?:s)?|sample-template)$/i;
11215
11359
  async function validateRepositoryHygiene(root, config) {
11216
- const qfaiRoot = import_node_path36.default.join(root, ".qfai");
11360
+ const qfaiRoot = import_node_path37.default.join(root, ".qfai");
11217
11361
  const specsRoot = resolvePath(root, config, "specsDir");
11218
11362
  const issues = [];
11219
11363
  for (const rule of LEGACY_DIR_RULES) {
11220
- const legacyPath = import_node_path36.default.join(qfaiRoot, rule.legacy);
11364
+ const legacyPath = import_node_path37.default.join(qfaiRoot, rule.legacy);
11221
11365
  if (!await isDirectory2(legacyPath)) {
11222
11366
  continue;
11223
11367
  }
@@ -11264,7 +11408,7 @@ async function collectSuspiciousTemplatePaths(root) {
11264
11408
  }
11265
11409
  let entries = [];
11266
11410
  try {
11267
- entries = await (0, import_promises35.readdir)(current, {
11411
+ entries = await (0, import_promises36.readdir)(current, {
11268
11412
  withFileTypes: true,
11269
11413
  encoding: "utf8"
11270
11414
  });
@@ -11272,10 +11416,10 @@ async function collectSuspiciousTemplatePaths(root) {
11272
11416
  continue;
11273
11417
  }
11274
11418
  for (const entry of entries) {
11275
- const absolute = import_node_path36.default.join(current, entry.name);
11419
+ const absolute = import_node_path37.default.join(current, entry.name);
11276
11420
  if (entry.isDirectory()) {
11277
11421
  if (SUSPICIOUS_TEMPLATE_NAME_RE.test(entry.name)) {
11278
- matches.push(toPosix(import_node_path36.default.relative(root, absolute)));
11422
+ matches.push(toPosix(import_node_path37.default.relative(root, absolute)));
11279
11423
  }
11280
11424
  queue.push(absolute);
11281
11425
  continue;
@@ -11283,9 +11427,9 @@ async function collectSuspiciousTemplatePaths(root) {
11283
11427
  if (!entry.isFile()) {
11284
11428
  continue;
11285
11429
  }
11286
- const baseName = import_node_path36.default.parse(entry.name).name;
11430
+ const baseName = import_node_path37.default.parse(entry.name).name;
11287
11431
  if (SUSPICIOUS_TEMPLATE_NAME_RE.test(baseName)) {
11288
- matches.push(toPosix(import_node_path36.default.relative(root, absolute)));
11432
+ matches.push(toPosix(import_node_path37.default.relative(root, absolute)));
11289
11433
  }
11290
11434
  }
11291
11435
  }
@@ -11293,7 +11437,7 @@ async function collectSuspiciousTemplatePaths(root) {
11293
11437
  }
11294
11438
  async function isDirectory2(target) {
11295
11439
  try {
11296
- return (await (0, import_promises35.stat)(target)).isDirectory();
11440
+ return (await (0, import_promises36.stat)(target)).isDirectory();
11297
11441
  } catch {
11298
11442
  return false;
11299
11443
  }
@@ -11303,7 +11447,7 @@ function toPosix(value) {
11303
11447
  }
11304
11448
 
11305
11449
  // src/core/validators/specSplitByCapability.ts
11306
- var import_node_path37 = __toESM(require("path"), 1);
11450
+ var import_node_path38 = __toESM(require("path"), 1);
11307
11451
  var CAP_ID_RE2 = /\bCAP-\d{4}\b/g;
11308
11452
  async function validateSpecSplitByCapability(root, config) {
11309
11453
  const specsRoot = resolvePath(root, config, "specsDir");
@@ -11314,8 +11458,8 @@ async function validateSpecSplitByCapability(root, config) {
11314
11458
  if (layeredEntries.length === 0) {
11315
11459
  return [];
11316
11460
  }
11317
- const policiesDir = layeredEntries[0]?.sharedDir ?? import_node_path37.default.join(specsRoot, "_policies");
11318
- const capabilitiesPath = import_node_path37.default.join(policiesDir, "03_Capabilities.md");
11461
+ const policiesDir = layeredEntries[0]?.sharedDir ?? import_node_path38.default.join(specsRoot, "_policies");
11462
+ const capabilitiesPath = import_node_path38.default.join(policiesDir, "03_Capabilities.md");
11319
11463
  const capabilityText = await readSafe(capabilitiesPath);
11320
11464
  const issues = [];
11321
11465
  if (!await exists5(capabilitiesPath)) {
@@ -11355,7 +11499,7 @@ async function validateSpecSplitByCapability(root, config) {
11355
11499
  );
11356
11500
  }
11357
11501
  const actualSpecIds = new Set(
11358
- layeredEntries.map((entry) => import_node_path37.default.basename(entry.dir).toLowerCase())
11502
+ layeredEntries.map((entry) => import_node_path38.default.basename(entry.dir).toLowerCase())
11359
11503
  );
11360
11504
  const expectedSpecIds = capIds.map((_, index) => `spec-${to4(index + 1)}`);
11361
11505
  const missingSpecIds = expectedSpecIds.filter((specId) => !actualSpecIds.has(specId));
@@ -11392,11 +11536,11 @@ async function validateSpecSplitByCapability(root, config) {
11392
11536
  continue;
11393
11537
  }
11394
11538
  const specId = `spec-${to4(index + 1)}`;
11395
- const entry = layeredEntries.find((value) => import_node_path37.default.basename(value.dir).toLowerCase() === specId);
11539
+ const entry = layeredEntries.find((value) => import_node_path38.default.basename(value.dir).toLowerCase() === specId);
11396
11540
  if (!entry) {
11397
11541
  continue;
11398
11542
  }
11399
- const specFilePath = import_node_path37.default.join(entry.dir, "01_Spec.md");
11543
+ const specFilePath = import_node_path38.default.join(entry.dir, "01_Spec.md");
11400
11544
  const specText = await readSafe(specFilePath);
11401
11545
  if (specText.trim().length === 0 || !specText.includes(capId)) {
11402
11546
  issues.push(
@@ -11415,8 +11559,8 @@ async function validateSpecSplitByCapability(root, config) {
11415
11559
  }
11416
11560
 
11417
11561
  // src/core/validators/statusInSpecs.ts
11418
- var import_promises36 = require("fs/promises");
11419
- var import_node_path38 = __toESM(require("path"), 1);
11562
+ var import_promises37 = require("fs/promises");
11563
+ var import_node_path39 = __toESM(require("path"), 1);
11420
11564
  var STRONG_PATTERNS = [
11421
11565
  { label: "release_candidate:", pattern: /\brelease_candidate\s*:/i },
11422
11566
  { label: "status:", pattern: /^\s*(?:-\s*)?status\s*:/im },
@@ -11473,23 +11617,23 @@ function hasMatch(text, pattern) {
11473
11617
  }
11474
11618
  async function readSafe12(filePath) {
11475
11619
  try {
11476
- return await (0, import_promises36.readFile)(filePath, "utf-8");
11620
+ return await (0, import_promises37.readFile)(filePath, "utf-8");
11477
11621
  } catch {
11478
11622
  return "";
11479
11623
  }
11480
11624
  }
11481
11625
  function isOpenQuestionsFile(filePath) {
11482
- return /open-questions\.md$/i.test(import_node_path38.default.basename(filePath));
11626
+ return /open-questions\.md$/i.test(import_node_path39.default.basename(filePath));
11483
11627
  }
11484
11628
 
11485
11629
  // src/core/validators/designToken.ts
11486
- var import_promises37 = require("fs/promises");
11487
- var import_node_path39 = __toESM(require("path"), 1);
11630
+ var import_promises38 = require("fs/promises");
11631
+ var import_node_path40 = __toESM(require("path"), 1);
11488
11632
  var import_fast_glob2 = __toESM(require("fast-glob"), 1);
11489
- var import_yaml6 = require("yaml");
11633
+ var import_yaml7 = require("yaml");
11490
11634
 
11491
11635
  // src/core/parse/designToken.ts
11492
- var import_yaml5 = require("yaml");
11636
+ var import_yaml6 = require("yaml");
11493
11637
  var REF_PATTERN = /\{([^}]+)\}/g;
11494
11638
  var MAX_RESOLVE_DEPTH = 10;
11495
11639
  function parseDesignToken(yamlContent) {
@@ -11502,7 +11646,7 @@ function parseDesignToken(yamlContent) {
11502
11646
  };
11503
11647
  let parsed;
11504
11648
  try {
11505
- parsed = (0, import_yaml5.parse)(yamlContent);
11649
+ parsed = (0, import_yaml6.parse)(yamlContent);
11506
11650
  } catch (error) {
11507
11651
  const msg = error instanceof Error ? error.message : String(error);
11508
11652
  result.errors.push({ message: `YAML parse error: ${msg}` });
@@ -11531,7 +11675,7 @@ function collectLayer(layer, layerName, target, errors) {
11531
11675
  }
11532
11676
  function flattenTokens(obj, prefix, target, errors) {
11533
11677
  for (const [key, value] of Object.entries(obj)) {
11534
- const path55 = `${prefix}.${key}`;
11678
+ const path58 = `${prefix}.${key}`;
11535
11679
  if (value && typeof value === "object" && !Array.isArray(value)) {
11536
11680
  const record2 = value;
11537
11681
  if ("$value" in record2) {
@@ -11547,9 +11691,9 @@ function flattenTokens(obj, prefix, target, errors) {
11547
11691
  if (typeof record2.platform === "string") {
11548
11692
  token.platform = record2.platform;
11549
11693
  }
11550
- target.set(path55, token);
11694
+ target.set(path58, token);
11551
11695
  } else {
11552
- flattenTokens(record2, path55, target, errors);
11696
+ flattenTokens(record2, path58, target, errors);
11553
11697
  }
11554
11698
  }
11555
11699
  }
@@ -11559,44 +11703,44 @@ function resolveAllReferences(result) {
11559
11703
  for (const [key, val] of result.primitives) allTokens.set(key, val);
11560
11704
  for (const [key, val] of result.semantics) allTokens.set(key, val);
11561
11705
  for (const [key, val] of result.components) allTokens.set(key, val);
11562
- for (const [path55] of allTokens) {
11563
- resolveTokenRef(path55, allTokens, /* @__PURE__ */ new Set(), 0, result);
11706
+ for (const [path58] of allTokens) {
11707
+ resolveTokenRef(path58, allTokens, /* @__PURE__ */ new Set(), 0, result);
11564
11708
  }
11565
11709
  }
11566
- function resolveTokenRef(path55, allTokens, visited, depth, result) {
11567
- if (result.resolved.has(path55)) {
11568
- return result.resolved.get(path55);
11710
+ function resolveTokenRef(path58, allTokens, visited, depth, result) {
11711
+ if (result.resolved.has(path58)) {
11712
+ return result.resolved.get(path58);
11569
11713
  }
11570
11714
  if (depth > MAX_RESOLVE_DEPTH) {
11571
11715
  result.errors.push({
11572
- message: `Max reference depth exceeded at: ${path55}`,
11573
- path: path55
11716
+ message: `Max reference depth exceeded at: ${path58}`,
11717
+ path: path58
11574
11718
  });
11575
11719
  return void 0;
11576
11720
  }
11577
- if (visited.has(path55)) {
11721
+ if (visited.has(path58)) {
11578
11722
  result.errors.push({
11579
- message: `Circular reference detected: ${path55}`,
11580
- path: path55
11723
+ message: `Circular reference detected: ${path58}`,
11724
+ path: path58
11581
11725
  });
11582
11726
  return void 0;
11583
11727
  }
11584
- const token = allTokens.get(path55);
11728
+ const token = allTokens.get(path58);
11585
11729
  if (!token) {
11586
11730
  return void 0;
11587
11731
  }
11588
11732
  if (typeof token.$value !== "string") {
11589
11733
  const rawValue2 = stringifyTokenValue(token.$value);
11590
- result.resolved.set(path55, rawValue2);
11734
+ result.resolved.set(path58, rawValue2);
11591
11735
  return rawValue2;
11592
11736
  }
11593
11737
  const rawValue = stringifyTokenValue(token.$value);
11594
11738
  const refs = [...rawValue.matchAll(REF_PATTERN)];
11595
11739
  if (refs.length === 0) {
11596
- result.resolved.set(path55, rawValue);
11740
+ result.resolved.set(path58, rawValue);
11597
11741
  return rawValue;
11598
11742
  }
11599
- visited.add(path55);
11743
+ visited.add(path58);
11600
11744
  let resolved = rawValue;
11601
11745
  for (const ref of refs) {
11602
11746
  const refPath = ref[1];
@@ -11604,8 +11748,8 @@ function resolveTokenRef(path55, allTokens, visited, depth, result) {
11604
11748
  const refToken = allTokens.get(refPath);
11605
11749
  if (!refToken) {
11606
11750
  result.errors.push({
11607
- message: `Unresolved token reference: {${refPath}} at ${path55}`,
11608
- path: path55
11751
+ message: `Unresolved token reference: {${refPath}} at ${path58}`,
11752
+ path: path58
11609
11753
  });
11610
11754
  continue;
11611
11755
  }
@@ -11614,7 +11758,7 @@ function resolveTokenRef(path55, allTokens, visited, depth, result) {
11614
11758
  resolved = resolved.split(`{${refPath}}`).join(refValue);
11615
11759
  }
11616
11760
  }
11617
- result.resolved.set(path55, resolved);
11761
+ result.resolved.set(path58, resolved);
11618
11762
  return resolved;
11619
11763
  }
11620
11764
  function stringifyTokenValue(value) {
@@ -11669,8 +11813,8 @@ var VALID_TYPES = [
11669
11813
  var VALID_PLATFORMS = ["web", "windows", "mobile-ios", "mobile-android", "cross-platform"];
11670
11814
  async function validateDesignToken(root, config) {
11671
11815
  const configuredDir = config.uiux?.designTokensDir;
11672
- const designDir = configuredDir ? import_node_path39.default.resolve(root, configuredDir) : import_node_path39.default.join(root, config.paths.contractsDir, "design");
11673
- const pattern = import_node_path39.default.posix.join(designDir.replace(/\\/g, "/"), "design-tokens*.yaml");
11816
+ const designDir = configuredDir ? import_node_path40.default.resolve(root, configuredDir) : import_node_path40.default.join(root, config.paths.contractsDir, "design");
11817
+ const pattern = import_node_path40.default.posix.join(designDir.replace(/\\/g, "/"), "design-tokens*.yaml");
11674
11818
  const files = await (0, import_fast_glob2.default)(pattern, {
11675
11819
  absolute: true,
11676
11820
  ignore: ["**/*.schema.yaml", "**/*.schema.yml"]
@@ -11680,11 +11824,11 @@ async function validateDesignToken(root, config) {
11680
11824
  }
11681
11825
  const issues = [];
11682
11826
  for (const filePath of files) {
11683
- const rel = import_node_path39.default.relative(root, filePath).replace(/\\/g, "/");
11827
+ const rel = import_node_path40.default.relative(root, filePath).replace(/\\/g, "/");
11684
11828
  let hasRootObjectError = false;
11685
11829
  let content;
11686
11830
  try {
11687
- content = await (0, import_promises37.readFile)(filePath, "utf-8");
11831
+ content = await (0, import_promises38.readFile)(filePath, "utf-8");
11688
11832
  } catch {
11689
11833
  issues.push(
11690
11834
  issue(
@@ -11698,7 +11842,7 @@ async function validateDesignToken(root, config) {
11698
11842
  continue;
11699
11843
  }
11700
11844
  try {
11701
- const parsed = (0, import_yaml6.parse)(content);
11845
+ const parsed = (0, import_yaml7.parse)(content);
11702
11846
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
11703
11847
  hasRootObjectError = true;
11704
11848
  issues.push(
@@ -11847,8 +11991,8 @@ function normalizePlatform(value) {
11847
11991
  }
11848
11992
 
11849
11993
  // src/core/validators/htmlMock.ts
11850
- var import_promises38 = require("fs/promises");
11851
- var import_node_path40 = __toESM(require("path"), 1);
11994
+ var import_promises39 = require("fs/promises");
11995
+ var import_node_path41 = __toESM(require("path"), 1);
11852
11996
  var import_fast_glob3 = __toESM(require("fast-glob"), 1);
11853
11997
 
11854
11998
  // src/core/uiux/contrastRatio.ts
@@ -12228,19 +12372,19 @@ async function validateHtmlMock(root, platform, config) {
12228
12372
  const startTime = performance.now();
12229
12373
  const budget = config.uiux?.htmlMockTimeout ?? 2e3;
12230
12374
  const patterns = [
12231
- import_node_path40.default.posix.join(root.replace(/\\/g, "/"), config.paths.discussionDir, "**/*.md"),
12232
- import_node_path40.default.posix.join(root.replace(/\\/g, "/"), config.paths.specsDir, "**/*.md")
12375
+ import_node_path41.default.posix.join(root.replace(/\\/g, "/"), config.paths.discussionDir, "**/*.md"),
12376
+ import_node_path41.default.posix.join(root.replace(/\\/g, "/"), config.paths.specsDir, "**/*.md")
12233
12377
  ];
12234
12378
  const files = await (0, import_fast_glob3.default)(patterns, { absolute: true });
12235
12379
  const mockBlocks = [];
12236
12380
  for (const filePath of files) {
12237
12381
  let content;
12238
12382
  try {
12239
- content = await (0, import_promises38.readFile)(filePath, "utf-8");
12383
+ content = await (0, import_promises39.readFile)(filePath, "utf-8");
12240
12384
  } catch {
12241
12385
  continue;
12242
12386
  }
12243
- const rel = import_node_path40.default.relative(root, filePath).replace(/\\/g, "/");
12387
+ const rel = import_node_path41.default.relative(root, filePath).replace(/\\/g, "/");
12244
12388
  for (const block of collectHtmlMockBlocks(content)) {
12245
12389
  mockBlocks.push({ file: rel, html: block.html, rawBlock: block.rawBlock });
12246
12390
  }
@@ -12406,8 +12550,8 @@ async function validateHtmlMock(root, platform, config) {
12406
12550
  }
12407
12551
 
12408
12552
  // src/core/validators/mermaidScreenFlow.ts
12409
- var import_promises39 = require("fs/promises");
12410
- var import_node_path41 = __toESM(require("path"), 1);
12553
+ var import_promises40 = require("fs/promises");
12554
+ var import_node_path42 = __toESM(require("path"), 1);
12411
12555
  var import_fast_glob4 = __toESM(require("fast-glob"), 1);
12412
12556
 
12413
12557
  // src/core/validators/mermaidUtils.ts
@@ -12458,18 +12602,18 @@ var FLOWCHART_RE = /^\s*flowchart\s+(TD|LR|TB|RL|BT)\b/;
12458
12602
  async function validateMermaidScreenFlow(root, config) {
12459
12603
  const issues = [];
12460
12604
  const patterns = [
12461
- import_node_path41.default.posix.join(root.replace(/\\/g, "/"), config.paths.discussionDir, "**/*.md"),
12462
- import_node_path41.default.posix.join(root.replace(/\\/g, "/"), config.paths.specsDir, "**/*.md")
12605
+ import_node_path42.default.posix.join(root.replace(/\\/g, "/"), config.paths.discussionDir, "**/*.md"),
12606
+ import_node_path42.default.posix.join(root.replace(/\\/g, "/"), config.paths.specsDir, "**/*.md")
12463
12607
  ];
12464
12608
  const files = await (0, import_fast_glob4.default)(patterns, { absolute: true });
12465
12609
  for (const filePath of files) {
12466
12610
  let content;
12467
12611
  try {
12468
- content = await (0, import_promises39.readFile)(filePath, "utf-8");
12612
+ content = await (0, import_promises40.readFile)(filePath, "utf-8");
12469
12613
  } catch {
12470
12614
  continue;
12471
12615
  }
12472
- const rel = import_node_path41.default.relative(root, filePath).replace(/\\/g, "/");
12616
+ const rel = import_node_path42.default.relative(root, filePath).replace(/\\/g, "/");
12473
12617
  const blocks = extractFencedCodeBlocks(content);
12474
12618
  for (const block of blocks) {
12475
12619
  if (block.language !== "mermaid") continue;
@@ -12567,10 +12711,10 @@ function parseTransitions(content) {
12567
12711
  }
12568
12712
 
12569
12713
  // src/core/validators/bpApDb.ts
12570
- var import_promises40 = require("fs/promises");
12571
- var import_node_path42 = __toESM(require("path"), 1);
12714
+ var import_promises41 = require("fs/promises");
12715
+ var import_node_path43 = __toESM(require("path"), 1);
12572
12716
  var import_fast_glob5 = __toESM(require("fast-glob"), 1);
12573
- var import_yaml7 = require("yaml");
12717
+ var import_yaml8 = require("yaml");
12574
12718
  var BP_ID_RE = /^BP-\d{4}$/;
12575
12719
  var AP_ID_RE = /^AP-\d{4}$/;
12576
12720
  var VALID_SEVERITIES = ["critical", "major", "minor"];
@@ -12605,9 +12749,9 @@ var AP_REQUIRED_FIELDS = [
12605
12749
  ];
12606
12750
  async function validateBpApDb(root, config) {
12607
12751
  const issues = [];
12608
- const designDir = import_node_path42.default.join(root, config.paths.contractsDir, "design");
12609
- const bpPattern = import_node_path42.default.posix.join(designDir.replace(/\\/g, "/"), "best-practices*.yaml");
12610
- const apPattern = import_node_path42.default.posix.join(designDir.replace(/\\/g, "/"), "anti-patterns*.yaml");
12752
+ const designDir = import_node_path43.default.join(root, config.paths.contractsDir, "design");
12753
+ const bpPattern = import_node_path43.default.posix.join(designDir.replace(/\\/g, "/"), "best-practices*.yaml");
12754
+ const apPattern = import_node_path43.default.posix.join(designDir.replace(/\\/g, "/"), "anti-patterns*.yaml");
12611
12755
  const globOptions = {
12612
12756
  absolute: true,
12613
12757
  ignore: ["**/*.schema.yaml", "**/*.schema.yml"]
@@ -12620,14 +12764,14 @@ async function validateBpApDb(root, config) {
12620
12764
  const seenBpIds = /* @__PURE__ */ new Set();
12621
12765
  const seenApIds = /* @__PURE__ */ new Set();
12622
12766
  for (const filePath of bpFiles) {
12623
- const rel = import_node_path42.default.relative(root, filePath).replace(/\\/g, "/");
12767
+ const rel = import_node_path43.default.relative(root, filePath).replace(/\\/g, "/");
12624
12768
  const entries = await parseRuleFile(filePath, rel, issues);
12625
12769
  for (const entry of entries) {
12626
12770
  validateBpEntry(entry, rel, seenBpIds, issues);
12627
12771
  }
12628
12772
  }
12629
12773
  for (const filePath of apFiles) {
12630
- const rel = import_node_path42.default.relative(root, filePath).replace(/\\/g, "/");
12774
+ const rel = import_node_path43.default.relative(root, filePath).replace(/\\/g, "/");
12631
12775
  const entries = await parseRuleFile(filePath, rel, issues);
12632
12776
  for (const entry of entries) {
12633
12777
  validateApEntry(entry, rel, seenApIds, issues);
@@ -12638,7 +12782,7 @@ async function validateBpApDb(root, config) {
12638
12782
  async function parseRuleFile(filePath, rel, issues) {
12639
12783
  let content;
12640
12784
  try {
12641
- content = await (0, import_promises40.readFile)(filePath, "utf-8");
12785
+ content = await (0, import_promises41.readFile)(filePath, "utf-8");
12642
12786
  } catch {
12643
12787
  issues.push(
12644
12788
  issue("QFAI-BPAP-001", `BP/AP file unreadable: ${rel}`, "error", rel, "bpApDb.readFile")
@@ -12647,7 +12791,7 @@ async function parseRuleFile(filePath, rel, issues) {
12647
12791
  }
12648
12792
  let parsed;
12649
12793
  try {
12650
- parsed = (0, import_yaml7.parse)(content);
12794
+ parsed = (0, import_yaml8.parse)(content);
12651
12795
  } catch (error) {
12652
12796
  issues.push(
12653
12797
  issue(
@@ -12802,8 +12946,8 @@ function toSafeString(value) {
12802
12946
  }
12803
12947
 
12804
12948
  // src/core/validators/platformDetection.ts
12805
- var import_promises41 = require("fs/promises");
12806
- var import_node_path43 = __toESM(require("path"), 1);
12949
+ var import_promises42 = require("fs/promises");
12950
+ var import_node_path44 = __toESM(require("path"), 1);
12807
12951
  var KNOWN_PLATFORMS = ["web", "windows", "mobile-ios", "mobile-android", "cross-platform"];
12808
12952
  async function detectPlatform(root, config, cliPlatform) {
12809
12953
  const issues = [];
@@ -12846,13 +12990,13 @@ async function detectPlatform(root, config, cliPlatform) {
12846
12990
  return { platform: "web", source: "fallback", issues };
12847
12991
  }
12848
12992
  async function inferPlatform(root, issues) {
12849
- if (await exists5(import_node_path43.default.join(root, "pubspec.yaml"))) {
12850
- const hasAndroid = await exists5(import_node_path43.default.join(root, "android"));
12851
- const hasIos = await exists5(import_node_path43.default.join(root, "ios"));
12852
- const hasWeb = await exists5(import_node_path43.default.join(root, "web"));
12853
- const hasWindows = await exists5(import_node_path43.default.join(root, "windows"));
12854
- const hasMacos = await exists5(import_node_path43.default.join(root, "macos"));
12855
- const hasLinux = await exists5(import_node_path43.default.join(root, "linux"));
12993
+ if (await exists5(import_node_path44.default.join(root, "pubspec.yaml"))) {
12994
+ const hasAndroid = await exists5(import_node_path44.default.join(root, "android"));
12995
+ const hasIos = await exists5(import_node_path44.default.join(root, "ios"));
12996
+ const hasWeb = await exists5(import_node_path44.default.join(root, "web"));
12997
+ const hasWindows = await exists5(import_node_path44.default.join(root, "windows"));
12998
+ const hasMacos = await exists5(import_node_path44.default.join(root, "macos"));
12999
+ const hasLinux = await exists5(import_node_path44.default.join(root, "linux"));
12856
13000
  const mobileTargets = [hasAndroid, hasIos].filter(Boolean).length;
12857
13001
  const desktopTargets = [hasWeb, hasWindows, hasMacos, hasLinux].filter(Boolean).length;
12858
13002
  if (mobileTargets + desktopTargets > 1) {
@@ -12872,10 +13016,10 @@ async function inferPlatform(root, issues) {
12872
13016
  }
12873
13017
  return null;
12874
13018
  }
12875
- const pkgJsonPath = import_node_path43.default.join(root, "package.json");
13019
+ const pkgJsonPath = import_node_path44.default.join(root, "package.json");
12876
13020
  if (await exists5(pkgJsonPath)) {
12877
13021
  try {
12878
- const raw = await (0, import_promises41.readFile)(pkgJsonPath, "utf-8");
13022
+ const raw = await (0, import_promises42.readFile)(pkgJsonPath, "utf-8");
12879
13023
  const pkg = JSON.parse(raw);
12880
13024
  const deps = {
12881
13025
  ...typeof pkg.dependencies === "object" && pkg.dependencies !== null ? pkg.dependencies : {},
@@ -12894,8 +13038,8 @@ async function inferPlatform(root, issues) {
12894
13038
  return "cross-platform";
12895
13039
  }
12896
13040
  if ("react-native" in deps) {
12897
- const hasAndroid = await exists5(import_node_path43.default.join(root, "android"));
12898
- const hasIos = await exists5(import_node_path43.default.join(root, "ios"));
13041
+ const hasAndroid = await exists5(import_node_path44.default.join(root, "android"));
13042
+ const hasIos = await exists5(import_node_path44.default.join(root, "ios"));
12899
13043
  if (hasAndroid && hasIos) {
12900
13044
  return "cross-platform";
12901
13045
  }
@@ -12917,15 +13061,15 @@ function normalizePlatformInput(platform) {
12917
13061
  }
12918
13062
 
12919
13063
  // src/core/validators/uiDefinitionConsistency.ts
12920
- var import_promises42 = require("fs/promises");
12921
- var import_node_path44 = __toESM(require("path"), 1);
13064
+ var import_promises43 = require("fs/promises");
13065
+ var import_node_path45 = __toESM(require("path"), 1);
12922
13066
  var import_fast_glob6 = __toESM(require("fast-glob"), 1);
12923
- var import_yaml8 = require("yaml");
13067
+ var import_yaml9 = require("yaml");
12924
13068
  async function validateUiDefinitionConsistency(root, config) {
12925
13069
  const issues = [];
12926
13070
  const configuredDir = config.uiux?.designTokensDir;
12927
- const designDir = configuredDir ? import_node_path44.default.resolve(root, configuredDir) : import_node_path44.default.join(root, config.paths.contractsDir, "design");
12928
- const tokenPattern = import_node_path44.default.posix.join(designDir.replace(/\\/g, "/"), "design-tokens*.yaml");
13071
+ const designDir = configuredDir ? import_node_path45.default.resolve(root, configuredDir) : import_node_path45.default.join(root, config.paths.contractsDir, "design");
13072
+ const tokenPattern = import_node_path45.default.posix.join(designDir.replace(/\\/g, "/"), "design-tokens*.yaml");
12929
13073
  const tokenFiles = await (0, import_fast_glob6.default)(tokenPattern, {
12930
13074
  absolute: true,
12931
13075
  ignore: ["**/*.schema.yaml", "**/*.schema.yml"]
@@ -12933,7 +13077,7 @@ async function validateUiDefinitionConsistency(root, config) {
12933
13077
  const resolvedTokens = /* @__PURE__ */ new Map();
12934
13078
  for (const tokenFile of tokenFiles) {
12935
13079
  try {
12936
- const content = await (0, import_promises42.readFile)(tokenFile, "utf-8");
13080
+ const content = await (0, import_promises43.readFile)(tokenFile, "utf-8");
12937
13081
  const result = parseDesignToken(content);
12938
13082
  for (const [key, val] of result.resolved) {
12939
13083
  resolvedTokens.set(key, val);
@@ -12941,14 +13085,14 @@ async function validateUiDefinitionConsistency(root, config) {
12941
13085
  } catch {
12942
13086
  }
12943
13087
  }
12944
- const uiContractDir = import_node_path44.default.join(root, config.paths.contractsDir, "ui");
12945
- const uiPattern = import_node_path44.default.posix.join(uiContractDir.replace(/\\/g, "/"), "**/*.yaml");
13088
+ const uiContractDir = import_node_path45.default.join(root, config.paths.contractsDir, "ui");
13089
+ const uiPattern = import_node_path45.default.posix.join(uiContractDir.replace(/\\/g, "/"), "**/*.yaml");
12946
13090
  const uiFiles = await (0, import_fast_glob6.default)(uiPattern, { absolute: true });
12947
13091
  const contractScreenIds = /* @__PURE__ */ new Set();
12948
13092
  for (const uiFile of uiFiles) {
12949
13093
  try {
12950
- const content = await (0, import_promises42.readFile)(uiFile, "utf-8");
12951
- const parsed = (0, import_yaml8.parse)(content);
13094
+ const content = await (0, import_promises43.readFile)(uiFile, "utf-8");
13095
+ const parsed = (0, import_yaml9.parse)(content);
12952
13096
  if (parsed && typeof parsed === "object") {
12953
13097
  const screens = parsed.screens;
12954
13098
  if (Array.isArray(screens)) {
@@ -12963,15 +13107,15 @@ async function validateUiDefinitionConsistency(root, config) {
12963
13107
  }
12964
13108
  }
12965
13109
  const mdPatterns = [
12966
- import_node_path44.default.posix.join(root.replace(/\\/g, "/"), config.paths.discussionDir, "**/*.md"),
12967
- import_node_path44.default.posix.join(root.replace(/\\/g, "/"), config.paths.specsDir, "**/*.md")
13110
+ import_node_path45.default.posix.join(root.replace(/\\/g, "/"), config.paths.discussionDir, "**/*.md"),
13111
+ import_node_path45.default.posix.join(root.replace(/\\/g, "/"), config.paths.specsDir, "**/*.md")
12968
13112
  ];
12969
13113
  const mdFiles = await (0, import_fast_glob6.default)(mdPatterns, { absolute: true });
12970
13114
  const mockScreenIds = /* @__PURE__ */ new Set();
12971
13115
  for (const mdFile of mdFiles) {
12972
13116
  try {
12973
- const content = await (0, import_promises42.readFile)(mdFile, "utf-8");
12974
- const rel = import_node_path44.default.relative(root, mdFile).replace(/\\/g, "/");
13117
+ const content = await (0, import_promises43.readFile)(mdFile, "utf-8");
13118
+ const rel = import_node_path45.default.relative(root, mdFile).replace(/\\/g, "/");
12975
13119
  const htmlBlocks = collectHtmlMockBlocks(content);
12976
13120
  if (resolvedTokens.size > 0) {
12977
13121
  for (const htmlBlock of htmlBlocks) {
@@ -13028,8 +13172,8 @@ async function validateUiDefinitionConsistency(root, config) {
13028
13172
  }
13029
13173
 
13030
13174
  // src/core/validators/researchSummary.ts
13031
- var import_promises43 = require("fs/promises");
13032
- var import_node_path45 = __toESM(require("path"), 1);
13175
+ var import_promises44 = require("fs/promises");
13176
+ var import_node_path46 = __toESM(require("path"), 1);
13033
13177
  var import_fast_glob7 = __toESM(require("fast-glob"), 1);
13034
13178
  var RESEARCH_SUMMARY_HEADING_RE = /^#{1,3}\s+Research\s+Summary/im;
13035
13179
  var SOURCE_ENTRY_RE = /^\s*-\s*id:\s*(\S+)/gm;
@@ -13037,17 +13181,17 @@ var REFLECTION_APPLY_RE = /action:\s*apply/i;
13037
13181
  var FULL_DATE_RE = /^\s+published:\s*["']?(\d{4}-\d{2}-\d{2})["']?/m;
13038
13182
  async function validateResearchSummary(root, config) {
13039
13183
  const issues = [];
13040
- const pattern = import_node_path45.default.posix.join(root.replace(/\\/g, "/"), config.paths.discussionDir, "**/*.md");
13184
+ const pattern = import_node_path46.default.posix.join(root.replace(/\\/g, "/"), config.paths.discussionDir, "**/*.md");
13041
13185
  const files = await (0, import_fast_glob7.default)(pattern, { absolute: true });
13042
13186
  for (const filePath of files) {
13043
13187
  let content;
13044
13188
  try {
13045
- content = await (0, import_promises43.readFile)(filePath, "utf-8");
13189
+ content = await (0, import_promises44.readFile)(filePath, "utf-8");
13046
13190
  } catch {
13047
13191
  continue;
13048
13192
  }
13049
13193
  if (!RESEARCH_SUMMARY_HEADING_RE.test(content)) continue;
13050
- const rel = import_node_path45.default.relative(root, filePath).replace(/\\/g, "/");
13194
+ const rel = import_node_path46.default.relative(root, filePath).replace(/\\/g, "/");
13051
13195
  const section = extractResearchSummarySection(content);
13052
13196
  if (!section) continue;
13053
13197
  const sourceEntries = extractSourceEntries(section);
@@ -13278,9 +13422,9 @@ function resolveFreshnessReferenceNow() {
13278
13422
  }
13279
13423
 
13280
13424
  // src/core/validators/agentDefinition.ts
13281
- var import_promises44 = require("fs/promises");
13282
- var import_node_path46 = __toESM(require("path"), 1);
13283
- var import_yaml9 = require("yaml");
13425
+ var import_promises45 = require("fs/promises");
13426
+ var import_node_path47 = __toESM(require("path"), 1);
13427
+ var import_yaml10 = require("yaml");
13284
13428
  var REQUIRED_AGENTS = [
13285
13429
  "uiux-expert.md",
13286
13430
  "design-expert.md",
@@ -13299,13 +13443,13 @@ var REQUIRED_SECTIONS = [
13299
13443
  var REQUIRED_PHASES = ["discussion", "SDD", "prototyping", "ATDD"];
13300
13444
  async function validateAgentDefinition(root, _config) {
13301
13445
  const issues = [];
13302
- const agentsDir = import_node_path46.default.join(root, ".qfai", "assistant", "agents");
13446
+ const agentsDir = import_node_path47.default.join(root, ".qfai", "assistant", "agents");
13303
13447
  if (!await exists5(agentsDir)) {
13304
13448
  return [];
13305
13449
  }
13306
13450
  let anyAgentExists = false;
13307
13451
  for (const agentFile of REQUIRED_AGENTS) {
13308
- if (await exists5(import_node_path46.default.join(agentsDir, agentFile))) {
13452
+ if (await exists5(import_node_path47.default.join(agentsDir, agentFile))) {
13309
13453
  anyAgentExists = true;
13310
13454
  break;
13311
13455
  }
@@ -13314,7 +13458,7 @@ async function validateAgentDefinition(root, _config) {
13314
13458
  return [];
13315
13459
  }
13316
13460
  for (const agentFile of REQUIRED_AGENTS) {
13317
- const filePath = import_node_path46.default.join(agentsDir, agentFile);
13461
+ const filePath = import_node_path47.default.join(agentsDir, agentFile);
13318
13462
  const rel = `.qfai/assistant/agents/${agentFile}`;
13319
13463
  if (!await exists5(filePath)) {
13320
13464
  issues.push(
@@ -13330,7 +13474,7 @@ async function validateAgentDefinition(root, _config) {
13330
13474
  }
13331
13475
  let content;
13332
13476
  try {
13333
- content = await (0, import_promises44.readFile)(filePath, "utf-8");
13477
+ content = await (0, import_promises45.readFile)(filePath, "utf-8");
13334
13478
  } catch {
13335
13479
  issues.push(
13336
13480
  issue(
@@ -13436,11 +13580,11 @@ async function validateAgentDefinition(root, _config) {
13436
13580
  }
13437
13581
  }
13438
13582
  }
13439
- const rosterPath = import_node_path46.default.join(root, ".qfai", "assistant", "steering", "review-roster.yml");
13583
+ const rosterPath = import_node_path47.default.join(root, ".qfai", "assistant", "steering", "review-roster.yml");
13440
13584
  if (await exists5(rosterPath)) {
13441
13585
  try {
13442
- const rosterContent = await (0, import_promises44.readFile)(rosterPath, "utf-8");
13443
- const roster = (0, import_yaml9.parse)(rosterContent);
13586
+ const rosterContent = await (0, import_promises45.readFile)(rosterPath, "utf-8");
13587
+ const roster = (0, import_yaml10.parse)(rosterContent);
13444
13588
  if (roster && typeof roster === "object") {
13445
13589
  const rosterObj = roster;
13446
13590
  const entries = Array.isArray(rosterObj.roster) ? rosterObj.roster : [];
@@ -13484,8 +13628,8 @@ function extractPhaseBody(phaseSection, phaseName) {
13484
13628
  }
13485
13629
 
13486
13630
  // src/core/validators/tddList.ts
13487
- var import_promises45 = require("fs/promises");
13488
- var import_node_path47 = __toESM(require("path"), 1);
13631
+ var import_promises46 = require("fs/promises");
13632
+ var import_node_path48 = __toESM(require("path"), 1);
13489
13633
  var REQUIRED_COLUMNS = [
13490
13634
  "TDD-ID",
13491
13635
  "TC-Refs",
@@ -13499,7 +13643,7 @@ var REQUIRED_COLUMNS = [
13499
13643
  var VALID_STATUSES = /* @__PURE__ */ new Set(["todo", "red", "green", "refactor", "done", "exception"]);
13500
13644
  var TEST_FILE_CHECK_STATUSES = /* @__PURE__ */ new Set(["green", "refactor", "done"]);
13501
13645
  var TDD_ID_FORMAT = /^TDD-\d{4}$/;
13502
- var TDD_LIST_REL_PATH = import_node_path47.default.join("tdd", "test-list.md");
13646
+ var TDD_LIST_REL_PATH = import_node_path48.default.join("tdd", "test-list.md");
13503
13647
  async function validateTddList(root, config) {
13504
13648
  const specsRoot = resolvePath(root, config, "specsDir");
13505
13649
  const entries = await collectSpecEntries(specsRoot);
@@ -13511,8 +13655,8 @@ async function validateTddList(root, config) {
13511
13655
  return issues;
13512
13656
  }
13513
13657
  async function validateSpecTddList(root, specDir, specNumber) {
13514
- const filePath = import_node_path47.default.join(specDir, TDD_LIST_REL_PATH);
13515
- const relPath = import_node_path47.default.relative(root, filePath).replace(/\\/g, "/");
13658
+ const filePath = import_node_path48.default.join(specDir, TDD_LIST_REL_PATH);
13659
+ const relPath = import_node_path48.default.relative(root, filePath).replace(/\\/g, "/");
13516
13660
  const issues = [];
13517
13661
  if (!await exists5(filePath)) {
13518
13662
  issues.push(
@@ -13699,9 +13843,9 @@ async function validateSpecTddList(root, specDir, specNumber) {
13699
13843
  continue;
13700
13844
  }
13701
13845
  const normalized = testFile.replace(/\\/g, "/");
13702
- const resolved = import_node_path47.default.resolve(root, normalized);
13703
- const relative = import_node_path47.default.relative(root, resolved);
13704
- if (import_node_path47.default.isAbsolute(normalized) || import_node_path47.default.win32.isAbsolute(normalized) || relative === ".." || relative.startsWith(".." + import_node_path47.default.sep)) {
13846
+ const resolved = import_node_path48.default.resolve(root, normalized);
13847
+ const relative = import_node_path48.default.relative(root, resolved);
13848
+ if (import_node_path48.default.isAbsolute(normalized) || import_node_path48.default.win32.isAbsolute(normalized) || relative === ".." || relative.startsWith(".." + import_node_path48.default.sep)) {
13705
13849
  issues.push(
13706
13850
  issue(
13707
13851
  "TDDLIST_TEST_FILE_MISSING",
@@ -13715,7 +13859,7 @@ async function validateSpecTddList(root, specDir, specNumber) {
13715
13859
  }
13716
13860
  let isFile2 = false;
13717
13861
  try {
13718
- isFile2 = (await (0, import_promises45.stat)(resolved)).isFile();
13862
+ isFile2 = (await (0, import_promises46.stat)(resolved)).isFile();
13719
13863
  } catch {
13720
13864
  }
13721
13865
  if (!isFile2) {
@@ -13764,11 +13908,11 @@ async function validateSpecTddList(root, specDir, specNumber) {
13764
13908
  }
13765
13909
  async function collectTestCaseIds(specDir) {
13766
13910
  const empty = { knownTcIds: /* @__PURE__ */ new Set(), unitComponentTcIds: /* @__PURE__ */ new Set() };
13767
- const testCasesPath = import_node_path47.default.join(specDir, "06_Test-Cases.md");
13911
+ const testCasesPath = import_node_path48.default.join(specDir, "06_Test-Cases.md");
13768
13912
  if (!await exists5(testCasesPath)) return empty;
13769
13913
  let content;
13770
13914
  try {
13771
- content = await (0, import_promises45.readFile)(testCasesPath, "utf-8");
13915
+ content = await (0, import_promises46.readFile)(testCasesPath, "utf-8");
13772
13916
  } catch {
13773
13917
  return empty;
13774
13918
  }
@@ -13794,8 +13938,8 @@ async function collectTestCaseIds(specDir) {
13794
13938
  }
13795
13939
 
13796
13940
  // src/core/validators/ddpValidation.ts
13797
- var import_promises46 = require("fs/promises");
13798
- var import_node_path48 = __toESM(require("path"), 1);
13941
+ var import_promises47 = require("fs/promises");
13942
+ var import_node_path49 = __toESM(require("path"), 1);
13799
13943
  var import_node_url3 = require("url");
13800
13944
  var import_fast_glob8 = __toESM(require("fast-glob"), 1);
13801
13945
  var DDP_HEADING_RE = /^#{1,3}\s+Design\s+Direction\s+Pack/im;
@@ -13813,9 +13957,9 @@ var _bannedPatterns = null;
13813
13957
  async function loadBannedPatterns() {
13814
13958
  if (_bannedPatterns !== null) return _bannedPatterns;
13815
13959
  try {
13816
- const thisDir = import_node_path48.default.dirname((0, import_node_url3.fileURLToPath)(__filename));
13817
- const filePath = import_node_path48.default.join(thisDir, "ddpBannedPatterns.txt");
13818
- const content = await (0, import_promises46.readFile)(filePath, "utf-8");
13960
+ const thisDir = import_node_path49.default.dirname((0, import_node_url3.fileURLToPath)(__filename));
13961
+ const filePath = import_node_path49.default.join(thisDir, "ddpBannedPatterns.txt");
13962
+ const content = await (0, import_promises47.readFile)(filePath, "utf-8");
13819
13963
  _bannedPatterns = content.split(/\r?\n/).map((l) => l.trim()).filter((l) => l.length > 0 && !l.startsWith("#"));
13820
13964
  } catch {
13821
13965
  _bannedPatterns = [];
@@ -13824,15 +13968,15 @@ async function loadBannedPatterns() {
13824
13968
  }
13825
13969
  async function validateDdpFields(root, config) {
13826
13970
  const issues = [];
13827
- const pattern = import_node_path48.default.posix.join(root.replace(/\\/g, "/"), config.paths.discussionDir, "**/*.md");
13971
+ const pattern = import_node_path49.default.posix.join(root.replace(/\\/g, "/"), config.paths.discussionDir, "**/*.md");
13828
13972
  const files = await (0, import_fast_glob8.default)(pattern, { absolute: true });
13829
13973
  let ddpFound = false;
13830
13974
  let isUiBearing2 = false;
13831
13975
  for (const filePath of files) {
13832
- const basename = import_node_path48.default.basename(filePath);
13976
+ const basename = import_node_path49.default.basename(filePath);
13833
13977
  if (basename === "03_Story-Workshop.md") {
13834
13978
  try {
13835
- const storyContent = await (0, import_promises46.readFile)(filePath, "utf-8");
13979
+ const storyContent = await (0, import_promises47.readFile)(filePath, "utf-8");
13836
13980
  if (UI_BEARING_KEYWORDS_RE.test(storyContent)) {
13837
13981
  isUiBearing2 = true;
13838
13982
  }
@@ -13843,13 +13987,13 @@ async function validateDdpFields(root, config) {
13843
13987
  for (const filePath of files) {
13844
13988
  let content;
13845
13989
  try {
13846
- content = await (0, import_promises46.readFile)(filePath, "utf-8");
13990
+ content = await (0, import_promises47.readFile)(filePath, "utf-8");
13847
13991
  } catch {
13848
13992
  continue;
13849
13993
  }
13850
13994
  if (!DDP_HEADING_RE.test(content)) continue;
13851
13995
  ddpFound = true;
13852
- const rel = import_node_path48.default.relative(root, filePath).replace(/\\/g, "/");
13996
+ const rel = import_node_path49.default.relative(root, filePath).replace(/\\/g, "/");
13853
13997
  const section = extractDdpSection(content);
13854
13998
  if (!section) continue;
13855
13999
  for (const field of DDP_REQUIRED_FIELDS) {
@@ -14042,7 +14186,7 @@ var ANTI_PATTERN_CHECKS = [
14042
14186
  ];
14043
14187
  async function validateResearchTraceability(root, config, issues) {
14044
14188
  const requireResearchSummary = config.uiux?.requireResearchSummary === true;
14045
- const discussionPattern = import_node_path48.default.posix.join(
14189
+ const discussionPattern = import_node_path49.default.posix.join(
14046
14190
  root.replace(/\\/g, "/"),
14047
14191
  config.paths.discussionDir,
14048
14192
  "**/*.md"
@@ -14051,7 +14195,7 @@ async function validateResearchTraceability(root, config, issues) {
14051
14195
  let hasResearchSummary = false;
14052
14196
  for (const filePath of discussionFiles) {
14053
14197
  try {
14054
- const content = await (0, import_promises46.readFile)(filePath, "utf-8");
14198
+ const content = await (0, import_promises47.readFile)(filePath, "utf-8");
14055
14199
  if (/research_summary\s*:/m.test(content)) {
14056
14200
  hasResearchSummary = true;
14057
14201
  break;
@@ -14070,7 +14214,7 @@ async function validateResearchTraceability(root, config, issues) {
14070
14214
  )
14071
14215
  );
14072
14216
  }
14073
- const contractPattern = import_node_path48.default.posix.join(
14217
+ const contractPattern = import_node_path49.default.posix.join(
14074
14218
  root.replace(/\\/g, "/"),
14075
14219
  config.paths.contractsDir,
14076
14220
  "design",
@@ -14080,11 +14224,11 @@ async function validateResearchTraceability(root, config, issues) {
14080
14224
  for (const filePath of contractFiles) {
14081
14225
  let content;
14082
14226
  try {
14083
- content = await (0, import_promises46.readFile)(filePath, "utf-8");
14227
+ content = await (0, import_promises47.readFile)(filePath, "utf-8");
14084
14228
  } catch {
14085
14229
  continue;
14086
14230
  }
14087
- const rel = import_node_path48.default.relative(root, filePath).replace(/\\/g, "/");
14231
+ const rel = import_node_path49.default.relative(root, filePath).replace(/\\/g, "/");
14088
14232
  const ruleBlocks = content.split(/(?=^-\s+(?:rule|id)\s*:)/m).filter((b) => b.trim());
14089
14233
  for (const block of ruleBlocks) {
14090
14234
  const idMatch = /(?:^|\n)\s*(?:-\s+)?id\s*:\s*(\S+)/m.exec(block);
@@ -14107,11 +14251,11 @@ async function validateStoryWorkshopTemplates(root, config, issues, discussionFi
14107
14251
  for (const filePath of discussionFiles) {
14108
14252
  let content;
14109
14253
  try {
14110
- content = await (0, import_promises46.readFile)(filePath, "utf-8");
14254
+ content = await (0, import_promises47.readFile)(filePath, "utf-8");
14111
14255
  } catch {
14112
14256
  continue;
14113
14257
  }
14114
- const rel = import_node_path48.default.relative(root, filePath).replace(/\\/g, "/");
14258
+ const rel = import_node_path49.default.relative(root, filePath).replace(/\\/g, "/");
14115
14259
  if (/screen_type\s*:\s*list/im.test(content)) {
14116
14260
  for (const field of LIST_TEMPLATE_REQUIRED_FIELDS) {
14117
14261
  if (!hasNonEmptyField(content, field)) {
@@ -14211,7 +14355,7 @@ function validateQualityProfile(config, issues) {
14211
14355
  }
14212
14356
  }
14213
14357
  async function validateOptionComparison(root, config, issues) {
14214
- const contractPattern = import_node_path48.default.posix.join(
14358
+ const contractPattern = import_node_path49.default.posix.join(
14215
14359
  root.replace(/\\/g, "/"),
14216
14360
  config.paths.contractsDir,
14217
14361
  "design",
@@ -14221,11 +14365,11 @@ async function validateOptionComparison(root, config, issues) {
14221
14365
  for (const filePath of contractFiles) {
14222
14366
  let content;
14223
14367
  try {
14224
- content = await (0, import_promises46.readFile)(filePath, "utf-8");
14368
+ content = await (0, import_promises47.readFile)(filePath, "utf-8");
14225
14369
  } catch {
14226
14370
  continue;
14227
14371
  }
14228
- const rel = import_node_path48.default.relative(root, filePath).replace(/\\/g, "/");
14372
+ const rel = import_node_path49.default.relative(root, filePath).replace(/\\/g, "/");
14229
14373
  const optionMatches = content.match(/(?:^|\n)\s*-\s+option\s*:/gm) ?? [];
14230
14374
  const numberedOptions = content.match(/(?:^|\n)\s*option_\d+\s*:/gm) ?? [];
14231
14375
  const totalOptions = optionMatches.length + numberedOptions.length;
@@ -14260,7 +14404,7 @@ async function validateOptionComparison(root, config, issues) {
14260
14404
  }
14261
14405
  async function validateCompetitiveRefs(root, config, issues) {
14262
14406
  const minRefs = config.uiux?.competitive_refs_min ?? 3;
14263
- const contractPattern = import_node_path48.default.posix.join(
14407
+ const contractPattern = import_node_path49.default.posix.join(
14264
14408
  root.replace(/\\/g, "/"),
14265
14409
  config.paths.contractsDir,
14266
14410
  "design",
@@ -14270,11 +14414,11 @@ async function validateCompetitiveRefs(root, config, issues) {
14270
14414
  for (const filePath of contractFiles) {
14271
14415
  let content;
14272
14416
  try {
14273
- content = await (0, import_promises46.readFile)(filePath, "utf-8");
14417
+ content = await (0, import_promises47.readFile)(filePath, "utf-8");
14274
14418
  } catch {
14275
14419
  continue;
14276
14420
  }
14277
- const rel = import_node_path48.default.relative(root, filePath).replace(/\\/g, "/");
14421
+ const rel = import_node_path49.default.relative(root, filePath).replace(/\\/g, "/");
14278
14422
  const refsBlock = extractNestedBlock(content, "competitive_refs");
14279
14423
  if (refsBlock !== null) {
14280
14424
  const refItems = refsBlock.split(/\n/).filter((l) => /^\s*-\s/.test(l));
@@ -14424,7 +14568,7 @@ function collectListItems(section, field) {
14424
14568
  }
14425
14569
 
14426
14570
  // src/core/validators/navigationFlow.ts
14427
- var import_promises47 = require("fs/promises");
14571
+ var import_promises48 = require("fs/promises");
14428
14572
  var NODE_DEF_RE = /([A-Za-z_][\w-]*)\s*(?:\[.*?\]|\(.*?\)|\{.*?\})/g;
14429
14573
  var EDGE_RE = /([A-Za-z_][\w-]*)(?:\s*(?:\[[^\]]*\]|\([^)]*\)|\{[^}]*\}))?(?:::\w+)?\s*(?:--+>|==+>|-.->|~~>)\s*(?:\|"?([^"|]*)"?\|)?\s*([A-Za-z_][\w-]*)(?:\s*(?:\[[^\]]*\]|\([^)]*\)|\{[^}]*\}))?(?:::\w+)?/g;
14430
14574
  var SUBGRAPH_RE = /^\s*subgraph\s+([\w-]+)/gm;
@@ -14703,7 +14847,7 @@ async function validateNavigationFlow(root, config) {
14703
14847
  const specFiles = allFiles.filter((f) => /[\\/]spec-\d{4}[\\/]/.test(f));
14704
14848
  const issues = [];
14705
14849
  for (const file of specFiles) {
14706
- const content = await (0, import_promises47.readFile)(file, "utf-8");
14850
+ const content = await (0, import_promises48.readFile)(file, "utf-8");
14707
14851
  const blocks = extractFencedCodeBlocks(content);
14708
14852
  const mermaidBlocks = blocks.filter((b) => b.language === "mermaid");
14709
14853
  const _flowchartBlocks = mermaidBlocks.filter((b) => hasFlowchartDeclaration(b.content));
@@ -14725,8 +14869,8 @@ async function validateNavigationFlow(root, config) {
14725
14869
  }
14726
14870
 
14727
14871
  // src/core/validators/renderCritique.ts
14728
- var import_node_path49 = __toESM(require("path"), 1);
14729
- var import_promises48 = require("fs/promises");
14872
+ var import_node_path50 = __toESM(require("path"), 1);
14873
+ var import_promises49 = require("fs/promises");
14730
14874
  var import_fast_glob9 = __toESM(require("fast-glob"), 1);
14731
14875
  var RENDERED_KEYWORDS_RE = /\b(rendered|screenshot|html\b|preview|visual\s*review)/i;
14732
14876
  var DDP_REFERENCE_RE = /\b(ddp|design\s*direction\s*pack)\b/i;
@@ -14745,8 +14889,8 @@ var FOUR_STATE_CHECK_RE = /\bfour_state_check\s*:/i;
14745
14889
  var MAX_PRIMARY_STEPS_RE = /\bmax_primary_steps\s*:\s*(\d+)/i;
14746
14890
  async function validateRenderCritique(root, config) {
14747
14891
  const issues = [];
14748
- const discussionDir = import_node_path49.default.join(root, config.paths.discussionDir).replace(/\\/g, "/");
14749
- const discussionFiles = await (0, import_fast_glob9.default)(import_node_path49.default.posix.join(discussionDir, "**/*.md"), { absolute: true });
14892
+ const discussionDir = import_node_path50.default.join(root, config.paths.discussionDir).replace(/\\/g, "/");
14893
+ const discussionFiles = await (0, import_fast_glob9.default)(import_node_path50.default.posix.join(discussionDir, "**/*.md"), { absolute: true });
14750
14894
  let hasDdp = false;
14751
14895
  for (const df of discussionFiles) {
14752
14896
  const content = await readSafe(df);
@@ -14756,12 +14900,12 @@ async function validateRenderCritique(root, config) {
14756
14900
  }
14757
14901
  }
14758
14902
  if (!hasDdp) return issues;
14759
- const skillsDir = import_node_path49.default.join(root, config.paths.skillsDir).replace(/\\/g, "/");
14760
- const evidenceDir = import_node_path49.default.join(root, ".qfai", "evidence").replace(/\\/g, "/");
14903
+ const skillsDir = import_node_path50.default.join(root, config.paths.skillsDir).replace(/\\/g, "/");
14904
+ const evidenceDir = import_node_path50.default.join(root, ".qfai", "evidence").replace(/\\/g, "/");
14761
14905
  const renderEvidenceViewports = await collectRenderEvidenceViewports(root);
14762
- const skillPromptPattern = import_node_path49.default.posix.join(skillsDir, "qfai-{prototyping,implement}*/SKILL.md");
14906
+ const skillPromptPattern = import_node_path50.default.posix.join(skillsDir, "qfai-{prototyping,implement}*/SKILL.md");
14763
14907
  const skillFiles = await (0, import_fast_glob9.default)(skillPromptPattern, { dot: true });
14764
- const evidencePattern = import_node_path49.default.posix.join(evidenceDir, "{prototyping*,critique-*}.md");
14908
+ const evidencePattern = import_node_path50.default.posix.join(evidenceDir, "{prototyping*,critique-*}.md");
14765
14909
  const evidenceFiles = await (0, import_fast_glob9.default)(evidencePattern, { dot: true });
14766
14910
  for (const sf of skillFiles) {
14767
14911
  const content = await readSafe(sf);
@@ -14769,7 +14913,7 @@ async function validateRenderCritique(root, config) {
14769
14913
  issues.push(
14770
14914
  issue(
14771
14915
  "QFAI-CRIT-001",
14772
- `Skill prompt does not mention rendered/screenshot/HTML review: ${import_node_path49.default.relative(root, sf)}`,
14916
+ `Skill prompt does not mention rendered/screenshot/HTML review: ${import_node_path50.default.relative(root, sf)}`,
14773
14917
  "error",
14774
14918
  sf,
14775
14919
  "renderCritique.codeOnly",
@@ -14786,7 +14930,7 @@ async function validateRenderCritique(root, config) {
14786
14930
  issues.push(
14787
14931
  issue(
14788
14932
  "QFAI-CRIT-002",
14789
- `Downstream skill prompt missing DDP reference: ${import_node_path49.default.relative(root, sf)}`,
14933
+ `Downstream skill prompt missing DDP reference: ${import_node_path50.default.relative(root, sf)}`,
14790
14934
  "error",
14791
14935
  sf,
14792
14936
  "renderCritique.ddpMissing",
@@ -14832,7 +14976,7 @@ async function validateRenderCritique(root, config) {
14832
14976
  issues.push(
14833
14977
  issue(
14834
14978
  "QFAI-CRIT-005",
14835
- `Read order not specified (DDP \u2192 Design Token \u2192 UI Contract \u2192 HTML Mock \u2192 Flow): ${import_node_path49.default.relative(root, sf)}`,
14979
+ `Read order not specified (DDP \u2192 Design Token \u2192 UI Contract \u2192 HTML Mock \u2192 Flow): ${import_node_path50.default.relative(root, sf)}`,
14836
14980
  "error",
14837
14981
  sf,
14838
14982
  "renderCritique.readOrder",
@@ -14859,7 +15003,7 @@ async function validateRenderCritique(root, config) {
14859
15003
  issues.push(
14860
15004
  issue(
14861
15005
  "QFAI-CRIT-006",
14862
- `Critique evidence incomplete (missing: ${missing.join(", ")}): ${import_node_path49.default.relative(root, ef)}`,
15006
+ `Critique evidence incomplete (missing: ${missing.join(", ")}): ${import_node_path50.default.relative(root, ef)}`,
14863
15007
  "error",
14864
15008
  ef,
14865
15009
  "renderCritique.incompleteEvidence",
@@ -14974,9 +15118,9 @@ async function collectContent(files) {
14974
15118
  return contents.join("\n---\n");
14975
15119
  }
14976
15120
  async function collectRenderEvidenceViewports(root) {
14977
- const prototypingJsonPath = import_node_path49.default.join(root, ".qfai", "evidence", "prototyping.json");
15121
+ const prototypingJsonPath = import_node_path50.default.join(root, ".qfai", "evidence", "prototyping.json");
14978
15122
  try {
14979
- const raw = await (0, import_promises48.readFile)(prototypingJsonPath, "utf-8");
15123
+ const raw = await (0, import_promises49.readFile)(prototypingJsonPath, "utf-8");
14980
15124
  const parsed = JSON.parse(raw);
14981
15125
  if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
14982
15126
  return /* @__PURE__ */ new Set();
@@ -15015,8 +15159,8 @@ async function collectRenderEvidenceViewports(root) {
15015
15159
  }
15016
15160
 
15017
15161
  // src/core/validators/designFidelity.ts
15018
- var import_promises49 = require("fs/promises");
15019
- var import_node_path50 = __toESM(require("path"), 1);
15162
+ var import_promises50 = require("fs/promises");
15163
+ var import_node_path51 = __toESM(require("path"), 1);
15020
15164
  var import_fast_glob10 = __toESM(require("fast-glob"), 1);
15021
15165
  var SCORECARD_HEADING_RE = /^#{1,3}\s+Fidelity\s+Scorecard/im;
15022
15166
  var BASE_DIMENSIONS = ["hierarchy", "clarity", "accessibility", "responsive"];
@@ -15041,19 +15185,19 @@ async function validateDesignFidelity(root, config) {
15041
15185
  const evidenceDirs = [".qfai/evidence", ".qfai/review"];
15042
15186
  const allFiles = [];
15043
15187
  for (const dir of evidenceDirs) {
15044
- const pattern = import_node_path50.default.posix.join(root.replace(/\\/g, "/"), dir, "**/*.md");
15188
+ const pattern = import_node_path51.default.posix.join(root.replace(/\\/g, "/"), dir, "**/*.md");
15045
15189
  const files = await (0, import_fast_glob10.default)(pattern, { absolute: true });
15046
15190
  allFiles.push(...files);
15047
15191
  }
15048
15192
  for (const filePath of allFiles) {
15049
15193
  let content;
15050
15194
  try {
15051
- content = await (0, import_promises49.readFile)(filePath, "utf-8");
15195
+ content = await (0, import_promises50.readFile)(filePath, "utf-8");
15052
15196
  } catch {
15053
15197
  continue;
15054
15198
  }
15055
15199
  if (!SCORECARD_HEADING_RE.test(content)) continue;
15056
- const rel = import_node_path50.default.relative(root, filePath).replace(/\\/g, "/");
15200
+ const rel = import_node_path51.default.relative(root, filePath).replace(/\\/g, "/");
15057
15201
  const section = extractScorecardSection(content);
15058
15202
  if (!section) continue;
15059
15203
  const dimensions = parseDimensions(section);
@@ -15345,7 +15489,7 @@ function hasAntiPatternMention(section, code) {
15345
15489
  }
15346
15490
 
15347
15491
  // src/core/validators/discussionDesignHardening.ts
15348
- var import_node_path51 = __toESM(require("path"), 1);
15492
+ var import_node_path52 = __toESM(require("path"), 1);
15349
15493
  var HTML_TAG_RE = /<(?:style|div|section|span|button|input|form|header|footer|nav|main|aside)\b/i;
15350
15494
  var MERMAID_SCREEN_FLOW_RE = /```mermaid[\s\S]*?(?:stateDiagram|flowchart|graph)[\s\S]*?(?:Screen|Page|View|Dashboard|Login|Settings|Home)\b/i;
15351
15495
  var DDS_HEADING = "## Design Direction Summary";
@@ -15360,10 +15504,17 @@ var DDS_SUBSECTIONS = [
15360
15504
  var REQUIRED_STATES = ["empty", "loading", "error", "populated"];
15361
15505
  var COMPETITIVE_REF_FIELDS = ["adopted_points", "rejected_points", "local_translation"];
15362
15506
  var PLACEHOLDER_RE = /^(?:tbd|todo|n\/a|na|xxx|\?\?\?|placeholder)$/i;
15507
+ var SURFACE_TYPE_RE = /\|\s*Surface Type\s*\|\s*(\S+)\s*\|/i;
15363
15508
  async function isUiBearing(packRoot) {
15364
- const storyPath = import_node_path51.default.join(packRoot, "03_Story-Workshop.md");
15509
+ const storyPath = import_node_path52.default.join(packRoot, "03_Story-Workshop.md");
15365
15510
  const content = await readSafe(storyPath);
15366
15511
  if (!content) return false;
15512
+ const surfaceMatch = SURFACE_TYPE_RE.exec(content);
15513
+ if (surfaceMatch?.[1]) {
15514
+ const surface = surfaceMatch[1].toLowerCase();
15515
+ if (surface === "non-ui") return false;
15516
+ if (["web-ui", "mobile-ui", "desktop-ui", "mixed"].includes(surface)) return true;
15517
+ }
15367
15518
  if (HTML_TAG_RE.test(content)) return true;
15368
15519
  if (MERMAID_SCREEN_FLOW_RE.test(content)) return true;
15369
15520
  return false;
@@ -15398,7 +15549,7 @@ function extractOptionNames(optionSection) {
15398
15549
  }
15399
15550
  async function validateDdsPresence(packRoot) {
15400
15551
  const issues = [];
15401
- const storyPath = import_node_path51.default.join(packRoot, "03_Story-Workshop.md");
15552
+ const storyPath = import_node_path52.default.join(packRoot, "03_Story-Workshop.md");
15402
15553
  const content = await readSafe(storyPath);
15403
15554
  const relPath = "03_Story-Workshop.md";
15404
15555
  if (!content || !content.includes(DDS_HEADING)) {
@@ -15432,7 +15583,7 @@ async function validateDdsPresence(packRoot) {
15432
15583
  }
15433
15584
  async function validateOptionComparison2(packRoot) {
15434
15585
  const issues = [];
15435
- const storyPath = import_node_path51.default.join(packRoot, "03_Story-Workshop.md");
15586
+ const storyPath = import_node_path52.default.join(packRoot, "03_Story-Workshop.md");
15436
15587
  const content = await readSafe(storyPath);
15437
15588
  if (!content) return issues;
15438
15589
  const dds = extractDdsSection(content);
@@ -15456,7 +15607,7 @@ async function validateOptionComparison2(packRoot) {
15456
15607
  }
15457
15608
  async function validateAnchorScreen(packRoot) {
15458
15609
  const issues = [];
15459
- const storyPath = import_node_path51.default.join(packRoot, "03_Story-Workshop.md");
15610
+ const storyPath = import_node_path52.default.join(packRoot, "03_Story-Workshop.md");
15460
15611
  const content = await readSafe(storyPath);
15461
15612
  if (!content) return issues;
15462
15613
  const dds = extractDdsSection(content);
@@ -15498,7 +15649,7 @@ async function validateAnchorScreen(packRoot) {
15498
15649
  }
15499
15650
  async function validateCompetitiveRefs2(packRoot) {
15500
15651
  const issues = [];
15501
- const sourcesPath = import_node_path51.default.join(packRoot, "04_Sources.md");
15652
+ const sourcesPath = import_node_path52.default.join(packRoot, "04_Sources.md");
15502
15653
  const content = await readSafe(sourcesPath);
15503
15654
  if (!content) return issues;
15504
15655
  const registryHeadingRe = /^##\s+Competitive Reference Registry\b.*$/m;
@@ -15555,7 +15706,7 @@ function fieldGuidance(field) {
15555
15706
  }
15556
15707
  async function validateCtaHierarchy(packRoot) {
15557
15708
  const issues = [];
15558
- const storyPath = import_node_path51.default.join(packRoot, "03_Story-Workshop.md");
15709
+ const storyPath = import_node_path52.default.join(packRoot, "03_Story-Workshop.md");
15559
15710
  const content = await readSafe(storyPath);
15560
15711
  if (!content) return issues;
15561
15712
  const dds = extractDdsSection(content);
@@ -15593,7 +15744,7 @@ async function validateCtaHierarchy(packRoot) {
15593
15744
  }
15594
15745
  async function validateStateCoverage(packRoot) {
15595
15746
  const issues = [];
15596
- const storyPath = import_node_path51.default.join(packRoot, "03_Story-Workshop.md");
15747
+ const storyPath = import_node_path52.default.join(packRoot, "03_Story-Workshop.md");
15597
15748
  const content = await readSafe(storyPath);
15598
15749
  if (!content) return issues;
15599
15750
  const dds = extractDdsSection(content);
@@ -15618,7 +15769,7 @@ async function validateStateCoverage(packRoot) {
15618
15769
  }
15619
15770
  async function validateDesignAntiGoals(packRoot) {
15620
15771
  const issues = [];
15621
- const storyPath = import_node_path51.default.join(packRoot, "03_Story-Workshop.md");
15772
+ const storyPath = import_node_path52.default.join(packRoot, "03_Story-Workshop.md");
15622
15773
  const content = await readSafe(storyPath);
15623
15774
  if (!content) return issues;
15624
15775
  const dds = extractDdsSection(content);
@@ -15655,7 +15806,7 @@ async function validateDesignAntiGoals(packRoot) {
15655
15806
  return issues;
15656
15807
  }
15657
15808
  async function validateDiscussionDesignHardening(root, config) {
15658
- const discussionDir = import_node_path51.default.join(root, config.paths.discussionDir);
15809
+ const discussionDir = import_node_path52.default.join(root, config.paths.discussionDir);
15659
15810
  const packRoot = await findLatestDiscussionPackDir(discussionDir);
15660
15811
  if (!packRoot) return [];
15661
15812
  const uiBearing = await isUiBearing(packRoot);
@@ -15672,8 +15823,8 @@ async function validateDiscussionDesignHardening(root, config) {
15672
15823
  }
15673
15824
 
15674
15825
  // src/core/validators/designAudit.ts
15675
- var import_promises50 = require("fs/promises");
15676
- var import_node_path52 = __toESM(require("path"), 1);
15826
+ var import_promises51 = require("fs/promises");
15827
+ var import_node_path53 = __toESM(require("path"), 1);
15677
15828
  var COSMETIC_CATEGORIES = ["generic-shell", "stock-imagery", "placeholder-copy"];
15678
15829
  function resolveAuditConfig(config) {
15679
15830
  const audit = config.uiux?.audit;
@@ -15755,19 +15906,19 @@ var RAW_COLOR_RE = /#[0-9a-fA-F]{3,8}\b|rgb\([^)]+\)|rgba\([^)]+\)|hsl\([^)]+\)|
15755
15906
  async function checkTokenDrift(root, auditConfig, cfg) {
15756
15907
  const findings = [];
15757
15908
  const configuredDir = cfg.uiux?.designTokensDir;
15758
- const tokensDir = configuredDir ? import_node_path52.default.resolve(root, configuredDir) : import_node_path52.default.join(root, cfg.paths.contractsDir, "design");
15909
+ const tokensDir = configuredDir ? import_node_path53.default.resolve(root, configuredDir) : import_node_path53.default.join(root, cfg.paths.contractsDir, "design");
15759
15910
  let hasTokenFiles = false;
15760
15911
  try {
15761
- const entries = await (0, import_promises50.readdir)(tokensDir);
15912
+ const entries = await (0, import_promises51.readdir)(tokensDir);
15762
15913
  hasTokenFiles = entries.some((e) => /\.ya?ml$/i.test(e));
15763
15914
  } catch {
15764
15915
  return findings;
15765
15916
  }
15766
15917
  if (!hasTokenFiles) return findings;
15767
- const contractsUiDir = import_node_path52.default.join(root, cfg.paths.contractsDir, "ui");
15918
+ const contractsUiDir = import_node_path53.default.join(root, cfg.paths.contractsDir, "ui");
15768
15919
  let htmlFiles = [];
15769
15920
  try {
15770
- const entries = await (0, import_promises50.readdir)(contractsUiDir);
15921
+ const entries = await (0, import_promises51.readdir)(contractsUiDir);
15771
15922
  htmlFiles = entries.filter((e) => /\.html?$/i.test(e));
15772
15923
  } catch {
15773
15924
  return findings;
@@ -15775,7 +15926,7 @@ async function checkTokenDrift(root, auditConfig, cfg) {
15775
15926
  let rawCount = 0;
15776
15927
  const sampleLiterals = [];
15777
15928
  for (const htmlFile of htmlFiles) {
15778
- const content = await readSafe(import_node_path52.default.join(contractsUiDir, htmlFile));
15929
+ const content = await readSafe(import_node_path53.default.join(contractsUiDir, htmlFile));
15779
15930
  if (!content) continue;
15780
15931
  const matches = content.match(RAW_COLOR_RE);
15781
15932
  if (matches) {
@@ -15826,12 +15977,12 @@ function deduplicateFindings(issues, maxPerRule) {
15826
15977
  async function validateDesignAudit(root, config) {
15827
15978
  const auditConfig = resolveAuditConfig(config);
15828
15979
  if (!auditConfig.enabled) return [];
15829
- const discussionDir = import_node_path52.default.join(root, config.paths.discussionDir);
15980
+ const discussionDir = import_node_path53.default.join(root, config.paths.discussionDir);
15830
15981
  const packRoot = await findLatestDiscussionPackDir(discussionDir);
15831
15982
  if (!packRoot) return [];
15832
15983
  const uiBearing = await isUiBearing(packRoot);
15833
15984
  if (!uiBearing) return [];
15834
- const storyPath = import_node_path52.default.join(packRoot, "03_Story-Workshop.md");
15985
+ const storyPath = import_node_path53.default.join(packRoot, "03_Story-Workshop.md");
15835
15986
  const content = await readSafe(storyPath);
15836
15987
  if (!content) return [];
15837
15988
  const findings = [];
@@ -15843,8 +15994,8 @@ async function validateDesignAudit(root, config) {
15843
15994
 
15844
15995
  // src/core/validators/designSlop.ts
15845
15996
  var import_node_fs2 = require("fs");
15846
- var import_promises51 = require("fs/promises");
15847
- var import_node_path53 = __toESM(require("path"), 1);
15997
+ var import_promises52 = require("fs/promises");
15998
+ var import_node_path54 = __toESM(require("path"), 1);
15848
15999
  var import_node_url4 = require("url");
15849
16000
  function isValidSlopPattern(rule) {
15850
16001
  if (typeof rule !== "object" || rule === null) return false;
@@ -15852,7 +16003,7 @@ function isValidSlopPattern(rule) {
15852
16003
  return typeof r.id === "string" && typeof r.category === "string" && typeof r.tier === "number" && Array.isArray(r.scopes) && typeof r.match === "string" && typeof r.message === "string" && typeof r.guidance === "string";
15853
16004
  }
15854
16005
  async function loadSlopPatterns(jsonPath) {
15855
- const raw = await (0, import_promises51.readFile)(jsonPath, "utf-8");
16006
+ const raw = await (0, import_promises52.readFile)(jsonPath, "utf-8");
15856
16007
  const parsed = JSON.parse(raw);
15857
16008
  if (!Array.isArray(parsed)) return [];
15858
16009
  return parsed.filter((r) => isValidSlopPattern(r));
@@ -15860,11 +16011,11 @@ async function loadSlopPatterns(jsonPath) {
15860
16011
  function defaultPatternsPath() {
15861
16012
  const base = __filename;
15862
16013
  const basePath = base.startsWith("file:") ? (0, import_node_url4.fileURLToPath)(base) : base;
15863
- const baseDir = import_node_path53.default.dirname(basePath);
16014
+ const baseDir = import_node_path54.default.dirname(basePath);
15864
16015
  const candidates = [
15865
- import_node_path53.default.join(baseDir, "designSlopPatterns.json"),
15866
- import_node_path53.default.resolve(baseDir, "../../../assets/validators/designSlopPatterns.json"),
15867
- import_node_path53.default.resolve(baseDir, "../../assets/validators/designSlopPatterns.json")
16016
+ import_node_path54.default.join(baseDir, "designSlopPatterns.json"),
16017
+ import_node_path54.default.resolve(baseDir, "../../../assets/validators/designSlopPatterns.json"),
16018
+ import_node_path54.default.resolve(baseDir, "../../assets/validators/designSlopPatterns.json")
15868
16019
  ];
15869
16020
  for (const c of candidates) {
15870
16021
  if ((0, import_node_fs2.existsSync)(c)) return c;
@@ -15875,7 +16026,7 @@ async function validateDesignSlop(root, config) {
15875
16026
  const auditConfig = resolveAuditConfig(config);
15876
16027
  if (!auditConfig.enabled) return [];
15877
16028
  if (!auditConfig.slopDetection) return [];
15878
- const discussionDir = import_node_path53.default.join(root, config.paths.discussionDir);
16029
+ const discussionDir = import_node_path54.default.join(root, config.paths.discussionDir);
15879
16030
  const packRoot = await findLatestDiscussionPackDir(discussionDir);
15880
16031
  if (!packRoot) return [];
15881
16032
  const uiBearing = await isUiBearing(packRoot);
@@ -15896,7 +16047,7 @@ async function validateDesignSlop(root, config) {
15896
16047
  continue;
15897
16048
  }
15898
16049
  for (const scope of pattern.scopes) {
15899
- const filePath = import_node_path53.default.join(packRoot, scope);
16050
+ const filePath = import_node_path54.default.join(packRoot, scope);
15900
16051
  const content = await readSafe(filePath);
15901
16052
  if (!content) continue;
15902
16053
  if (regex.test(content) && !seenRules.has(pattern.id)) {
@@ -15917,6 +16068,460 @@ async function validateDesignSlop(root, config) {
15917
16068
  return findings.map((f) => findingToIssue(f, auditConfig.qualityProfile, "slop"));
15918
16069
  }
15919
16070
 
16071
+ // src/core/validators/uixValidators.ts
16072
+ var import_promises54 = require("fs/promises");
16073
+ var import_node_path56 = __toESM(require("path"), 1);
16074
+
16075
+ // src/core/validators/uixDetection.ts
16076
+ var import_promises53 = require("fs/promises");
16077
+ var import_node_path55 = __toESM(require("path"), 1);
16078
+ var UI_BEARING_SURFACES = /* @__PURE__ */ new Set(["web-ui", "mobile-ui", "desktop-ui", "mixed"]);
16079
+ var NON_UI_SURFACES = /* @__PURE__ */ new Set(["non-ui"]);
16080
+ function stripCodeBlocks(content) {
16081
+ let stripped = content.replace(/```[\s\S]*?```/g, "");
16082
+ stripped = stripped.replace(/`[^`]+`/g, "");
16083
+ return stripped;
16084
+ }
16085
+ var HTML_TAG_RE2 = /<(?:style|div|section|span|button|input|form|header|footer|nav|main|aside)\b/i;
16086
+ var MERMAID_SCREEN_FLOW_RE2 = /```mermaid[\s\S]*?(?:stateDiagram)[\s\S]*?(?:Screen|Page|View|Dashboard|Login|Settings|Home)\b/i;
16087
+ var SCREEN_CONTRACT_YAML_RE = /screens:\s*\n\s*-\s*route:/;
16088
+ async function isUiBearingSpec(root) {
16089
+ const specContent = await readSafe(import_node_path55.default.join(root, "01_Spec.md"));
16090
+ const surfaceMatch = /^\s*-\s*surface:\s*(\S+)/im.exec(specContent);
16091
+ if (surfaceMatch?.[1]) {
16092
+ const surface = surfaceMatch[1].toLowerCase();
16093
+ if (UI_BEARING_SURFACES.has(surface)) return true;
16094
+ if (NON_UI_SURFACES.has(surface)) return false;
16095
+ }
16096
+ try {
16097
+ await (0, import_promises53.readdir)(import_node_path55.default.join(root, "uiux"));
16098
+ return true;
16099
+ } catch {
16100
+ }
16101
+ const contractsContent = await readSafe(import_node_path55.default.join(root, "uiux", "40_contracts.md"));
16102
+ if (contractsContent && SCREEN_CONTRACT_YAML_RE.test(contractsContent)) return true;
16103
+ const storyContent = await readSafe(import_node_path55.default.join(root, "03_Story-Workshop.md"));
16104
+ if (storyContent) {
16105
+ const stripped = stripCodeBlocks(storyContent);
16106
+ if (HTML_TAG_RE2.test(stripped)) return true;
16107
+ if (MERMAID_SCREEN_FLOW_RE2.test(storyContent)) return true;
16108
+ }
16109
+ return false;
16110
+ }
16111
+
16112
+ // src/core/validators/uixValidators.ts
16113
+ function uixIssue(code, message, severity, file, suggestedAction) {
16114
+ return {
16115
+ code,
16116
+ severity,
16117
+ category: "compatibility",
16118
+ message,
16119
+ file,
16120
+ suggested_action: suggestedAction
16121
+ };
16122
+ }
16123
+ function parseSimpleYaml(content) {
16124
+ const result = {};
16125
+ const store = (rawKey, value) => {
16126
+ const key = rawKey.trim().toLowerCase().replace(/\s+/g, "_");
16127
+ if (key && value) result[key] = value;
16128
+ };
16129
+ for (const line of content.split("\n")) {
16130
+ const yamlMatch = /^\s*(\w[\w_]*):\s*(.*)$/.exec(line);
16131
+ if (yamlMatch?.[1] !== void 0 && yamlMatch[2] !== void 0) {
16132
+ store(yamlMatch[1], yamlMatch[2].trim());
16133
+ continue;
16134
+ }
16135
+ const bulletMatch = /^\s*-\s+([\w][\w\s]*?):\s+(.+)$/.exec(line);
16136
+ if (bulletMatch?.[1] !== void 0 && bulletMatch[2] !== void 0) {
16137
+ store(bulletMatch[1], bulletMatch[2].trim());
16138
+ continue;
16139
+ }
16140
+ const tableMatch = /^\s*\|\s*(\w[\w\s_]*?)\s*\|\s*(.+?)\s*\|\s*$/.exec(line);
16141
+ if (tableMatch?.[1] !== void 0 && tableMatch[2] !== void 0) {
16142
+ if (!/^[-:]+$/.test(tableMatch[2].trim())) {
16143
+ store(tableMatch[1], tableMatch[2].trim());
16144
+ }
16145
+ }
16146
+ }
16147
+ return result;
16148
+ }
16149
+ async function validateSidecarMissing(root, _config) {
16150
+ if (!await isUiBearingSpec(root)) return [];
16151
+ try {
16152
+ await (0, import_promises54.readdir)(import_node_path56.default.join(root, "uiux"));
16153
+ return [];
16154
+ } catch {
16155
+ return [
16156
+ uixIssue(
16157
+ "UIX-VAL-SIDECAR-MISSING",
16158
+ "UI-bearing spec detected but uiux/ sidecar directory is missing.",
16159
+ "error",
16160
+ "uiux/",
16161
+ "Create the uiux/ directory and populate it with the sidecar template files."
16162
+ )
16163
+ ];
16164
+ }
16165
+ }
16166
+ var STRATEGY_REQUIRED_FIELDS_LEGACY = [
16167
+ "selection_required",
16168
+ "candidate_options",
16169
+ "chosen_option",
16170
+ "verification_expectations",
16171
+ "none_as_legitimate_outcome"
16172
+ ];
16173
+ var STRATEGY_REQUIRED_FIELDS_CURRENT = ["surface_type", "approach", "rationale"];
16174
+ var STRATEGY_MIN_LENGTH_FIELDS = ["rationale", "approach"];
16175
+ var STRATEGY_MIN_LENGTH = 20;
16176
+ async function validateStrategyCompleteness(root, _config) {
16177
+ if (!await isUiBearingSpec(root)) return [];
16178
+ const strategyPath = import_node_path56.default.join(root, "uiux", "10_strategy.md");
16179
+ const content = await readSafe(strategyPath);
16180
+ if (!content) return [];
16181
+ const parsed = parseSimpleYaml(content);
16182
+ const issues = [];
16183
+ const relPath = "uiux/10_strategy.md";
16184
+ const isCurrentFormat = parsed["surface_type"] !== void 0;
16185
+ const requiredFields = isCurrentFormat ? STRATEGY_REQUIRED_FIELDS_CURRENT : STRATEGY_REQUIRED_FIELDS_LEGACY;
16186
+ for (const field of requiredFields) {
16187
+ const value = parsed[field];
16188
+ if (value === void 0 || value === "") {
16189
+ issues.push(
16190
+ uixIssue(
16191
+ "UIX-VAL-STRATEGY-INCOMPLETE",
16192
+ `Strategy field '${field}' is missing or empty in 10_strategy.md.`,
16193
+ "error",
16194
+ relPath,
16195
+ `Add the '${field}' field to uiux/10_strategy.md with a valid value.`
16196
+ )
16197
+ );
16198
+ }
16199
+ }
16200
+ for (const field of STRATEGY_MIN_LENGTH_FIELDS) {
16201
+ const value = parsed[field];
16202
+ if (value === void 0 || value.length < STRATEGY_MIN_LENGTH) {
16203
+ issues.push(
16204
+ uixIssue(
16205
+ "UIX-VAL-STRATEGY-INCOMPLETE",
16206
+ `Strategy field '${field}' must be at least ${STRATEGY_MIN_LENGTH} characters (current: ${value?.length ?? 0}).`,
16207
+ "error",
16208
+ relPath,
16209
+ `Expand the '${field}' field in uiux/10_strategy.md to at least ${STRATEGY_MIN_LENGTH} characters.`
16210
+ )
16211
+ );
16212
+ }
16213
+ }
16214
+ return issues;
16215
+ }
16216
+ async function validateScoringAxes(root, _config) {
16217
+ if (!await isUiBearingSpec(root)) return [];
16218
+ const singlePath = import_node_path56.default.join(root, "uiux", "20_eval_axes.md");
16219
+ let content = await readSafe(singlePath);
16220
+ let relPath = "uiux/20_eval_axes.md";
16221
+ if (!content) {
16222
+ const splitFiles = [
16223
+ "20_eval_axis_usability.md",
16224
+ "21_eval_axis_consistency.md",
16225
+ "22_eval_axis_accessibility.md",
16226
+ "23_eval_axis_delight.md"
16227
+ ];
16228
+ const parts = [];
16229
+ for (const f of splitFiles) {
16230
+ const c = await readSafe(import_node_path56.default.join(root, "uiux", f));
16231
+ if (c) parts.push(c);
16232
+ }
16233
+ if (parts.length > 0) {
16234
+ content = parts.join("\n");
16235
+ relPath = "uiux/20_eval_axis_*.md";
16236
+ }
16237
+ }
16238
+ if (!content) return [];
16239
+ const issues = [];
16240
+ const lines = content.split("\n");
16241
+ let inTrendSection = false;
16242
+ for (const line of lines) {
16243
+ if (/trend[_-]derived/i.test(line)) {
16244
+ inTrendSection = true;
16245
+ continue;
16246
+ }
16247
+ if (inTrendSection && /^#+\s/.test(line)) {
16248
+ inTrendSection = false;
16249
+ continue;
16250
+ }
16251
+ const isBulletRow = /^\s*-\s/.test(line);
16252
+ const isTableRow2 = /^\s*\|/.test(line) && !/^\s*\|[\s-:|]+\|[\s-:|]*$/.test(line);
16253
+ if (inTrendSection && (isBulletRow || isTableRow2)) {
16254
+ if (!/source_translation/i.test(line)) {
16255
+ issues.push(
16256
+ uixIssue(
16257
+ "UIX-VAL-SCORING-AXIS-INCOMPLETE",
16258
+ "Trend-derived scoring axis row is missing source_translation field.",
16259
+ "error",
16260
+ relPath,
16261
+ "Add source_translation to each trend-derived evaluation axis row."
16262
+ )
16263
+ );
16264
+ }
16265
+ }
16266
+ }
16267
+ return issues;
16268
+ }
16269
+ var AGGREGATE_REQUIRED_FIELDS = ["weights", "normalization", "threshold"];
16270
+ async function validateAggregateScoringRules(root, _config) {
16271
+ if (!await isUiBearingSpec(root)) return [];
16272
+ const aggregatePath = import_node_path56.default.join(root, "uiux", "21_aggregate_scoring.md");
16273
+ let content = await readSafe(aggregatePath);
16274
+ let relPath = "uiux/21_aggregate_scoring.md";
16275
+ if (!content) {
16276
+ const delightContent = await readSafe(import_node_path56.default.join(root, "uiux", "23_eval_axis_delight.md"));
16277
+ if (delightContent) {
16278
+ const lines = delightContent.split("\n");
16279
+ let inSection = false;
16280
+ const sectionLines = [];
16281
+ for (const line of lines) {
16282
+ if (/^#+\s*Aggregate\s+Scoring/i.test(line)) {
16283
+ inSection = true;
16284
+ continue;
16285
+ }
16286
+ if (inSection && /^#+\s/.test(line)) break;
16287
+ if (inSection) sectionLines.push(line);
16288
+ }
16289
+ if (sectionLines.length > 0) {
16290
+ content = sectionLines.join("\n");
16291
+ relPath = "uiux/23_eval_axis_delight.md";
16292
+ }
16293
+ }
16294
+ }
16295
+ if (!content) return [];
16296
+ const parsed = parseSimpleYaml(content);
16297
+ const issues = [];
16298
+ for (const field of AGGREGATE_REQUIRED_FIELDS) {
16299
+ const value = parsed[field] ?? parsed[`${field}s`];
16300
+ if (value === void 0 || value === "") {
16301
+ issues.push(
16302
+ uixIssue(
16303
+ "UIX-VAL-AGGREGATE-SCORING-INCOMPLETE",
16304
+ `Aggregate scoring field '${field}' is missing in ${relPath}.`,
16305
+ "error",
16306
+ relPath,
16307
+ `Add the '${field}' field to ${relPath}.`
16308
+ )
16309
+ );
16310
+ }
16311
+ }
16312
+ return issues;
16313
+ }
16314
+ async function validateOptionComparison3(root, _config) {
16315
+ if (!await isUiBearingSpec(root)) return [];
16316
+ const issues = [];
16317
+ const compPath = import_node_path56.default.join(root, "uiux", "30_comparison.md");
16318
+ const compContent = await readSafe(compPath);
16319
+ if (compContent) {
16320
+ const headingOptions = compContent.match(/^##\s+Option\b/gim);
16321
+ const tableOptions = compContent.match(/\bOption\s+[A-Z]\b/gim);
16322
+ const uniqueTableOptions = tableOptions ? new Set(tableOptions.map((m) => m.trim().toUpperCase())).size : 0;
16323
+ const optionCount = Math.max(headingOptions?.length ?? 0, uniqueTableOptions);
16324
+ if (optionCount < 2) {
16325
+ issues.push(
16326
+ uixIssue(
16327
+ "UIX-VAL-COMPARISON-INSUFFICIENT",
16328
+ "30_comparison.md must contain at least 2 options for meaningful comparison.",
16329
+ "error",
16330
+ "uiux/30_comparison.md",
16331
+ "Add at least 2 '## Option' sections to uiux/30_comparison.md."
16332
+ )
16333
+ );
16334
+ }
16335
+ }
16336
+ const anchorPath = import_node_path56.default.join(root, "uiux", "31_anchor.md");
16337
+ const anchorContent = await readSafe(anchorPath);
16338
+ if (anchorContent) {
16339
+ if (!/selected_anchor\s*:/i.test(anchorContent) && !/chosen\s*:/i.test(anchorContent) && !/source\s+option\s*:/i.test(anchorContent)) {
16340
+ issues.push(
16341
+ uixIssue(
16342
+ "UIX-VAL-ANCHOR-MISSING",
16343
+ "31_anchor.md is missing a selected anchor declaration.",
16344
+ "error",
16345
+ "uiux/31_anchor.md",
16346
+ "Add a 'selected_anchor:' field to uiux/31_anchor.md."
16347
+ )
16348
+ );
16349
+ }
16350
+ }
16351
+ return issues;
16352
+ }
16353
+ var SCREEN_CONTRACT_REQUIRED_FIELDS = [
16354
+ "route",
16355
+ "actor",
16356
+ "purpose",
16357
+ "primary_tasks",
16358
+ "required_states",
16359
+ "transitions",
16360
+ "observable_outcomes"
16361
+ ];
16362
+ async function validateScreenContracts(root, _config) {
16363
+ if (!await isUiBearingSpec(root)) return [];
16364
+ const contractsPath = import_node_path56.default.join(root, "uiux", "40_contracts.md");
16365
+ const content = await readSafe(contractsPath);
16366
+ if (!content) return [];
16367
+ const parsed = parseSimpleYaml(content);
16368
+ const issues = [];
16369
+ const relPath = "uiux/40_contracts.md";
16370
+ const sectionHeadings = {
16371
+ primary_tasks: /^#{3,4}\s+Primary\s+Tasks/im,
16372
+ required_states: /^#{3,4}\s+Required\s+States/im,
16373
+ transitions: /^#{3,4}\s+Transitions/im,
16374
+ observable_outcomes: /^#{3,4}\s+Observable\s+Outcomes/im
16375
+ };
16376
+ for (const field of SCREEN_CONTRACT_REQUIRED_FIELDS) {
16377
+ const value = parsed[field];
16378
+ const hasValue = value !== void 0 && value !== "";
16379
+ const headingPattern = sectionHeadings[field];
16380
+ const hasHeading = headingPattern ? headingPattern.test(content) : false;
16381
+ if (!hasValue && !hasHeading) {
16382
+ issues.push(
16383
+ uixIssue(
16384
+ "UIX-VAL-SCREEN-CONTRACT-INCOMPLETE",
16385
+ `Screen contract field '${field}' is missing in 40_contracts.md.`,
16386
+ "error",
16387
+ relPath,
16388
+ `Add the '${field}' field to screen contracts in uiux/40_contracts.md.`
16389
+ )
16390
+ );
16391
+ }
16392
+ }
16393
+ return issues;
16394
+ }
16395
+ async function validateOqClosure(root, _config) {
16396
+ if (!await isUiBearingSpec(root)) return [];
16397
+ const rootOqPath = import_node_path56.default.join(root, "11_OQ-Register.md");
16398
+ const uiuxOqPath = import_node_path56.default.join(root, "uiux", "11_OQ-Register.md");
16399
+ const rootContent = await readSafe(rootOqPath);
16400
+ const content = rootContent || await readSafe(uiuxOqPath);
16401
+ if (!content) return [];
16402
+ const issues = [];
16403
+ const relPath = rootContent ? "11_OQ-Register.md" : "uiux/11_OQ-Register.md";
16404
+ const oqBlocks = content.split(/(?=^##\s+OQ-\d{4})/m);
16405
+ for (const block of oqBlocks) {
16406
+ const idMatch = /^##\s+(OQ-\d{4})/m.exec(block);
16407
+ if (!idMatch?.[1]) continue;
16408
+ const oqId = idMatch[1];
16409
+ const isOpen = /status\s*:\s*open/i.test(block);
16410
+ const isCritical = /severity\s*:\s*(?:critical|blocking)/i.test(block);
16411
+ if (isOpen && isCritical) {
16412
+ issues.push(
16413
+ uixIssue(
16414
+ "UIX-VAL-OQ-OPEN-CRITICAL",
16415
+ `Open critical OQ found: ${oqId}. Must be resolved before proceeding.`,
16416
+ "error",
16417
+ relPath,
16418
+ `Resolve or downgrade ${oqId} in uiux/11_OQ-Register.md before validation can pass.`
16419
+ )
16420
+ );
16421
+ }
16422
+ }
16423
+ const tableRowRegex = /^\s*\|\s*(OQ-\d{4})\s*\|/;
16424
+ for (const line of content.split("\n")) {
16425
+ const rowMatch = tableRowRegex.exec(line);
16426
+ if (!rowMatch?.[1]) continue;
16427
+ const oqId = rowMatch[1];
16428
+ const cols = line.split("|").map((c) => c.trim()).filter(Boolean);
16429
+ const disposition = cols[3]?.toLowerCase();
16430
+ if (disposition === "open") {
16431
+ const fullRow = line.toLowerCase();
16432
+ if (/critical|blocking/i.test(fullRow)) {
16433
+ issues.push(
16434
+ uixIssue(
16435
+ "UIX-VAL-OQ-OPEN-CRITICAL",
16436
+ `Open critical OQ found: ${oqId}. Must be resolved before proceeding.`,
16437
+ "error",
16438
+ relPath,
16439
+ `Resolve or downgrade ${oqId} in ${relPath} before validation can pass.`
16440
+ )
16441
+ );
16442
+ }
16443
+ }
16444
+ }
16445
+ return issues;
16446
+ }
16447
+ var CURRENT_SIDECAR_VERSION = "1.0.0";
16448
+ async function validateMigration(root, config) {
16449
+ if (!await isUiBearingSpec(root)) return [];
16450
+ const strict = config.uiux?.migration?.strict === true;
16451
+ const issues = [];
16452
+ try {
16453
+ await (0, import_promises54.readdir)(import_node_path56.default.join(root, "uiux"));
16454
+ } catch {
16455
+ issues.push(
16456
+ uixIssue(
16457
+ "UIX-VAL-MIGRATION-SIDECAR-MISSING",
16458
+ "UI-bearing spec detected but uiux/ sidecar directory is missing. Migration required.",
16459
+ strict ? "error" : "warning",
16460
+ "uiux/",
16461
+ "Run the UIX sidecar migration to create the uiux/ directory structure."
16462
+ )
16463
+ );
16464
+ return issues;
16465
+ }
16466
+ const versionPath = import_node_path56.default.join(root, "uiux", ".sidecar-version");
16467
+ const versionContent = await readSafe(versionPath);
16468
+ if (versionContent) {
16469
+ const version = versionContent.trim();
16470
+ if (version && version !== CURRENT_SIDECAR_VERSION) {
16471
+ issues.push(
16472
+ uixIssue(
16473
+ "UIX-VAL-MIGRATION-STALE-VERSION",
16474
+ `Sidecar template version '${version}' is outdated (current: ${CURRENT_SIDECAR_VERSION}).`,
16475
+ "warning",
16476
+ "uiux/.sidecar-version",
16477
+ `Upgrade sidecar template from ${version} to ${CURRENT_SIDECAR_VERSION}. Run the migration tool for upgrade steps.`
16478
+ )
16479
+ );
16480
+ }
16481
+ }
16482
+ return issues;
16483
+ }
16484
+ function applyPhase1Ratchet(issues, releaseDate, now = /* @__PURE__ */ new Date()) {
16485
+ const phase1EndMs = releaseDate.getTime() + 30 * 24 * 60 * 60 * 1e3;
16486
+ if (now.getTime() > phase1EndMs) return issues;
16487
+ return issues.map((iss) => {
16488
+ if (iss.code.startsWith("UIX-VAL-") && iss.severity === "error") {
16489
+ return { ...iss, severity: "warning" };
16490
+ }
16491
+ return iss;
16492
+ });
16493
+ }
16494
+ async function runAllUixValidators(root, config) {
16495
+ let effectiveRoot = root;
16496
+ const directSpec = await readSafe(import_node_path56.default.join(root, "01_Spec.md"));
16497
+ if (!directSpec) {
16498
+ const discussionDir = import_node_path56.default.join(root, config.paths.discussionDir);
16499
+ const packRoot = await findLatestDiscussionPackDir(discussionDir);
16500
+ if (!packRoot) return [];
16501
+ effectiveRoot = packRoot;
16502
+ }
16503
+ const validators = [
16504
+ validateSidecarMissing,
16505
+ validateStrategyCompleteness,
16506
+ validateScoringAxes,
16507
+ validateAggregateScoringRules,
16508
+ validateOptionComparison3,
16509
+ validateScreenContracts,
16510
+ validateOqClosure,
16511
+ validateMigration
16512
+ ];
16513
+ const results = await Promise.all(validators.map((v) => v(effectiveRoot, config)));
16514
+ let issues = results.flat();
16515
+ const relDateStr = config.uiux?.phase1ReleaseDate;
16516
+ if (relDateStr) {
16517
+ const releaseDate = new Date(relDateStr);
16518
+ if (!isNaN(releaseDate.getTime())) {
16519
+ issues = applyPhase1Ratchet(issues, releaseDate);
16520
+ }
16521
+ }
16522
+ return issues;
16523
+ }
16524
+
15920
16525
  // src/core/validate.ts
15921
16526
  var UIUX_VALIDATION_BUDGET_MS = 2e3;
15922
16527
  async function validateProject(root, configResult, options = {}) {
@@ -15936,7 +16541,8 @@ async function validateProject(root, configResult, options = {}) {
15936
16541
  () => validateResearchSummary(root, config),
15937
16542
  () => validateAgentDefinition(root, config),
15938
16543
  () => validateDesignAudit(root, config),
15939
- () => validateDesignSlop(root, config)
16544
+ () => validateDesignSlop(root, config),
16545
+ () => runAllUixValidators(root, config)
15940
16546
  ];
15941
16547
  const uiuxIssueGroups = await Promise.all(uiuxValidators.map((validator) => validator()));
15942
16548
  const uiuxIssues = [...platformResult.issues, ...uiuxIssueGroups.flat()];
@@ -16023,15 +16629,15 @@ var REPORT_GUARDRAILS_MAX = 20;
16023
16629
  var REPORT_TEST_STRATEGY_SAMPLE_LIMIT = 20;
16024
16630
  var SC_TAG_RE4 = /^SC-\d{4}-\d{4}$/;
16025
16631
  async function createReportData(root, validation, configResult) {
16026
- const resolvedRoot = import_node_path54.default.resolve(root);
16632
+ const resolvedRoot = import_node_path57.default.resolve(root);
16027
16633
  const resolved = configResult ?? await loadConfig(resolvedRoot);
16028
16634
  const config = resolved.config;
16029
16635
  const configPath = resolved.configPath;
16030
16636
  const specsRoot = resolvePath(resolvedRoot, config, "specsDir");
16031
16637
  const contractsRoot = resolvePath(resolvedRoot, config, "contractsDir");
16032
- const apiRoot = import_node_path54.default.join(contractsRoot, "api");
16033
- const uiRoot = import_node_path54.default.join(contractsRoot, "ui");
16034
- const dbRoot = import_node_path54.default.join(contractsRoot, "db");
16638
+ const apiRoot = import_node_path57.default.join(contractsRoot, "api");
16639
+ const uiRoot = import_node_path57.default.join(contractsRoot, "ui");
16640
+ const dbRoot = import_node_path57.default.join(contractsRoot, "db");
16035
16641
  const srcRoot = resolvePath(resolvedRoot, config, "srcDir");
16036
16642
  const testsRoot = resolvePath(resolvedRoot, config, "testsDir");
16037
16643
  const specEntries = await collectSpecEntries(specsRoot);
@@ -16968,7 +17574,7 @@ async function collectChangeTypeSummary(specsRoot) {
16968
17574
  };
16969
17575
  const deltaFiles = await collectDeltaFiles(specsRoot);
16970
17576
  for (const deltaFile of deltaFiles) {
16971
- const text = await (0, import_promises52.readFile)(deltaFile, "utf-8");
17577
+ const text = await (0, import_promises55.readFile)(deltaFile, "utf-8");
16972
17578
  const parsed = parseDeltaV1(text);
16973
17579
  for (const entry of parsed.entries) {
16974
17580
  if (!entry.meta) {
@@ -17005,7 +17611,7 @@ async function collectSpecContractRefs(specFiles, contractIdList) {
17005
17611
  idToSpecs.set(contractId, /* @__PURE__ */ new Set());
17006
17612
  }
17007
17613
  for (const file of specFiles) {
17008
- const text = await (0, import_promises52.readFile)(file, "utf-8");
17614
+ const text = await (0, import_promises55.readFile)(file, "utf-8");
17009
17615
  const parsed = parseSpec(text, file);
17010
17616
  const specKey = parsed.specId;
17011
17617
  if (!specKey) {
@@ -17042,7 +17648,7 @@ async function collectIds(files) {
17042
17648
  result[prefix] = /* @__PURE__ */ new Set();
17043
17649
  }
17044
17650
  for (const file of files) {
17045
- const text = await (0, import_promises52.readFile)(file, "utf-8");
17651
+ const text = await (0, import_promises55.readFile)(file, "utf-8");
17046
17652
  for (const prefix of ID_PREFIXES) {
17047
17653
  const ids = extractIds(text, prefix);
17048
17654
  ids.forEach((id) => result[prefix].add(id));
@@ -17057,7 +17663,7 @@ async function collectIds(files) {
17057
17663
  async function collectUpstreamIds(files) {
17058
17664
  const ids = /* @__PURE__ */ new Set();
17059
17665
  for (const file of files) {
17060
- const text = await (0, import_promises52.readFile)(file, "utf-8");
17666
+ const text = await (0, import_promises55.readFile)(file, "utf-8");
17061
17667
  extractAllIds(text).forEach((id) => ids.add(id));
17062
17668
  }
17063
17669
  return ids;
@@ -17078,7 +17684,7 @@ async function evaluateTraceability(upstreamIds, srcRoot, testsRoot) {
17078
17684
  }
17079
17685
  const pattern = buildIdPattern(Array.from(upstreamIds));
17080
17686
  for (const file of targetFiles) {
17081
- const text = await (0, import_promises52.readFile)(file, "utf-8");
17687
+ const text = await (0, import_promises55.readFile)(file, "utf-8");
17082
17688
  if (pattern.test(text)) {
17083
17689
  return true;
17084
17690
  }
@@ -17197,7 +17803,7 @@ function normalizeScSources(root, sources) {
17197
17803
  async function countScenarios(scenarioFiles) {
17198
17804
  let total = 0;
17199
17805
  for (const file of scenarioFiles) {
17200
- const text = await (0, import_promises52.readFile)(file, "utf-8");
17806
+ const text = await (0, import_promises55.readFile)(file, "utf-8");
17201
17807
  const { document, errors } = parseScenarioDocument(text, file);
17202
17808
  if (!document || errors.length > 0) {
17203
17809
  continue;
@@ -17228,7 +17834,7 @@ async function collectTestStrategy(scenarioFiles, root, config, limit) {
17228
17834
  let totalScenarios = 0;
17229
17835
  let e2eCount = 0;
17230
17836
  for (const file of scenarioFiles) {
17231
- const text = await (0, import_promises52.readFile)(file, "utf-8");
17837
+ const text = await (0, import_promises55.readFile)(file, "utf-8");
17232
17838
  const { document, errors } = parseScenarioDocument(text, file);
17233
17839
  if (!document || errors.length > 0) {
17234
17840
  continue;
@@ -17316,10 +17922,10 @@ function buildHotspots(issues) {
17316
17922
  async function collectTddCoverage(entries) {
17317
17923
  const specs = [];
17318
17924
  for (const entry of entries) {
17319
- const testCasesPath = import_node_path54.default.join(entry.dir, "06_Test-Cases.md");
17925
+ const testCasesPath = import_node_path57.default.join(entry.dir, "06_Test-Cases.md");
17320
17926
  let tcContent;
17321
17927
  try {
17322
- tcContent = await (0, import_promises52.readFile)(testCasesPath, "utf-8");
17928
+ tcContent = await (0, import_promises55.readFile)(testCasesPath, "utf-8");
17323
17929
  } catch {
17324
17930
  continue;
17325
17931
  }
@@ -17351,10 +17957,10 @@ async function collectTddCoverage(entries) {
17351
17957
  });
17352
17958
  continue;
17353
17959
  }
17354
- const tddListPath = import_node_path54.default.join(entry.dir, "tdd", "test-list.md");
17960
+ const tddListPath = import_node_path57.default.join(entry.dir, "tdd", "test-list.md");
17355
17961
  let tddContent;
17356
17962
  try {
17357
- tddContent = await (0, import_promises52.readFile)(tddListPath, "utf-8");
17963
+ tddContent = await (0, import_promises55.readFile)(tddListPath, "utf-8");
17358
17964
  } catch {
17359
17965
  specs.push({
17360
17966
  specNumber: entry.specNumber,
@@ -17440,6 +18046,8 @@ async function collectTddCoverage(entries) {
17440
18046
  ID_PREFIXES,
17441
18047
  RUNTIME_HEAVY_CHECKS,
17442
18048
  STATIC_OBLIGATIONS,
18049
+ VALID_MODE_SOURCES,
18050
+ adaptSurfaceEvidence,
17443
18051
  autogenerateUiFidelity,
17444
18052
  buildUiFidelityScreens,
17445
18053
  checkDecisionGuardrails,
@@ -17465,6 +18073,7 @@ async function collectTddCoverage(entries) {
17465
18073
  filterDecisionGuardrailsByKeyword,
17466
18074
  findConfigRoot,
17467
18075
  formatGuardrailsForLlm,
18076
+ formatModeLog,
17468
18077
  formatReportJson,
17469
18078
  formatReportMarkdown,
17470
18079
  getConfigPath,
@@ -17472,9 +18081,11 @@ async function collectTddCoverage(entries) {
17472
18081
  loadConfig,
17473
18082
  loadDecisionGuardrails,
17474
18083
  normalizeDecisionGuardrails,
18084
+ readDiscussionRecommendation,
17475
18085
  resolveObligations,
17476
18086
  resolveObligationsWithOptIn,
17477
18087
  resolvePath,
18088
+ resolvePrecedence,
17478
18089
  resolveToolVersion,
17479
18090
  runMockPaths,
17480
18091
  runSddPreflight,