@aisdlc/core 1.3.0 → 1.3.2

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.js CHANGED
@@ -722,6 +722,12 @@ function walkMdFiles(dir, fileList = [], excludeDirs = [
722
722
  "node_modules",
723
723
  ".git",
724
724
  "dist",
725
+ "bin",
726
+ "obj",
727
+ "build",
728
+ "out",
729
+ "target",
730
+ "coverage",
725
731
  ".changeset",
726
732
  "scratch",
727
733
  "test-scaffold",
@@ -733,7 +739,9 @@ function walkMdFiles(dir, fileList = [], excludeDirs = [
733
739
  ".claude",
734
740
  ".cursor",
735
741
  ".github",
736
- ".vscode"
742
+ ".vscode",
743
+ ".vs",
744
+ ".idea"
737
745
  ]) {
738
746
  try {
739
747
  if (!fs6.existsSync(dir)) return fileList;
@@ -2052,6 +2060,7 @@ var TYPE_TO_SCHEMA_MAP = {
2052
2060
  "security-requirement": "schemas/security/security-req.schema.json",
2053
2061
  "threat-actor": "schemas/security/threat-actor.schema.json",
2054
2062
  // Architecture
2063
+ "architecture-introduction": "schemas/architecture/introduction-and-goals.schema.json",
2055
2064
  "architecture-decision-record": "schemas/architecture/adr.schema.json",
2056
2065
  component: "schemas/architecture/component.schema.json",
2057
2066
  // Safety
@@ -2095,6 +2104,7 @@ var PREFIX_TAXONOMY = [
2095
2104
  ["HAZ-", "hazard"],
2096
2105
  ["SAF-REQ-", "safety-requirement"],
2097
2106
  ["SAF-", "safety-requirement"],
2107
+ ["ARCH-INTRO-", "architecture-introduction"],
2098
2108
  ["ADR-", "architecture-decision-record"],
2099
2109
  ["CMP-", "component"],
2100
2110
  ["MAN-USER-", "user-manual"],
@@ -3284,10 +3294,18 @@ var SKIPPED_DIR_NAMES = /* @__PURE__ */ new Set([
3284
3294
  "node_modules",
3285
3295
  ".git",
3286
3296
  "dist",
3297
+ "bin",
3298
+ "obj",
3299
+ "build",
3300
+ "out",
3301
+ "target",
3302
+ "coverage",
3287
3303
  "scratch",
3288
3304
  "fixtures",
3289
3305
  "templates",
3290
- "examples"
3306
+ "examples",
3307
+ ".vs",
3308
+ ".idea"
3291
3309
  ]);
3292
3310
  function shouldSkipDir(name) {
3293
3311
  return SKIPPED_DIR_NAMES.has(name) || name.startsWith("temp-");
@@ -3389,8 +3407,14 @@ function verifyTraceability(options = {}) {
3389
3407
  });
3390
3408
  }
3391
3409
  }
3392
- if (relFile.includes("architecture/") || relFile.includes("06_runtime_view") || relFile.includes("08_security_concept") || relFile.includes("09_decisions")) {
3393
- architectureViews.push({ file: relFile, content });
3410
+ const isArchIntro = frontmatter.type === "architecture-introduction" || frontmatter.id !== void 0 && String(frontmatter.id).trim().startsWith("ARCH-INTRO-");
3411
+ if (!isArchIntro && (relFile.includes("architecture/") || relFile.includes("06_runtime_view") || relFile.includes("08_security_concept") || relFile.includes("09_decisions"))) {
3412
+ architectureViews.push({
3413
+ file: relFile,
3414
+ id: frontmatter.id ? String(frontmatter.id).trim() : void 0,
3415
+ type: typeof frontmatter.type === "string" ? frontmatter.type.trim() : void 0,
3416
+ content
3417
+ });
3394
3418
  }
3395
3419
  } catch {
3396
3420
  }
@@ -3473,6 +3497,14 @@ function verifyTraceability(options = {}) {
3473
3497
  productTraces.push(...associatedHandoff.subgraph.abuseCases);
3474
3498
  }
3475
3499
  }
3500
+ for (const [artId, artEntry] of artifactMap.entries()) {
3501
+ if (artEntry.type === "architecture-introduction" || artId.startsWith("ARCH-INTRO-")) {
3502
+ const citedGoals = artEntry.frontmatter["cites-quality-goals"];
3503
+ if (Array.isArray(citedGoals) && citedGoals.includes(reqId)) {
3504
+ productTraces.push(artId);
3505
+ }
3506
+ }
3507
+ }
3476
3508
  const validProductTraces = Array.from(
3477
3509
  new Set(productTraces.filter((id) => artifactMap.has(id)))
3478
3510
  );
@@ -3493,8 +3525,9 @@ function verifyTraceability(options = {}) {
3493
3525
  }
3494
3526
  for (const view of architectureViews) {
3495
3527
  if (view.content.includes(reqId)) {
3496
- const viewName = path8.basename(view.file);
3497
- if (!archTraces.includes(viewName)) archTraces.push(viewName);
3528
+ const isEntityWithId = view.type === "component" || view.type === "adr" || view.type === "architecture-decision-record" || view.type === "security-enclave" || view.id !== void 0 && (view.id.startsWith("CMP-") || view.id.startsWith("ADR-") || view.id.startsWith("SEC-ENC-"));
3529
+ const viewIdentifier = isEntityWithId && view.id ? view.id : path8.basename(view.file);
3530
+ if (!archTraces.includes(viewIdentifier)) archTraces.push(viewIdentifier);
3498
3531
  }
3499
3532
  }
3500
3533
  const uniqueArchTraces = Array.from(new Set(archTraces));
@@ -3831,7 +3864,7 @@ function scanAllTestArtifacts(rootDir) {
3831
3864
  for (const e of entries) {
3832
3865
  const fullPath = path8.join(dir, e.name);
3833
3866
  if (e.isDirectory()) {
3834
- if (!["node_modules", ".git", "dist", ".changeset", "scratch", "fixtures", "templates", "examples"].includes(e.name)) {
3867
+ if (!["node_modules", ".git", "dist", "bin", "obj", "build", "out", "target", "coverage", ".changeset", "scratch", "fixtures", "templates", "examples", ".vs", ".idea"].includes(e.name)) {
3835
3868
  walk(fullPath);
3836
3869
  }
3837
3870
  } else {
@@ -7136,7 +7169,31 @@ function buildGraphElements(rootDir = process.cwd()) {
7136
7169
  for (const n of pdac.drifts) {
7137
7170
  driftTargets.add(n.targetId);
7138
7171
  }
7172
+ const NON_GRAPH_TYPES = /* @__PURE__ */ new Set([
7173
+ "term",
7174
+ "handoff",
7175
+ "agent-handoff",
7176
+ "tasks",
7177
+ "task-plan",
7178
+ "spec-change-proposal",
7179
+ "proposal",
7180
+ "spec-design",
7181
+ "spec-change-design",
7182
+ "design",
7183
+ "delivery-spec",
7184
+ "spec",
7185
+ "pre-merge-audit-report",
7186
+ "user-design-feedback",
7187
+ "release-kpis",
7188
+ "session-telemetry",
7189
+ "license-manifest",
7190
+ "mcp-config",
7191
+ "user-manual",
7192
+ "production-manual",
7193
+ "architecture-glossary"
7194
+ ]);
7139
7195
  const scannedArtifactsMap = /* @__PURE__ */ new Map();
7196
+ const archViewByBasename = /* @__PURE__ */ new Map();
7140
7197
  const artifactDirs = ["product", "architecture", "specs", "security"];
7141
7198
  for (const dirName of artifactDirs) {
7142
7199
  const fullDirPath = path8.join(rootDir, dirName);
@@ -7146,6 +7203,7 @@ function buildGraphElements(rootDir = process.cwd()) {
7146
7203
  try {
7147
7204
  const fileContent = fs6.readFileSync(f, "utf-8");
7148
7205
  const relPath = path8.relative(rootDir, f).replace(/\\/g, "/");
7206
+ if (relPath.includes("/changes/") || relPath.startsWith("changes/")) continue;
7149
7207
  const match = fileContent.match(/^---\r?\n([\s\S]*?)\r?\n---/);
7150
7208
  if (!match) continue;
7151
7209
  let fm = {};
@@ -7157,8 +7215,14 @@ function buildGraphElements(rootDir = process.cwd()) {
7157
7215
  const id = typeof fm.id === "string" ? fm.id.trim() : void 0;
7158
7216
  if (!id) continue;
7159
7217
  let type = typeof fm.type === "string" ? fm.type.trim() : "";
7218
+ if (NON_GRAPH_TYPES.has(type) || id.startsWith("TERM-") || id.startsWith("HOF-") || id.startsWith("AHOF-") || id.startsWith("CHG-") || id.startsWith("DSG-") || id.startsWith("SPEC-") || id.startsWith("TSK-") || id.startsWith("UDF-") || id.startsWith("AUDIT-")) {
7219
+ continue;
7220
+ }
7160
7221
  let layer = "product";
7161
- if (id.startsWith("JRN-") || type === "journey") {
7222
+ if (id.startsWith("ARCH-INTRO-") || type === "architecture-introduction") {
7223
+ type = "architecture-introduction";
7224
+ layer = "product";
7225
+ } else if (id.startsWith("JRN-") || type === "journey") {
7162
7226
  type = "journey";
7163
7227
  layer = "product";
7164
7228
  } else if (id.startsWith("ACT-THREAT-") || type === "threat-actor") {
@@ -7191,16 +7255,19 @@ function buildGraphElements(rootDir = process.cwd()) {
7191
7255
  } else if (id.startsWith("SEC-REQ-") || type === "security-requirement") {
7192
7256
  type = "security-requirement";
7193
7257
  layer = "requirement";
7194
- } else if (id.startsWith("FR-") || id.startsWith("QR-") || id.startsWith("CON-") || type === "requirement") {
7258
+ } else if (id.startsWith("FR-") || id.startsWith("QR-") || id.startsWith("CON-") || type === "requirement" || type === "functional-requirement" || type === "quality-requirement") {
7195
7259
  type = "requirement";
7196
7260
  layer = "requirement";
7261
+ } else if (id.startsWith("ARCH-") || relPath.includes("architecture/")) {
7262
+ type = "architecture";
7263
+ layer = "architecture";
7197
7264
  } else {
7198
7265
  type = type || "product";
7199
7266
  layer = "product";
7200
7267
  }
7201
7268
  const title = typeof fm.title === "string" ? fm.title.trim() : id;
7202
7269
  const content = fileContent.replace(/^---\r?\n[\s\S]*?\r?\n---/, "").trim();
7203
- scannedArtifactsMap.set(id, {
7270
+ const scannedArt = {
7204
7271
  id,
7205
7272
  type,
7206
7273
  title,
@@ -7208,7 +7275,12 @@ function buildGraphElements(rootDir = process.cwd()) {
7208
7275
  filePath: relPath,
7209
7276
  content,
7210
7277
  frontmatter: fm
7211
- });
7278
+ };
7279
+ if (layer === "architecture" && type === "architecture") {
7280
+ archViewByBasename.set(path8.basename(relPath), scannedArt);
7281
+ } else {
7282
+ scannedArtifactsMap.set(id, scannedArt);
7283
+ }
7212
7284
  } catch {
7213
7285
  }
7214
7286
  }
@@ -7217,7 +7289,7 @@ function buildGraphElements(rootDir = process.cwd()) {
7217
7289
  for (const r of trace.rows) {
7218
7290
  const isOk = (r.productStatus === "COMPLIANT" || r.productStatus === "CONFORME") && (r.archStatus === "COMPLIANT" || r.archStatus === "CONFORME") && (r.testStatus === "COMPLIANT" || r.testStatus === "CONFORME");
7219
7291
  const status = isOk ? "COMPLIANT" : "ORPHAN";
7220
- const reqType = r.id.startsWith("SEC-REQ-") ? "security-requirement" : r.type || "requirement";
7292
+ const reqType = r.id.startsWith("SEC-REQ-") ? "security-requirement" : "requirement";
7221
7293
  nodeMap.set(r.id, {
7222
7294
  id: r.id,
7223
7295
  label: r.id,
@@ -7243,16 +7315,18 @@ function buildGraphElements(rootDir = process.cwd()) {
7243
7315
  const u = up.trim();
7244
7316
  if (u && u !== "NINGUNO" && u !== "NONE") {
7245
7317
  const isAbuse = u.startsWith("ABUSE-");
7318
+ const isArchIntro = u.startsWith("ARCH-INTRO-");
7319
+ const rel = isAbuse ? "mitigated-by" : isArchIntro ? "defines-goal" : "derives-from";
7246
7320
  addEdge({
7247
7321
  id: `edge-${u}-${r.id}`,
7248
7322
  source: u,
7249
7323
  target: r.id,
7250
- label: isAbuse ? "mitigated-by" : "derives-from",
7251
- relation: isAbuse ? "mitigated-by" : "derives-from",
7324
+ label: rel,
7325
+ relation: rel,
7252
7326
  status
7253
7327
  });
7254
7328
  if (!nodeMap.has(u)) {
7255
- const upType = u.startsWith("ACT-") ? "actor" : u.startsWith("UC-") ? "use-case" : u.startsWith("BR-") ? "business-rule" : u.startsWith("ABUSE-") ? "abuse-case" : "product";
7329
+ const upType = u.startsWith("ARCH-INTRO-") ? "architecture-introduction" : u.startsWith("ACT-") ? "actor" : u.startsWith("UC-") ? "use-case" : u.startsWith("BR-") ? "business-rule" : u.startsWith("ABUSE-") ? "abuse-case" : "product";
7256
7330
  nodeMap.set(u, {
7257
7331
  id: u,
7258
7332
  label: u,
@@ -7277,13 +7351,16 @@ function buildGraphElements(rootDir = process.cwd()) {
7277
7351
  });
7278
7352
  if (!nodeMap.has(a)) {
7279
7353
  const aType = a.startsWith("CMP-") ? "component" : a.startsWith("ADR-") ? "adr" : a.startsWith("SEC-ENC-") ? "security-enclave" : "architecture";
7354
+ const viewArt = archViewByBasename.get(a);
7280
7355
  nodeMap.set(a, {
7281
7356
  id: a,
7282
7357
  label: a,
7283
- title: a,
7358
+ title: viewArt?.title || a,
7284
7359
  type: aType,
7285
7360
  layer: "architecture",
7286
- status: "COMPLIANT"
7361
+ status: "COMPLIANT",
7362
+ filePath: viewArt?.filePath,
7363
+ content: viewArt?.content
7287
7364
  });
7288
7365
  }
7289
7366
  }
@@ -7347,7 +7424,80 @@ function buildGraphElements(rootDir = process.cwd()) {
7347
7424
  }
7348
7425
  for (const art of scannedArtifactsMap.values()) {
7349
7426
  const fm = art.frontmatter;
7350
- if (art.type === "journey") {
7427
+ if (art.type === "architecture-introduction") {
7428
+ const citedActors = Array.isArray(fm["cites-product-actors"]) ? fm["cites-product-actors"] : [];
7429
+ for (const actId of citedActors) {
7430
+ if (typeof actId === "string" && actId.trim()) {
7431
+ const a = actId.trim();
7432
+ if (!nodeMap.has(a)) {
7433
+ nodeMap.set(a, {
7434
+ id: a,
7435
+ label: a,
7436
+ title: a,
7437
+ type: "actor",
7438
+ layer: "product",
7439
+ status: "COMPLIANT"
7440
+ });
7441
+ }
7442
+ addEdge({
7443
+ id: `edge-${art.id}-${a}`,
7444
+ source: art.id,
7445
+ target: a,
7446
+ label: "identifies",
7447
+ relation: "identifies",
7448
+ status: "COMPLIANT"
7449
+ });
7450
+ }
7451
+ }
7452
+ const citedUcs = Array.isArray(fm["cites-use-cases"]) ? fm["cites-use-cases"] : [];
7453
+ for (const ucId of citedUcs) {
7454
+ if (typeof ucId === "string" && ucId.trim()) {
7455
+ const u = ucId.trim();
7456
+ if (!nodeMap.has(u)) {
7457
+ nodeMap.set(u, {
7458
+ id: u,
7459
+ label: u,
7460
+ title: u,
7461
+ type: "use-case",
7462
+ layer: "product",
7463
+ status: "COMPLIANT"
7464
+ });
7465
+ }
7466
+ addEdge({
7467
+ id: `edge-${art.id}-${u}`,
7468
+ source: art.id,
7469
+ target: u,
7470
+ label: "envisions",
7471
+ relation: "envisions",
7472
+ status: "COMPLIANT"
7473
+ });
7474
+ }
7475
+ }
7476
+ const citedGoals = Array.isArray(fm["cites-quality-goals"]) ? fm["cites-quality-goals"] : [];
7477
+ for (const qrId of citedGoals) {
7478
+ if (typeof qrId === "string" && qrId.trim()) {
7479
+ const q = qrId.trim();
7480
+ if (!nodeMap.has(q)) {
7481
+ nodeMap.set(q, {
7482
+ id: q,
7483
+ label: q,
7484
+ title: q,
7485
+ type: q.startsWith("SEC-REQ-") ? "security-requirement" : "requirement",
7486
+ layer: "requirement",
7487
+ status: "COMPLIANT"
7488
+ });
7489
+ }
7490
+ addEdge({
7491
+ id: `edge-${art.id}-${q}`,
7492
+ source: art.id,
7493
+ target: q,
7494
+ label: "defines-goal",
7495
+ relation: "defines-goal",
7496
+ status: "COMPLIANT"
7497
+ });
7498
+ }
7499
+ }
7500
+ } else if (art.type === "journey") {
7351
7501
  const persona = typeof fm.persona === "string" ? fm.persona : typeof fm.actor === "string" ? fm.actor : void 0;
7352
7502
  if (persona) {
7353
7503
  if (!nodeMap.has(persona)) {
@@ -7415,7 +7565,31 @@ function buildGraphElements(rootDir = process.cwd()) {
7415
7565
  status: "COMPLIANT"
7416
7566
  });
7417
7567
  }
7418
- const governedBy = Array.isArray(fm["governed-by"]) ? fm["governed-by"] : [];
7568
+ const supportingActors = Array.isArray(fm["supporting-actors"]) ? fm["supporting-actors"] : Array.isArray(fm["secondary-actors"]) ? fm["secondary-actors"] : [];
7569
+ for (const actorId of supportingActors) {
7570
+ if (typeof actorId === "string" && actorId.trim()) {
7571
+ const a = actorId.trim();
7572
+ if (!nodeMap.has(a)) {
7573
+ nodeMap.set(a, {
7574
+ id: a,
7575
+ label: a,
7576
+ title: a,
7577
+ type: "actor",
7578
+ layer: "product",
7579
+ status: "COMPLIANT"
7580
+ });
7581
+ }
7582
+ addEdge({
7583
+ id: `edge-${a}-${art.id}`,
7584
+ source: a,
7585
+ target: art.id,
7586
+ label: "initiates",
7587
+ relation: "initiates",
7588
+ status: "COMPLIANT"
7589
+ });
7590
+ }
7591
+ }
7592
+ const governedBy = Array.isArray(fm["governed-by"]) ? fm["governed-by"] : Array.isArray(fm["business-rules"]) ? fm["business-rules"] : [];
7419
7593
  for (const ruleId of governedBy) {
7420
7594
  if (typeof ruleId === "string" && ruleId.trim()) {
7421
7595
  const r = ruleId.trim();
@@ -7476,7 +7650,31 @@ function buildGraphElements(rootDir = process.cwd()) {
7476
7650
  status: "COMPLIANT"
7477
7651
  });
7478
7652
  }
7479
- const rawTargets = fm["targets-use-case"] ? [fm["targets-use-case"]] : Array.isArray(fm["targets-use-cases"]) ? fm["targets-use-cases"] : Array.isArray(fm["targets"]) ? fm["targets"] : [];
7653
+ const secondaryThreatActors = Array.isArray(fm["secondary-actors"]) ? fm["secondary-actors"] : Array.isArray(fm["supporting-actors"]) ? fm["supporting-actors"] : [];
7654
+ for (const secActor of secondaryThreatActors) {
7655
+ if (typeof secActor === "string" && secActor.trim()) {
7656
+ const sa = secActor.trim();
7657
+ if (!nodeMap.has(sa)) {
7658
+ nodeMap.set(sa, {
7659
+ id: sa,
7660
+ label: sa,
7661
+ title: sa,
7662
+ type: sa.startsWith("ACT-THREAT-") ? "threat-actor" : "actor",
7663
+ layer: "product",
7664
+ status: "COMPLIANT"
7665
+ });
7666
+ }
7667
+ addEdge({
7668
+ id: `edge-${sa}-${art.id}`,
7669
+ source: sa,
7670
+ target: art.id,
7671
+ label: "threatens",
7672
+ relation: "threatens",
7673
+ status: "COMPLIANT"
7674
+ });
7675
+ }
7676
+ }
7677
+ const rawTargets = Array.isArray(fm["targets-use-case"]) ? fm["targets-use-case"] : typeof fm["targets-use-case"] === "string" ? [fm["targets-use-case"]] : Array.isArray(fm["targets-use-cases"]) ? fm["targets-use-cases"] : Array.isArray(fm["targets"]) ? fm["targets"] : [];
7480
7678
  for (const tgt of rawTargets) {
7481
7679
  if (typeof tgt === "string" && tgt.trim()) {
7482
7680
  const t = tgt.trim();
@@ -7541,6 +7739,40 @@ function buildGraphElements(rootDir = process.cwd()) {
7541
7739
  });
7542
7740
  }
7543
7741
  }
7742
+ } else if (art.type === "adr") {
7743
+ const affectedComponents = Array.isArray(fm["affects-components"]) ? fm["affects-components"] : typeof fm["affects-components"] === "string" ? [fm["affects-components"]] : [];
7744
+ for (const cmpId of affectedComponents) {
7745
+ if (typeof cmpId === "string" && cmpId.trim()) {
7746
+ const c = cmpId.trim();
7747
+ if (nodeMap.has(c)) {
7748
+ addEdge({
7749
+ id: `edge-${art.id}-${c}`,
7750
+ source: art.id,
7751
+ target: c,
7752
+ label: "governs",
7753
+ relation: "governs",
7754
+ status: "COMPLIANT"
7755
+ });
7756
+ }
7757
+ }
7758
+ }
7759
+ } else if (art.type === "security-enclave") {
7760
+ const enforcedBy = Array.isArray(fm["enforced-by"]) ? fm["enforced-by"] : typeof fm["enforced-by"] === "string" ? [fm["enforced-by"]] : [];
7761
+ for (const secReq of enforcedBy) {
7762
+ if (typeof secReq === "string" && secReq.trim()) {
7763
+ const s = secReq.trim();
7764
+ if (nodeMap.has(s)) {
7765
+ addEdge({
7766
+ id: `edge-${s}-${art.id}`,
7767
+ source: s,
7768
+ target: art.id,
7769
+ label: "satisfies",
7770
+ relation: "satisfies",
7771
+ status: "COMPLIANT"
7772
+ });
7773
+ }
7774
+ }
7775
+ }
7544
7776
  }
7545
7777
  }
7546
7778
  for (const [id, node] of nodeMap.entries()) {
@@ -7690,6 +7922,9 @@ function collectDashboardMetrics(rootDir) {
7690
7922
  function escapeHtml(str) {
7691
7923
  return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&#39;");
7692
7924
  }
7925
+ function safeJsonForScript(data) {
7926
+ return JSON.stringify(data).replace(/</g, "\\u003c").replace(/>/g, "\\u003e").replace(/&/g, "\\u0026").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
7927
+ }
7693
7928
  function renderAutonomyModeSection(mode, statusIcon, count, roleDescription, allTasks = []) {
7694
7929
  const modeTasks = (allTasks || []).filter(
7695
7930
  (t) => (t.task?.autonomyMode || "").toUpperCase() === mode
@@ -7801,14 +8036,14 @@ function renderAutonomyModeSection(mode, statusIcon, count, roleDescription, all
7801
8036
  }
7802
8037
  function renderDashboardHtml(graphData, metricsData, title = "AI-SDLC: Dashboard Interactivo de Trazabilidad y Calidad") {
7803
8038
  const cytoscapeScript = getCytoscapeScript();
7804
- const elementsJson = JSON.stringify(graphData.elements);
8039
+ const elementsJson = safeJsonForScript(graphData.elements);
7805
8040
  const now = (/* @__PURE__ */ new Date()).toISOString();
7806
8041
  return `<!DOCTYPE html>
7807
8042
  <html lang="es">
7808
8043
  <head>
7809
8044
  <meta charset="UTF-8">
7810
8045
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7811
- <title>${title}</title>
8046
+ <title>${escapeHtml(title)}</title>
7812
8047
  <style>
7813
8048
  :root {
7814
8049
  --bg-dark: #0f172a;
@@ -8111,6 +8346,7 @@ function renderDashboardHtml(graphData, metricsData, title = "AI-SDLC: Dashboard
8111
8346
  <div class="tool-row">
8112
8347
  <select id="type-filter" class="tool-select" onchange="filterType(this.value)">
8113
8348
  <option value="all">All Typologies</option>
8349
+ <option value="architecture-introduction">Vision &amp; Goals (ARCH-INTRO)</option>
8114
8350
  <option value="journey">Journeys (JRN)</option>
8115
8351
  <option value="actor">Actors (ACT)</option>
8116
8352
  <option value="use-case">Use Cases (UC)</option>
@@ -8145,6 +8381,7 @@ function renderDashboardHtml(graphData, metricsData, title = "AI-SDLC: Dashboard
8145
8381
  <div class="graph-legend">
8146
8382
  <div class="legend-header">Node Typologies</div>
8147
8383
  <div class="legend-items">
8384
+ <div class="legend-item"><span class="legend-dot" style="background:#0f766e; border:2px solid #2dd4bf;"></span><strong>Vision / Goals</strong> (ARCH-INTRO)</div>
8148
8385
  <div class="legend-item"><span class="legend-dot" style="background:#581c87; border:2px solid #c084fc;"></span><strong>Journey</strong> (JRN)</div>
8149
8386
  <div class="legend-item"><span class="legend-dot" style="background:#065f46; border:2px solid #34d399; border-radius:50%;"></span><strong>Actor</strong> (ACT)</div>
8150
8387
  <div class="legend-item"><span class="legend-dot" style="background:#0369a1; border:2px solid #38bdf8;"></span><strong>Use Case</strong> (UC)</div>
@@ -8183,12 +8420,12 @@ function renderDashboardHtml(graphData, metricsData, title = "AI-SDLC: Dashboard
8183
8420
  ${metricsData.trace.rows.map((r) => {
8184
8421
  const isRowOk = (r.productStatus === "COMPLIANT" || r.productStatus === "CONFORME") && (r.archStatus === "COMPLIANT" || r.archStatus === "CONFORME") && (r.testStatus === "COMPLIANT" || r.testStatus === "CONFORME");
8185
8422
  return `
8186
- <tr onclick="focusNode('${r.id}')" onmouseenter="startRowTooltip(event, '${r.id}')" onmouseleave="cancelRowTooltip()" onmousemove="updateRowTooltipPos(event)" style="cursor: pointer;">
8187
- <td><strong><code>${r.id}</code></strong></td>
8188
- <td>${r.title}</td>
8189
- <td><code>${r.productTraces}</code></td>
8190
- <td><code>${r.archTraces}</code></td>
8191
- <td><code>${r.testTraces}</code></td>
8423
+ <tr onclick="focusNode('${escapeHtml(String(r.id || ""))}')" onmouseenter="startRowTooltip(event, '${escapeHtml(String(r.id || ""))}')" onmouseleave="cancelRowTooltip()" onmousemove="updateRowTooltipPos(event)" style="cursor: pointer;">
8424
+ <td><strong><code>${escapeHtml(String(r.id || ""))}</code></strong></td>
8425
+ <td>${escapeHtml(String(r.title || ""))}</td>
8426
+ <td><code>${escapeHtml(String(r.productTraces || ""))}</code></td>
8427
+ <td><code>${escapeHtml(String(r.archTraces || ""))}</code></td>
8428
+ <td><code>${escapeHtml(String(r.testTraces || ""))}</code></td>
8192
8429
  <td><span class="badge ${isRowOk ? "badge-green" : "badge-amber"}">${isRowOk ? "COMPLIANT" : "ORPHAN"}</span></td>
8193
8430
  </tr>
8194
8431
  `;
@@ -8401,7 +8638,7 @@ function renderDashboardHtml(graphData, metricsData, title = "AI-SDLC: Dashboard
8401
8638
  </script>
8402
8639
  <script>
8403
8640
  var elements = ${elementsJson};
8404
- var govTasks = ${JSON.stringify(metricsData.gov.tasks || [])};
8641
+ var govTasks = ${safeJsonForScript(metricsData.gov.tasks || [])};
8405
8642
  var cy = null;
8406
8643
 
8407
8644
  var reqHoverTimer = null;
@@ -8564,6 +8801,19 @@ function renderDashboardHtml(graphData, metricsData, title = "AI-SDLC: Dashboard
8564
8801
  }
8565
8802
  },
8566
8803
  // Typology Styles
8804
+ {
8805
+ selector: 'node.type-architecture-introduction',
8806
+ style: {
8807
+ 'shape': 'round-rectangle',
8808
+ 'background-color': '#0f766e',
8809
+ 'border-color': '#2dd4bf',
8810
+ 'border-width': 3,
8811
+ 'width': 92,
8812
+ 'height': 44,
8813
+ 'font-weight': 'bold',
8814
+ 'text-max-width': '86px'
8815
+ }
8816
+ },
8567
8817
  {
8568
8818
  selector: 'node.type-journey',
8569
8819
  style: {
@@ -8680,7 +8930,7 @@ function renderDashboardHtml(graphData, metricsData, title = "AI-SDLC: Dashboard
8680
8930
  },
8681
8931
  // Fallback Layer styles if type is missing
8682
8932
  {
8683
- selector: 'node.layer-product:not(.type-journey):not(.type-actor):not(.type-use-case):not(.type-usecase):not(.type-business-rule):not(.type-rule):not(.type-abuse-case):not(.type-threat-actor)',
8933
+ selector: 'node.layer-product:not(.type-architecture-introduction):not(.type-journey):not(.type-actor):not(.type-use-case):not(.type-usecase):not(.type-business-rule):not(.type-rule):not(.type-abuse-case):not(.type-threat-actor)',
8684
8934
  style: { 'shape': 'diamond', 'background-color': '#0e7490', 'border-color': '#38bdf8', 'width': 50, 'height': 50 }
8685
8935
  },
8686
8936
  {
@@ -8709,6 +8959,10 @@ function renderDashboardHtml(graphData, metricsData, title = "AI-SDLC: Dashboard
8709
8959
  }
8710
8960
  },
8711
8961
  // Semantic Edge Relations
8962
+ {
8963
+ selector: 'edge.relation-identifies, edge.relation-envisions, edge.relation-defines-goal',
8964
+ style: { 'line-color': '#2dd4bf', 'target-arrow-color': '#2dd4bf', 'width': 2 }
8965
+ },
8712
8966
  {
8713
8967
  selector: 'edge.relation-pursues',
8714
8968
  style: { 'line-color': '#c084fc', 'target-arrow-color': '#c084fc', 'width': 2 }
@@ -8849,7 +9103,10 @@ function renderDashboardHtml(graphData, metricsData, title = "AI-SDLC: Dashboard
8849
9103
  var typeName = (data.type || data.layer || 'node').toLowerCase();
8850
9104
  var typeBadgeColor = '#334155';
8851
9105
  var typeTitle = (data.type || data.layer || 'NODE').toUpperCase();
8852
- if (typeName === 'journey') {
9106
+ if (typeName === 'architecture-introduction') {
9107
+ typeBadgeColor = '#0f766e';
9108
+ typeTitle = 'VISION & GOALS (ARCH-INTRO)';
9109
+ } else if (typeName === 'journey') {
8853
9110
  typeBadgeColor = '#581c87';
8854
9111
  typeTitle = 'JOURNEY (JRN)';
8855
9112
  } else if (typeName === 'actor') {
@@ -8915,6 +9172,11 @@ function renderDashboardHtml(graphData, metricsData, title = "AI-SDLC: Dashboard
8915
9172
  if (!cy) return;
8916
9173
  if (type === 'all') {
8917
9174
  cy.elements().show();
9175
+ } else if (type === 'architecture-introduction') {
9176
+ cy.elements().hide();
9177
+ var nodes = cy.nodes('.type-architecture-introduction');
9178
+ nodes.show();
9179
+ nodes.connectedEdges().show();
8918
9180
  } else if (type === 'journey') {
8919
9181
  cy.elements().hide();
8920
9182
  var nodes = cy.nodes('.type-journey');
@@ -9034,9 +9296,10 @@ function renderDashboardHtml(graphData, metricsData, title = "AI-SDLC: Dashboard
9034
9296
 
9035
9297
  function switchTab(name) {
9036
9298
  hideTooltip();
9037
- document.querySelectorAll('.tab-btn').forEach(function(b) { b.classList.remove('active'); });
9299
+ document.querySelectorAll('.tab-btn').forEach(function(b) {
9300
+ b.classList.toggle('active', b.getAttribute('onclick') === "switchTab('" + name + "')");
9301
+ });
9038
9302
  document.querySelectorAll('.tab-panel').forEach(function(p) { p.classList.remove('active'); });
9039
- event.target?.classList?.add('active');
9040
9303
  var p = document.getElementById('tab-' + name);
9041
9304
  if (p) p.classList.add('active');
9042
9305
  if (name === 'graph' && cy) {
@@ -9250,14 +9513,20 @@ var SPECIALIZED_AGENT_TEMPLATES = [
9250
9513
  {
9251
9514
  id: "agent-product-analyst",
9252
9515
  title: "Product Analyst / Scribe",
9253
- description: "Assist the human Product Owner in exploring, modeling, and refining product definition using ProductShape (ACT-*, UC-*, FR-*, QR-*, BR-*).",
9254
- globs: "product/**, specs/product/**, templates/product/**",
9516
+ description: "Assist the human Product Owner in exploring, modeling, and refining product definition using NAF v4 Enterprise & Capability Vision (ARCH-INTRO-*) and ProductShape (ACT-*, UC-*, FR-*, QR-*, BR-*).",
9517
+ globs: "product/**, specs/product/**, templates/product/**, templates/architecture/introduction-and-goals.template.md",
9255
9518
  body: `## Role & Mission
9256
9519
  - **Role**: Product Analyst Agent (\`agent-product-analyst\`) and Technical Scribe (AI as Scribe) of the AI-SDLC framework.
9257
- - **Mission**: Transform natural language business intent into canonical ProductShape artifacts (\`ACT-*\`, \`JRN-*\`, \`UC-*\`, \`BR-*\`, \`TERM-*\`, \`FR-*\`, \`QR-*\`) 100% compliant with JSON schemas.
9520
+ - **Mission**: Capture new product intent via NAF v4 Enterprise & Capability Vision (\`ARCH-INTRO-*\`) and transform natural language business intent into canonical ProductShape artifacts (\`ACT-*\`, \`JRN-*\`, \`UC-*\`, \`BR-*\`, \`TERM-*\`, \`FR-*\`, \`QR-*\`) 100% compliant with JSON schemas.
9521
+
9522
+ ## Phase 0: Greenfield Product Inception (New Product Intent)
9523
+ - **Trigger**: When the user expresses intent to create a new product from scratch with the framework (or when \`specs/product/\` has no baseline product model yet), initiate **Phase 0** before any SDD change (\`change:new\`) or implementation:
9524
+ 1. **Structured Discovery Interview (3\u20135 Questions)**: Ask a concise 3\u20135 question interview to clarify: (a) core problem and mission statement, (b) primary actors/stakeholders (\`ACT-*\`), (c) essential MVP capabilities/use cases (\`UC-*\`), and (d) top 3\u20135 priority quality goals and constraints (\`QR-*\`, \`CON-*\`).
9525
+ 2. **Enterprise & Capability Vision (\`ARCH-INTRO-001\`)**: Draft the root vision artifact (\`type: architecture-introduction\`, \`naf-perspective: "Enterprise & Capability"\`, \`status: draft\`) using \`templates/architecture/introduction-and-goals.template.md\` (\`schemas/architecture/introduction-and-goals.schema.json\`).
9526
+ 3. **ProductShape Derivation**: Derive the initial draft artifacts (\`ACT-*\`, \`UC-*\`, \`BR-*\`, \`FR-*\`, \`QR-*\` with \`status: draft\`) in \`specs/product/\` linked to \`ARCH-INTRO-001\` for human Product Owner review.
9258
9527
 
9259
9528
  ## Operational Directives & Guardrails
9260
- - Every generated artifact must strictly adhere to the canonical Markdown and YAML frontmatter format in \`schemas/product/\`.
9529
+ - Every generated artifact must strictly adhere to the canonical Markdown and YAML frontmatter format in \`schemas/product/\` and \`schemas/architecture/introduction-and-goals.schema.json\`.
9261
9530
  - \`status\` ALWAYS starts as \`draft\`. Creating artifacts directly as \`active\` or \`approved\` is forbidden.
9262
9531
  - Always write relationships in their single canonical direction (\`derives-from: [UC-*]\`, \`primary-actor: ACT-*\`).
9263
9532
  - In \`UC-*\`, format section \`1. Intent and Outcome\` under the standard English structure: \`As a <ACT-ID>... I want <action>... To <outcome>...\`.
@@ -9505,9 +9774,10 @@ This file defines the operational rules and specialized role mappings for Google
9505
9774
  ## 2. Specialized Roles Mapping
9506
9775
 
9507
9776
  ### 1. \`agent-product-analyst\` (Product Analyst / Scribe)
9508
- - **Mission**: Assist in defining and refining the product model using ProductShape.
9777
+ - **Mission**: Capture new product intent via NAF v4 Enterprise & Capability Vision (\`ARCH-INTRO-*\`) and define/refine the product model using ProductShape.
9778
+ - **Phase 0 (Greenfield Product Inception)**: When creating a new product from scratch (or when \`specs/product/\` has no baseline), run a concise 3\u20135 question discovery interview (mission/problem, actors \`ACT-*\`, MVP capabilities \`UC-*\`, priority quality goals \`QR-*\`), draft \`ARCH-INTRO-001\` (\`templates/architecture/introduction-and-goals.template.md\`), and derive initial \`ACT-*\`, \`UC-*\`, \`BR-*\`, \`FR-*\`, and \`QR-*\` drafts.
9509
9779
  - **Input**: Business intent or natural language specification.
9510
- - **Output**: Markdown artifacts with YAML frontmatter compliant with \`schemas/product/\` (\`ACT-*\`, \`UC-*\`, \`FR-*\`, \`QR-*\`, \`BR-*\`).
9780
+ - **Output**: Markdown artifacts with YAML frontmatter compliant with \`schemas/architecture/introduction-and-goals.schema.json\` (\`ARCH-INTRO-*\`) and \`schemas/product/\` (\`ACT-*\`, \`UC-*\`, \`FR-*\`, \`QR-*\`, \`BR-*\`).
9511
9781
  - **Guardrails**:
9512
9782
  - \`status\` always starts in \`draft\`.
9513
9783
  - In \`UC-*\`, format section \`1. Intent and Outcome\` under the standard English structure: \`As a <ACT-ID>... I want <action>... To <outcome>...\`.
@@ -9638,7 +9908,7 @@ Before finalizing changes or proposing commits:
9638
9908
  ## 4. Specialized Roles Mapping
9639
9909
  | Role | Mission (summary) |
9640
9910
  | :--- | :--- |
9641
- | \`agent-product-analyst\` | ProductShape artifacts (\`ACT-*\`, \`UC-*\`, \`FR-*\`, \`QR-*\`, \`BR-*\`); status starts \`draft\`. |
9911
+ | \`agent-product-analyst\` | Phase 0 NAF v4 Enterprise & Capability Vision (\`ARCH-INTRO-*\`, 3\u20135 question interview) + ProductShape artifacts (\`ACT-*\`, \`UC-*\`, \`FR-*\`, \`QR-*\`, \`BR-*\`); status starts \`draft\`. |
9642
9912
  | \`agent-threat-modeler\` | STRIDE/OWASP threat triad (\`ACT-THREAT-*\`, \`ABUSE-*\`, \`SEC-REQ-*\`). |
9643
9913
  | \`agent-system-architect\` | Modular arc42 + NAF v4 architecture (\`CMP-*\`, Mermaid diagrams, \`ADR-*\`). |
9644
9914
  | \`agent-qa-engineer\` | FAILING BDD/Gherkin suites before \`agent-developer\` writes code. |
@@ -9673,6 +9943,7 @@ Canonical Reference: [\`process/09_agent_protocols.md\`](process/09_agent_protoc
9673
9943
 
9674
9944
  ## 1. Identifier Taxonomy and JSON Schemas
9675
9945
  Every generated or modified artifact must follow canonical naming and satisfy its schema in \`schemas/\`:
9946
+ - Enterprise & Capability Vision (Phase 0): \`ARCH-INTRO-[NUM3]\` (\`schemas/architecture/introduction-and-goals.schema.json\`)
9676
9947
  - Product Actors: \`ACT-[SUFFIX]\` (\`schemas/product/actor.schema.json\`)
9677
9948
  - Use Cases: \`UC-[SUFFIX]\` (\`schemas/product/use-case.schema.json\`)
9678
9949
  - Functional Requirements: \`FR-[SUFFIX]-[NUM3]\` (\`schemas/product/requirement.schema.json\`)
@@ -9683,6 +9954,7 @@ Every generated or modified artifact must follow canonical naming and satisfy it
9683
9954
  - Security Requirements: \`SEC-REQ-[SUFFIX]\` (\`schemas/security/security-req.schema.json\`)
9684
9955
 
9685
9956
  ## 2. "AI as Scribe" Protocol Rules
9957
+ - **Phase 0 (New Product Inception)**: When starting a new product, conduct a 3\u20135 question discovery interview, instantiate \`ARCH-INTRO-001\` (\`naf-perspective: "Enterprise & Capability"\`) in \`draft\`, and derive \`ACT-*\`, \`UC-*\`, \`BR-*\`, \`FR-*\`, and \`QR-*\`.
9686
9958
  - **Initial Status**: Every new artifact strictly starts with \`status: draft\`. Creating artifacts directly as \`active\` or \`approved\` is forbidden.
9687
9959
  - **Gherkin Acceptance Criteria**: In every \`FR-*\` requirement, include a \`\`\`gherkin block with tags \`@<ID> @automated @regression\` and at least one \`Scenario Outline\` with an \`Examples\` data table.
9688
9960
  - **Security Mitigations**: Every \`SEC-REQ-*\` must include negative Gherkin tests (\`@security @mitigation\`).
@@ -9764,9 +10036,10 @@ npx tsx packages/cli/src/index.ts sdd integrate --change <chg-id>
9764
10036
  ## 3. Specialized Roles Mapping
9765
10037
 
9766
10038
  ### 1. \`agent-product-analyst\` (Product Analyst / Scribe)
9767
- - **Mission**: Assist in defining and refining the product model using ProductShape.
10039
+ - **Mission**: Capture new product intent via NAF v4 Enterprise & Capability Vision (\`ARCH-INTRO-*\`) and define/refine the product model using ProductShape.
10040
+ - **Phase 0 (Greenfield Product Inception)**: When creating a new product from scratch (or when \`specs/product/\` has no baseline), run a concise 3\u20135 question discovery interview (mission/problem, actors \`ACT-*\`, MVP capabilities \`UC-*\`, priority quality goals \`QR-*\`), draft \`ARCH-INTRO-001\` (\`templates/architecture/introduction-and-goals.template.md\`), and derive initial \`ACT-*\`, \`UC-*\`, \`BR-*\`, \`FR-*\`, and \`QR-*\` drafts.
9768
10041
  - **Input**: Business intent or natural language specification.
9769
- - **Output**: Markdown artifacts with YAML frontmatter compliant with \`schemas/product/\` (\`ACT-*\`, \`UC-*\`, \`FR-*\`, \`QR-*\`, \`BR-*\`).
10042
+ - **Output**: Markdown artifacts with YAML frontmatter compliant with \`schemas/architecture/introduction-and-goals.schema.json\` (\`ARCH-INTRO-*\`) and \`schemas/product/\` (\`ACT-*\`, \`UC-*\`, \`FR-*\`, \`QR-*\`, \`BR-*\`).
9770
10043
  - **Guardrails**: \`status\` always starts in \`draft\`. Mandatory Gherkin criteria with \`@<ID> @automated @regression\`.
9771
10044
 
9772
10045
  ### 2. \`agent-threat-modeler\` (Threat Modeler and Security Specialist)
@@ -9925,7 +10198,7 @@ Non-Negotiable Quality Thresholds (\`quality-policy.yaml\`):
9925
10198
 
9926
10199
  | Role | Mission (summary) |
9927
10200
  | :--- | :--- |
9928
- | \`agent-product-analyst\` | ProductShape artifacts (\`ACT-*\`, \`UC-*\`, \`FR-*\`, \`QR-*\`, \`BR-*\`); \`status\` starts \`draft\`. |
10201
+ | \`agent-product-analyst\` | Phase 0 NAF v4 Enterprise & Capability Vision (\`ARCH-INTRO-*\`, 3\u20135 question interview) + ProductShape artifacts (\`ACT-*\`, \`UC-*\`, \`FR-*\`, \`QR-*\`, \`BR-*\`); \`status\` starts \`draft\`. |
9929
10202
  | \`agent-threat-modeler\` | STRIDE/OWASP threat triad (\`ACT-THREAT-*\`, \`ABUSE-*\`, \`SEC-REQ-*\`). |
9930
10203
  | \`agent-system-architect\` | Modular arc42 + NAF v4 architecture (\`CMP-*\`, Mermaid diagrams, \`ADR-*\`). |
9931
10204
  | \`agent-qa-engineer\` | FAILING BDD/Gherkin suites before \`agent-developer\` writes production code. |
@@ -10733,7 +11006,7 @@ var ALL_ARCHITECTURE_TEMPLATES = [
10733
11006
  { filename: "cross-cutting-concepts.template.md", content: STARTER_ARCH_CROSS_CUTTING_CONCEPTS, isMinimal: false },
10734
11007
  { filename: "deployment-view.template.md", content: STARTER_ARCH_DEPLOYMENT_VIEW, isMinimal: false },
10735
11008
  { filename: "glossary.template.md", content: STARTER_ARCH_GLOSSARY, isMinimal: false },
10736
- { filename: "introduction-and-goals.template.md", content: STARTER_ARCH_INTRODUCTION_AND_GOALS, isMinimal: false },
11009
+ { filename: "introduction-and-goals.template.md", content: STARTER_ARCH_INTRODUCTION_AND_GOALS, isMinimal: true },
10737
11010
  { filename: "level-1-whitebox.template.md", content: STARTER_ARCH_LEVEL_1_WHITEBOX, isMinimal: false },
10738
11011
  { filename: "quality-requirements.template.md", content: STARTER_ARCH_QUALITY_REQUIREMENTS, isMinimal: false },
10739
11012
  { filename: "risks-and-technical-debt.template.md", content: STARTER_ARCH_RISKS_AND_TECHNICAL_DEBT, isMinimal: false },