specatlas 0.1.27 → 0.1.28

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.
Files changed (2) hide show
  1. package/dist/bin.js +482 -275
  2. package/package.json +2 -2
package/dist/bin.js CHANGED
@@ -1586,7 +1586,7 @@ var require_core = __commonJS({
1586
1586
  });
1587
1587
 
1588
1588
  // src/cli.ts
1589
- import path45 from "path";
1589
+ import path46 from "path";
1590
1590
 
1591
1591
  // ../core/dist/index.js
1592
1592
  import path from "path";
@@ -1599,23 +1599,25 @@ import path2 from "path";
1599
1599
  import path3 from "path";
1600
1600
  import { parse as parseYaml4 } from "yaml";
1601
1601
  import { z as z3 } from "zod";
1602
+ import path5 from "path";
1602
1603
  import path4 from "path";
1603
1604
  import { stringify as stringifyYaml3 } from "yaml";
1604
- import path5 from "path";
1605
+ import { stringify as stringifyYaml4 } from "yaml";
1606
+ import path6 from "path";
1605
1607
  import { parse as parseYaml5 } from "yaml";
1606
1608
  import { execFile } from "child_process";
1607
- import path6 from "path";
1608
1609
  import path7 from "path";
1609
- import { randomBytes } from "crypto";
1610
- import path10 from "path";
1611
1610
  import path8 from "path";
1612
- import { parse as parseYaml6, stringify as stringifyYaml4 } from "yaml";
1613
- import { z as z4 } from "zod";
1611
+ import { randomBytes } from "crypto";
1612
+ import path11 from "path";
1614
1613
  import path9 from "path";
1614
+ import { parse as parseYaml6, stringify as stringifyYaml5 } from "yaml";
1615
+ import { z as z4 } from "zod";
1616
+ import path10 from "path";
1615
1617
  import { parse as parseYaml7 } from "yaml";
1616
1618
  import { z as z5 } from "zod";
1617
- import path11 from "path";
1618
1619
  import path12 from "path";
1620
+ import path13 from "path";
1619
1621
 
1620
1622
  // ../../node_modules/.pnpm/highlight.js@11.12.0/node_modules/highlight.js/es/core.js
1621
1623
  var import_core = __toESM(require_core(), 1);
@@ -9020,18 +9022,18 @@ function renderMarkdown(markdown2, opts = {}) {
9020
9022
  }
9021
9023
 
9022
9024
  // ../core/dist/index.js
9023
- import path13 from "path";
9024
9025
  import path14 from "path";
9025
9026
  import path15 from "path";
9027
+ import path16 from "path";
9026
9028
  import { promises as fs } from "fs";
9027
9029
  import { cp, rename, rm } from "fs/promises";
9028
- import path16 from "path";
9029
- import { promises as fs2 } from "fs";
9030
9030
  import path17 from "path";
9031
- import { parse as parseYaml8, stringify as stringifyYaml5 } from "yaml";
9031
+ import { promises as fs2 } from "fs";
9032
9032
  import path18 from "path";
9033
+ import { parse as parseYaml8, stringify as stringifyYaml6 } from "yaml";
9033
9034
  import path19 from "path";
9034
9035
  import path20 from "path";
9036
+ import path21 from "path";
9035
9037
  var CORE_VERSION = "0.0.1";
9036
9038
  var REQ_ID_RE = /^REQ-[A-Z0-9]+(?:-[A-Z0-9]+)*-\d{3}$/;
9037
9039
  var SCENARIO_ID_RE = /^REQ-[A-Z0-9]+(?:-[A-Z0-9]+)*-\d{3}-S\d+$/;
@@ -9747,14 +9749,14 @@ function wordBoundary(text, term) {
9747
9749
  const re = new RegExp(`(?<![\\p{L}\\p{N}])${escaped}(?![\\p{L}\\p{N}])`, "iu");
9748
9750
  return re.test(text);
9749
9751
  }
9750
- function lintText(text, code, terms, label, path212, line) {
9752
+ function lintText(text, code, terms, label, path222, line) {
9751
9753
  const out = [];
9752
9754
  const lower = text.toLowerCase();
9753
9755
  for (const term of terms) {
9754
9756
  if (wordBoundary(lower, term)) {
9755
9757
  out.push(
9756
9758
  diag(code, "error", `${label}: "${term}"`, {
9757
- path: path212,
9759
+ path: path222,
9758
9760
  line,
9759
9761
  suggestion: "La especificaci\xF3n es funcional y de negocio: describe comportamiento, no tecnolog\xEDa ni adjetivos vagos"
9760
9762
  })
@@ -9763,7 +9765,7 @@ function lintText(text, code, terms, label, path212, line) {
9763
9765
  }
9764
9766
  return out;
9765
9767
  }
9766
- function lintRequirement(req, path212, opts = {}) {
9768
+ function lintRequirement(req, path222, opts = {}) {
9767
9769
  const out = [];
9768
9770
  const vague = opts.language === "en" ? VAGUE_EN : VAGUE_ES;
9769
9771
  const tech = opts.language === "en" ? TECH_EN : TECH_ES;
@@ -9774,32 +9776,32 @@ function lintRequirement(req, path212, opts = {}) {
9774
9776
  ...req.scenarios.flatMap((s) => [...s.when.map((w) => ({ text: w, line: s.line })), ...s.then.map((t) => ({ text: t, line: s.line }))])
9775
9777
  ];
9776
9778
  for (const part of parts) {
9777
- out.push(...lintText(part.text, "LINT-BIZ-002", vague, "Palabra vaga en la especificaci\xF3n", path212, part.line));
9779
+ out.push(...lintText(part.text, "LINT-BIZ-002", vague, "Palabra vaga en la especificaci\xF3n", path222, part.line));
9778
9780
  if (opts.businessOnly !== false) {
9779
- out.push(...lintText(part.text, "LINT-BIZ-001", tech, "Jerga t\xE9cnica en la especificaci\xF3n de negocio", path212, part.line));
9781
+ out.push(...lintText(part.text, "LINT-BIZ-001", tech, "Jerga t\xE9cnica en la especificaci\xF3n de negocio", path222, part.line));
9780
9782
  }
9781
9783
  }
9782
9784
  if (req.scenarios.length === 0) {
9783
- out.push(diag("TRACE-001", "error", `El requisito ${req.id} no tiene ning\xFAn escenario`, { path: path212, line: req.line, suggestion: "A\xF1ade al menos un escenario CUANDO/ENTONCES" }));
9785
+ out.push(diag("TRACE-001", "error", `El requisito ${req.id} no tiene ning\xFAn escenario`, { path: path222, line: req.line, suggestion: "A\xF1ade al menos un escenario CUANDO/ENTONCES" }));
9784
9786
  }
9785
9787
  return out;
9786
9788
  }
9787
- function lintDelta(delta, livingRequirements, path212, opts = {}) {
9789
+ function lintDelta(delta, livingRequirements, path222, opts = {}) {
9788
9790
  const out = [...delta.diagnostics];
9789
9791
  for (const req of [...delta.added, ...delta.modified]) {
9790
- out.push(...lintRequirement(req, path212, opts));
9792
+ out.push(...lintRequirement(req, path222, opts));
9791
9793
  }
9792
9794
  for (const req of delta.modified) {
9793
9795
  const living = livingRequirements.get(req.id);
9794
9796
  if (!living) {
9795
- out.push(diag("TRACE-007", "error", `MODIFIED ${req.id} no existe en la spec viva; usa ADDED`, { path: path212, line: req.line }));
9797
+ out.push(diag("TRACE-007", "error", `MODIFIED ${req.id} no existe en la spec viva; usa ADDED`, { path: path222, line: req.line }));
9796
9798
  continue;
9797
9799
  }
9798
9800
  for (const existing of living.scenarios) {
9799
9801
  if (!req.scenarios.some((s) => s.id === existing.id)) {
9800
9802
  out.push(
9801
9803
  diag("TRACE-007", "error", `MODIFIED ${req.id} pierde el escenario ${existing.id}: copia el bloque completo`, {
9802
- path: path212,
9804
+ path: path222,
9803
9805
  line: req.line,
9804
9806
  suggestion: "Copia el bloque completo de la spec viva y ed\xEDtalo; para quitarlo, decl\xE1ralo en REMOVED"
9805
9807
  })
@@ -9810,15 +9812,15 @@ function lintDelta(delta, livingRequirements, path212, opts = {}) {
9810
9812
  for (const req of delta.removed) {
9811
9813
  const living = livingRequirements.get(req.id);
9812
9814
  if (!living) {
9813
- out.push(diag("TRACE-007", "error", `REMOVED ${req.id} no existe en la spec viva`, { path: path212, line: req.line }));
9815
+ out.push(diag("TRACE-007", "error", `REMOVED ${req.id} no existe en la spec viva`, { path: path222, line: req.line }));
9814
9816
  }
9815
9817
  }
9816
9818
  for (const rename2 of delta.renamed) {
9817
9819
  if (rename2.from.id !== rename2.to.id) {
9818
- out.push(diag("LINT-DLT-003", "error", `RENAMED cambia el id (${rename2.from.id} \u2192 ${rename2.to.id}); los ids son inmutables`, { path: path212, line: rename2.line }));
9820
+ out.push(diag("LINT-DLT-003", "error", `RENAMED cambia el id (${rename2.from.id} \u2192 ${rename2.to.id}); los ids son inmutables`, { path: path222, line: rename2.line }));
9819
9821
  }
9820
9822
  if (!livingRequirements.has(rename2.from.id)) {
9821
- out.push(diag("TRACE-007", "error", `RENAMED ${rename2.from.id} no existe en la spec viva`, { path: path212, line: rename2.line }));
9823
+ out.push(diag("TRACE-007", "error", `RENAMED ${rename2.from.id} no existe en la spec viva`, { path: path222, line: rename2.line }));
9822
9824
  }
9823
9825
  }
9824
9826
  return out;
@@ -9842,7 +9844,7 @@ var MERMAID_KEYWORDS = [
9842
9844
  "architecture-beta"
9843
9845
  ];
9844
9846
  var MERMAID_BLOCKS = /^\s*(alt|loop|opt|par|rect|critical|break|subgraph)\b/;
9845
- function lintPlan(planText, path212) {
9847
+ function lintPlan(planText, path222) {
9846
9848
  const out = [];
9847
9849
  const blocks = [...planText.matchAll(/```mermaid\r?\n([\s\S]*?)```/g)];
9848
9850
  for (const [index, block] of blocks.entries()) {
@@ -9852,7 +9854,7 @@ function lintPlan(planText, path212) {
9852
9854
  if (!MERMAID_KEYWORDS.some((keyword) => first.startsWith(keyword))) {
9853
9855
  out.push(
9854
9856
  diag("LINT-PLN-002", "error", `Diagrama mermaid ${index + 1}: la primera l\xEDnea debe declarar el tipo (${MERMAID_KEYWORDS.slice(0, 5).join(", ")}\u2026) y empieza por "${first.slice(0, 30)}"`, {
9855
- path: path212,
9857
+ path: path222,
9856
9858
  suggestion: "Corrige el tipo del diagrama o elimina el bloque"
9857
9859
  })
9858
9860
  );
@@ -9866,7 +9868,7 @@ function lintPlan(planText, path212) {
9866
9868
  if (open !== 0) {
9867
9869
  out.push(
9868
9870
  diag("LINT-PLN-002", "error", `Diagrama mermaid ${index + 1}: faltan ${Math.abs(open)} \`end\` (bloques alt/loop/subgraph sin cerrar)`, {
9869
- path: path212,
9871
+ path: path222,
9870
9872
  suggestion: "Cierra cada bloque alt/loop/opt/par/rect/subgraph con `end`"
9871
9873
  })
9872
9874
  );
@@ -9878,7 +9880,7 @@ function lintPlan(planText, path212) {
9878
9880
  if (message.includes(";")) {
9879
9881
  out.push(
9880
9882
  diag("LINT-PLN-003", "error", `Diagrama mermaid ${index + 1}: el mensaje "${message.trim().slice(0, 40)}\u2026" usa \`;\` y mermaid lo interpreta como fin de sentencia`, {
9881
- path: path212,
9883
+ path: path222,
9882
9884
  suggestion: "Sustituye `;` por `\xB7` o `,` en los mensajes de sequenceDiagram"
9883
9885
  })
9884
9886
  );
@@ -10033,6 +10035,16 @@ function checkTrace(input) {
10033
10035
  findings.push(diag("TRACE-006", "warning", `Evidencia hu\xE9rfana: ${ev.scenario} no existe`, { path: change.verify?.path, line: ev.line }));
10034
10036
  }
10035
10037
  }
10038
+ for (const cover of change.fixCovers ?? []) {
10039
+ if (!livingReqs.has(cover)) {
10040
+ findings.push(
10041
+ diag("TRACE-011", "warning", `El fix ${change.slug} declara ${cover}, que no existe en las specs vivas`, {
10042
+ path: change.fix?.path,
10043
+ suggestion: "Corrige el identificador o qu\xEDtalo: la declaraci\xF3n es opcional"
10044
+ })
10045
+ );
10046
+ }
10047
+ }
10036
10048
  const summary = countBySeverity(findings);
10037
10049
  return { graph: buildTraceGraph(input), findings, summary };
10038
10050
  }
@@ -10396,34 +10408,110 @@ function detectionToYaml(result, generatedAt) {
10396
10408
  }
10397
10409
  return lines.join("\n") + "\n";
10398
10410
  }
10411
+ var LIVING_FIXES_DIR = path4.join(".sdd", "fixes");
10412
+ var COMMENT_RE = /<!--[\s\S]*?-->/g;
10413
+ function parseFixCovers(content) {
10414
+ const clean = content.replace(COMMENT_RE, "");
10415
+ const covers = [];
10416
+ for (const line of clean.replace(/\r\n?/g, "\n").split("\n")) {
10417
+ const match = /^\s*-?\s*Cubre:\s*(.+?)\s*$/.exec(line);
10418
+ if (!match) continue;
10419
+ for (const raw of (match[1] ?? "").split(",")) {
10420
+ const id = raw.trim().toUpperCase();
10421
+ if (/^REQ-[A-Z0-9-]+$/.test(id) && !covers.includes(id)) covers.push(id);
10422
+ }
10423
+ }
10424
+ return covers;
10425
+ }
10426
+ function coversOf(value) {
10427
+ if (Array.isArray(value)) {
10428
+ return value.map((v) => String(v).trim().toUpperCase()).filter((v) => /^REQ-[A-Z0-9-]+$/.test(v));
10429
+ }
10430
+ if (typeof value === "string") {
10431
+ return value.split(",").map((v) => v.trim().toUpperCase()).filter((v) => /^REQ-[A-Z0-9-]+$/.test(v));
10432
+ }
10433
+ return [];
10434
+ }
10435
+ function parseLivingFix(content, file) {
10436
+ const fm = parseFrontmatter(content, file);
10437
+ const data = fm.data;
10438
+ const base = path4.basename(file).replace(/\.md$/i, "");
10439
+ const slug = typeof data["slug"] === "string" && data["slug"].trim() !== "" ? data["slug"].trim() : base.replace(/^\d{4}-\d{2}-/, "");
10440
+ const fix = {
10441
+ slug,
10442
+ file,
10443
+ date: typeof data["date"] === "string" ? data["date"] : "",
10444
+ result: typeof data["result"] === "string" ? data["result"] : "pass",
10445
+ covers: coversOf(data["covers"]),
10446
+ content: fm.body.trimStart()
10447
+ };
10448
+ if (typeof data["domain"] === "string") fix.domain = data["domain"];
10449
+ if (typeof data["title"] === "string") fix.title = data["title"];
10450
+ return fix;
10451
+ }
10452
+ async function loadLivingFixes(root) {
10453
+ const dir = path4.join(root, LIVING_FIXES_DIR);
10454
+ const entries = (await listDir(dir)).filter((entry) => entry.toLowerCase().endsWith(".md")).sort();
10455
+ const fixes = [];
10456
+ for (const entry of entries) {
10457
+ const file = path4.join(dir, entry);
10458
+ const content = await readTextIfExists(file);
10459
+ if (content === void 0) continue;
10460
+ fixes.push(parseLivingFix(content, file));
10461
+ }
10462
+ return fixes.sort((a, b) => b.date.localeCompare(a.date) || b.slug.localeCompare(a.slug));
10463
+ }
10464
+ async function writeLivingFix(root, input) {
10465
+ const dir = path4.join(root, LIVING_FIXES_DIR);
10466
+ const month = input.date.slice(0, 7);
10467
+ const file = path4.join(dir, `${month}-${input.slug}.md`);
10468
+ const relativePath = toPosix(path4.relative(root, file));
10469
+ if (await exists(file)) return { file, relativePath, created: false };
10470
+ const data = {
10471
+ slug: input.slug,
10472
+ date: input.date,
10473
+ result: input.result ?? "pass"
10474
+ };
10475
+ if (input.domain) data["domain"] = input.domain;
10476
+ if (input.title) data["title"] = input.title;
10477
+ if (input.covers && input.covers.length > 0) data["covers"] = input.covers;
10478
+ const body = input.content.replace(/^[\r\n]+/, "").trimEnd();
10479
+ const text = `---
10480
+ ${stringifyYaml3(data, { lineWidth: 120 })}---
10481
+
10482
+ ${body}
10483
+ `;
10484
+ await writeText(file, text);
10485
+ return { file, relativePath, created: true };
10486
+ }
10399
10487
  var SDD_DIR = ".sdd";
10400
10488
  async function findWorkspaceRoot(start) {
10401
- let current = path4.resolve(start);
10489
+ let current = path5.resolve(start);
10402
10490
  for (let i = 0; i < 40; i += 1) {
10403
- if (await isDirectory(path4.join(current, SDD_DIR))) return current;
10404
- const parent = path4.dirname(current);
10491
+ if (await isDirectory(path5.join(current, SDD_DIR))) return current;
10492
+ const parent = path5.dirname(current);
10405
10493
  if (parent === current) return void 0;
10406
10494
  current = parent;
10407
10495
  }
10408
10496
  return void 0;
10409
10497
  }
10410
10498
  async function loadApprovals(sddDir) {
10411
- const file = path4.join(sddDir, "approvals.yaml");
10499
+ const file = path5.join(sddDir, "approvals.yaml");
10412
10500
  const raw = await readTextIfExists(file);
10413
10501
  if (raw === void 0) return { byArtifact: /* @__PURE__ */ new Map(), diagnostics: [] };
10414
10502
  const parsed = parseApprovals(raw, file);
10415
10503
  const map = /* @__PURE__ */ new Map();
10416
10504
  for (const a of parsed.approvals?.approvals ?? []) {
10417
- map.set(a.artifact.split(path4.sep).join("/"), { hash: a.artifactHash, by: a.approvedBy, at: a.approvedAt });
10505
+ map.set(a.artifact.split(path5.sep).join("/"), { hash: a.artifactHash, by: a.approvedBy, at: a.approvedAt });
10418
10506
  }
10419
10507
  return { byArtifact: map, diagnostics: parsed.diagnostics };
10420
10508
  }
10421
10509
  async function loadSpecs(sddDir) {
10422
- const specsDir = path4.join(sddDir, "specs");
10510
+ const specsDir = path5.join(sddDir, "specs");
10423
10511
  const domains = await listDirs(specsDir);
10424
10512
  const out = [];
10425
10513
  for (const domain of domains) {
10426
- const file = path4.join(specsDir, domain, "spec.md");
10514
+ const file = path5.join(specsDir, domain, "spec.md");
10427
10515
  if (!await exists(file)) continue;
10428
10516
  const content = await readText(file);
10429
10517
  out.push({ domain, path: file, spec: parseSpecFile(content, file) });
@@ -10431,10 +10519,10 @@ async function loadSpecs(sddDir) {
10431
10519
  return out;
10432
10520
  }
10433
10521
  async function loadChange(root, slug, relDir) {
10434
- const dir = path4.join(root, SDD_DIR, "changes", relDir ?? slug);
10522
+ const dir = path5.join(root, SDD_DIR, "changes", relDir ?? slug);
10435
10523
  const diagnostics = [];
10436
10524
  const change = { slug, dir, diagnostics };
10437
- const metaFile = path4.join(dir, "meta.yaml");
10525
+ const metaFile = path5.join(dir, "meta.yaml");
10438
10526
  const metaRaw = await readTextIfExists(metaFile);
10439
10527
  if (metaRaw === void 0) {
10440
10528
  diagnostics.push(diag("ATLAS-FILES-001", "error", `El cambio "${slug}" no tiene meta.yaml`, { path: metaFile, suggestion: "Crea meta.yaml con slug, lane y dominio" }));
@@ -10443,7 +10531,7 @@ async function loadChange(root, slug, relDir) {
10443
10531
  diagnostics.push(...parsed.diagnostics);
10444
10532
  if (parsed.meta) change.meta = parsed.meta;
10445
10533
  }
10446
- const deltaFile = path4.join(dir, "spec.md");
10534
+ const deltaFile = path5.join(dir, "spec.md");
10447
10535
  const deltaRaw = await readTextIfExists(deltaFile);
10448
10536
  if (deltaRaw !== void 0) {
10449
10537
  change.delta = parseDelta(deltaRaw, deltaFile);
@@ -10451,41 +10539,42 @@ async function loadChange(root, slug, relDir) {
10451
10539
  } else {
10452
10540
  diagnostics.push(diag("ATLAS-FILES-002", "warning", `El cambio "${slug}" no tiene spec.md (delta)`, { path: deltaFile }));
10453
10541
  }
10454
- const planFile = path4.join(dir, "plan.md");
10542
+ const planFile = path5.join(dir, "plan.md");
10455
10543
  if (await exists(planFile)) change.planPath = planFile;
10456
- const reviewFile = path4.join(dir, "review.md");
10544
+ const reviewFile = path5.join(dir, "review.md");
10457
10545
  if (await exists(reviewFile)) change.reviewPath = reviewFile;
10458
- const presentationFile = path4.join(dir, "presentation", "index.html");
10546
+ const presentationFile = path5.join(dir, "presentation", "index.html");
10459
10547
  if (await exists(presentationFile)) change.presentationPath = presentationFile;
10460
- const tasksFile = path4.join(dir, "tasks.md");
10548
+ const tasksFile = path5.join(dir, "tasks.md");
10461
10549
  const tasksRaw = await readTextIfExists(tasksFile);
10462
10550
  if (tasksRaw !== void 0) {
10463
10551
  change.tasks = parseTasksFile(tasksRaw, tasksFile);
10464
10552
  diagnostics.push(...change.tasks.diagnostics);
10465
10553
  }
10466
- const verifyFile = path4.join(dir, "verify.md");
10554
+ const verifyFile = path5.join(dir, "verify.md");
10467
10555
  const verifyRaw = await readTextIfExists(verifyFile);
10468
10556
  if (verifyRaw !== void 0) {
10469
10557
  change.verify = parseVerifyFile(verifyRaw, verifyFile);
10470
10558
  diagnostics.push(...change.verify.diagnostics);
10471
10559
  }
10472
- const fixFile = path4.join(dir, "fix.md");
10560
+ const fixFile = path5.join(dir, "fix.md");
10473
10561
  const fixRaw = await readTextIfExists(fixFile);
10474
10562
  if (fixRaw !== void 0) {
10475
10563
  change.fix = parseVerifyFile(fixRaw, fixFile);
10476
10564
  diagnostics.push(...change.fix.diagnostics);
10565
+ change.fixCovers = parseFixCovers(fixRaw);
10477
10566
  }
10478
- const mockupManifest = path4.join(dir, "mockups", "manifest.yaml");
10567
+ const mockupManifest = path5.join(dir, "mockups", "manifest.yaml");
10479
10568
  if (await exists(mockupManifest)) change.mockupManifestPath = mockupManifest;
10480
10569
  return change;
10481
10570
  }
10482
10571
  async function listChangeSlugs(root, opts = {}) {
10483
- const changesDir = path4.join(root, SDD_DIR, "changes");
10572
+ const changesDir = path5.join(root, SDD_DIR, "changes");
10484
10573
  const dirs = await listDirs(changesDir);
10485
10574
  return dirs.filter((d) => opts.includeArchived ? true : d !== "archive");
10486
10575
  }
10487
10576
  async function loadWorkspace(root) {
10488
- const sddDir = path4.join(root, SDD_DIR);
10577
+ const sddDir = path5.join(root, SDD_DIR);
10489
10578
  const { config, diagnostics: configDiags } = await loadConfig(sddDir);
10490
10579
  const diagnostics = [...configDiags];
10491
10580
  const specs = await loadSpecs(sddDir);
@@ -10498,20 +10587,20 @@ async function loadWorkspace(root) {
10498
10587
  diagnostics.push(...change.diagnostics);
10499
10588
  }
10500
10589
  const archived = [];
10501
- const archivedDir = path4.join(sddDir, "changes", "archive");
10590
+ const archivedDir = path5.join(sddDir, "changes", "archive");
10502
10591
  if (await isDirectory(archivedDir)) {
10503
10592
  for (const entry of await listDirs(archivedDir)) {
10504
- archived.push(await loadChange(root, entry.replace(/^\d{4}-\d{2}-/, ""), path4.join("archive", entry)));
10593
+ archived.push(await loadChange(root, entry.replace(/^\d{4}-\d{2}-/, ""), path5.join("archive", entry)));
10505
10594
  }
10506
10595
  }
10507
10596
  return { workspace: { root, sddDir, specs, changes, archived, diagnostics }, config };
10508
10597
  }
10509
10598
  async function ensureSddDirs(sddDir) {
10510
10599
  const created = [];
10511
- const dirs = ["specs", "changes", "runs", "metrics", path4.join("profiles", "custom")];
10600
+ const dirs = ["specs", "changes", "runs", "metrics", "fixes", path5.join("profiles", "custom")];
10512
10601
  const { ensureDir: ensureDir2 } = await import("./fsx-VF2P7ALA-XKEWTMDH.js");
10513
10602
  for (const d of dirs) {
10514
- const full = path4.join(sddDir, d);
10603
+ const full = path5.join(sddDir, d);
10515
10604
  if (!await exists(full)) {
10516
10605
  await ensureDir2(full);
10517
10606
  created.push(full);
@@ -10679,6 +10768,10 @@ notes: 12/12 casos
10679
10768
 
10680
10769
  ## Evidencia
10681
10770
 
10771
+ <!-- Opcional: declara los requisitos que este fix afecta (aparecen en su trazabilidad).
10772
+ Cubre: REQ-DOMINIO-001
10773
+ -->
10774
+
10682
10775
  <!-- Registra la evidencia real con:
10683
10776
  satlas verify <slug> --file fix --scenario REQ-DOMINIO-001-S1 --command "<comando>" --by "<nombre>"
10684
10777
  o, si es manual:
@@ -10802,6 +10895,10 @@ by: your-name
10802
10895
  ## Rollback
10803
10896
  ## Evidence
10804
10897
 
10898
+ <!-- Optional: declare the requirements this fix affects (they show up in its traceability).
10899
+ Cubre: REQ-DOMAIN-001
10900
+ -->
10901
+
10805
10902
  <!-- Register real evidence with:
10806
10903
  satlas verify <slug> --file fix --scenario REQ-DOMAIN-001-S1 --command "<command>" --by "<name>"
10807
10904
  -->
@@ -10826,7 +10923,7 @@ function changeMetaYaml(meta) {
10826
10923
  if (meta.owner) doc["owner"] = meta.owner;
10827
10924
  if (meta.mockups) doc["mockups"] = meta.mockups;
10828
10925
  return `# Estado del cambio. La fase se DERIVA de los artefactos; aqu\xED solo hechos.
10829
- ` + stringifyYaml3(doc, { lineWidth: 120 });
10926
+ ` + stringifyYaml4(doc, { lineWidth: 120 });
10830
10927
  }
10831
10928
  function indexMarkdown(input) {
10832
10929
  const es = input.language !== "en";
@@ -10840,6 +10937,15 @@ function indexMarkdown(input) {
10840
10937
  if (input.specs.length === 0) lines.push(es ? "_Sin specs todav\xEDa._" : "_No specs yet._");
10841
10938
  for (const spec of input.specs) lines.push(`- \`${spec.domain}\` \u2014 ${spec.requirements} ${es ? "requisitos" : "requirements"}`);
10842
10939
  lines.push("");
10940
+ lines.push(es ? "## Fixes vivos" : "## Living fixes");
10941
+ lines.push("");
10942
+ const fixes = input.fixes ?? [];
10943
+ if (fixes.length === 0) lines.push(es ? "_Sin fixes archivados todav\xEDa._" : "_No archived fixes yet._");
10944
+ for (const fix of fixes) {
10945
+ const parts = [fix.date, fix.domain ? `\`${fix.domain}\`` : "", `\`${fix.slug}\``, fix.result].filter((part) => part !== "");
10946
+ lines.push(`- ${parts.join(" \xB7 ")}`);
10947
+ }
10948
+ lines.push("");
10843
10949
  lines.push(es ? "## Cambios activos" : "## Active changes");
10844
10950
  lines.push("");
10845
10951
  if (input.changes.length === 0) lines.push(es ? "_Sin cambios activos._" : "_No active changes._");
@@ -10991,8 +11097,8 @@ function issueBody(input) {
10991
11097
  lines.push(`- Siguiente: \`${input.next}\` \u2014 ${input.nextDescription}`);
10992
11098
  lines.push("");
10993
11099
  lines.push("### Artefactos");
10994
- lines.push(`- Spec: \`${path5.posix.join(".sdd", "changes", change.slug, "spec.md")}\``);
10995
- if (change.planPath) lines.push(`- Plan: \`${path5.posix.join(".sdd", "changes", change.slug, "plan.md")}\``);
11100
+ lines.push(`- Spec: \`${path6.posix.join(".sdd", "changes", change.slug, "spec.md")}\``);
11101
+ if (change.planPath) lines.push(`- Plan: \`${path6.posix.join(".sdd", "changes", change.slug, "plan.md")}\``);
10996
11102
  if (change.tasks) lines.push(`- Tareas: ${change.tasks.counts.done}/${change.tasks.counts.total}`);
10997
11103
  if (change.verify) lines.push(`- Evidencia registrada: ${change.verify.evidence.length}`);
10998
11104
  lines.push("");
@@ -11056,10 +11162,10 @@ async function issueLabels(root, number, runner = defaultRunner) {
11056
11162
  }
11057
11163
  }
11058
11164
  async function patchChangeMeta(root, slug, patch) {
11059
- const file = path5.join(path5.resolve(root), ".sdd", "changes", slug, "meta.yaml");
11165
+ const file = path6.join(path6.resolve(root), ".sdd", "changes", slug, "meta.yaml");
11060
11166
  const raw = await readTextIfExists(file);
11061
11167
  if (raw === void 0) {
11062
- return { path: file, diagnostics: [diag("ATLAS-GH-007", "error", `No existe ${path5.relative(root, file)}`)] };
11168
+ return { path: file, diagnostics: [diag("ATLAS-GH-007", "error", `No existe ${path6.relative(root, file)}`)] };
11063
11169
  }
11064
11170
  let data;
11065
11171
  try {
@@ -11113,7 +11219,7 @@ async function approveFromGithub(opts) {
11113
11219
  const { signApproval: signApproval2 } = await import("./approvals-2PO6223I-DENKLEXU.js");
11114
11220
  const signed = await signApproval2({
11115
11221
  root: opts.root,
11116
- artifact: path5.posix.join("changes", opts.slug, "spec.md"),
11222
+ artifact: path6.posix.join("changes", opts.slug, "spec.md"),
11117
11223
  by,
11118
11224
  channel: "tracker",
11119
11225
  note: `Aprobado v\xEDa GitHub issue #${number} (etiqueta "${label}")`
@@ -11208,10 +11314,10 @@ function buildEvidenceYaml(evidence) {
11208
11314
  return lines.join("\n");
11209
11315
  }
11210
11316
  async function recordEvidence(opts) {
11211
- const root = path6.resolve(opts.root);
11317
+ const root = path7.resolve(opts.root);
11212
11318
  const slug = opts.slug;
11213
11319
  const fileKind = opts.file ?? "verify";
11214
- const file = path6.join(root, ".sdd", "changes", slug, fileKind === "fix" ? "fix.md" : "verify.md");
11320
+ const file = path7.join(root, ".sdd", "changes", slug, fileKind === "fix" ? "fix.md" : "verify.md");
11215
11321
  const diagnostics = [];
11216
11322
  const scenario = opts.scenario.toUpperCase();
11217
11323
  if (!SCENARIO_ID_RE.test(scenario)) {
@@ -11301,7 +11407,7 @@ function generateRunId(now = /* @__PURE__ */ new Date()) {
11301
11407
  return `${iso.slice(0, 8)}-${iso.slice(8, 14)}-${randomBytes(3).toString("hex")}`;
11302
11408
  }
11303
11409
  function runsDir(root) {
11304
- return path7.join(path7.resolve(root), ".sdd", "runs");
11410
+ return path8.join(path8.resolve(root), ".sdd", "runs");
11305
11411
  }
11306
11412
  async function createRun(root, slug, phase, inputs, now = /* @__PURE__ */ new Date()) {
11307
11413
  const runId = generateRunId(now);
@@ -11314,25 +11420,25 @@ async function createRun(root, slug, phase, inputs, now = /* @__PURE__ */ new Da
11314
11420
  updatedAt: localStamp(now)
11315
11421
  };
11316
11422
  if (inputs) state.inputs = inputs;
11317
- const dir = path7.join(runsDir(root), runId);
11423
+ const dir = path8.join(runsDir(root), runId);
11318
11424
  await ensureDir(dir);
11319
- await writeText(path7.join(dir, "state.json"), `${JSON.stringify(state, null, 2)}
11425
+ await writeText(path8.join(dir, "state.json"), `${JSON.stringify(state, null, 2)}
11320
11426
  `);
11321
- await writeText(path7.join(dir, "events.jsonl"), "");
11427
+ await writeText(path8.join(dir, "events.jsonl"), "");
11322
11428
  await appendRunEvent(root, runId, "run_started", { slug, phase });
11323
11429
  return state;
11324
11430
  }
11325
11431
  async function appendRunEvent(root, runId, type, data, now = /* @__PURE__ */ new Date()) {
11326
11432
  const event = { eventId: randomBytes(4).toString("hex"), at: localStamp(now), type };
11327
11433
  if (data) event.data = data;
11328
- const file = path7.join(runsDir(root), runId, "events.jsonl");
11434
+ const file = path8.join(runsDir(root), runId, "events.jsonl");
11329
11435
  const previous = await readTextIfExists(file) ?? "";
11330
11436
  await writeText(file, `${previous}${JSON.stringify(event)}
11331
11437
  `);
11332
11438
  return event;
11333
11439
  }
11334
11440
  async function updateRunStatus(root, runId, status, now = /* @__PURE__ */ new Date()) {
11335
- const file = path7.join(runsDir(root), runId, "state.json");
11441
+ const file = path8.join(runsDir(root), runId, "state.json");
11336
11442
  const raw = await readTextIfExists(file);
11337
11443
  if (raw === void 0) return;
11338
11444
  const state = JSON.parse(raw);
@@ -11342,13 +11448,13 @@ async function updateRunStatus(root, runId, status, now = /* @__PURE__ */ new Da
11342
11448
  `);
11343
11449
  }
11344
11450
  async function readRun(root, runId) {
11345
- const dir = path7.join(runsDir(root), runId);
11346
- const stateRaw = await readTextIfExists(path7.join(dir, "state.json"));
11451
+ const dir = path8.join(runsDir(root), runId);
11452
+ const stateRaw = await readTextIfExists(path8.join(dir, "state.json"));
11347
11453
  if (stateRaw === void 0) {
11348
11454
  return { diagnostics: [diag("ATLAS-RUN-001", "error", `No existe el run ${runId}`, { path: dir })] };
11349
11455
  }
11350
11456
  const state = JSON.parse(stateRaw);
11351
- const eventsRaw = await readTextIfExists(path7.join(dir, "events.jsonl")) ?? "";
11457
+ const eventsRaw = await readTextIfExists(path8.join(dir, "events.jsonl")) ?? "";
11352
11458
  const events = [];
11353
11459
  for (const line of eventsRaw.split("\n")) {
11354
11460
  if (line.trim() === "") continue;
@@ -11365,7 +11471,7 @@ async function listRuns(root, filter = {}) {
11365
11471
  const ids = await listDirs(dir);
11366
11472
  const out = [];
11367
11473
  for (const id of ids) {
11368
- const raw = await readTextIfExists(path7.join(dir, id, "state.json"));
11474
+ const raw = await readTextIfExists(path8.join(dir, id, "state.json"));
11369
11475
  if (raw === void 0) continue;
11370
11476
  try {
11371
11477
  const state = JSON.parse(raw);
@@ -11400,20 +11506,20 @@ var mockupManifestSchema = z4.object({
11400
11506
  });
11401
11507
  var TOKEN_CANDIDATES = ["design/tokens.json", "DESIGN.md", "design/DESIGN.md", ".sdd/design/tokens.json"];
11402
11508
  function mockupsDir(root, slug) {
11403
- return path8.join(path8.resolve(root), ".sdd", "changes", slug, "mockups");
11509
+ return path9.join(path9.resolve(root), ".sdd", "changes", slug, "mockups");
11404
11510
  }
11405
11511
  async function tokensFile(root) {
11406
11512
  for (const candidate of TOKEN_CANDIDATES) {
11407
- const abs = path8.join(root, candidate);
11513
+ const abs = path9.join(root, candidate);
11408
11514
  if (await exists(abs)) return candidate;
11409
11515
  }
11410
11516
  return void 0;
11411
11517
  }
11412
11518
  async function computeInputsHash(root, change) {
11413
- const deltaPath = path8.join(change.dir, "spec.md");
11519
+ const deltaPath = path9.join(change.dir, "spec.md");
11414
11520
  const delta = await readTextIfExists(deltaPath) ?? "";
11415
11521
  const tokens = await tokensFile(root);
11416
- const tokensContent = tokens ? await readTextIfExists(path8.join(root, tokens)) ?? "" : "";
11522
+ const tokensContent = tokens ? await readTextIfExists(path9.join(root, tokens)) ?? "" : "";
11417
11523
  return artifactHash(`${delta}
11418
11524
  ---tokens---
11419
11525
  ${tokensContent}`);
@@ -11438,7 +11544,7 @@ function planMockups(change, platform = "web") {
11438
11544
  return { platform, screens };
11439
11545
  }
11440
11546
  async function readMockupManifest(root, slug) {
11441
- const file = path8.join(mockupsDir(root, slug), "manifest.yaml");
11547
+ const file = path9.join(mockupsDir(root, slug), "manifest.yaml");
11442
11548
  const raw = await readTextIfExists(file);
11443
11549
  if (raw === void 0) return { diagnostics: [] };
11444
11550
  let data;
@@ -11530,7 +11636,7 @@ async function checkMockups(root, slug, change) {
11530
11636
  const { manifest, path: manifestPath, diagnostics } = await readMockupManifest(root, slug);
11531
11637
  const findings = [...diagnostics];
11532
11638
  if (!manifest || !manifestPath) {
11533
- return { findings: [...findings, diag("LINT-MKP-004", "error", "No existe mockups/manifest.yaml", { path: path8.join(dir, "manifest.yaml") })], stale: false };
11639
+ return { findings: [...findings, diag("LINT-MKP-004", "error", "No existe mockups/manifest.yaml", { path: path9.join(dir, "manifest.yaml") })], stale: false };
11534
11640
  }
11535
11641
  const known = /* @__PURE__ */ new Set();
11536
11642
  for (const req of [...change.delta?.added ?? [], ...change.delta?.modified ?? []]) {
@@ -11538,12 +11644,12 @@ async function checkMockups(root, slug, change) {
11538
11644
  }
11539
11645
  findings.push(...lintMockupManifest(manifest, known, manifestPath));
11540
11646
  for (const screen of manifest.screens) {
11541
- const html = await readTextIfExists(path8.join(dir, screen.file));
11647
+ const html = await readTextIfExists(path9.join(dir, screen.file));
11542
11648
  if (html === void 0) {
11543
- findings.push(diag("ATLAS-MKP-004", "error", `Falta el archivo del mockup: ${screen.file}`, { path: path8.join(dir, screen.file) }));
11649
+ findings.push(diag("ATLAS-MKP-004", "error", `Falta el archivo del mockup: ${screen.file}`, { path: path9.join(dir, screen.file) }));
11544
11650
  continue;
11545
11651
  }
11546
- findings.push(...lintMockupHtml(html, path8.join(dir, screen.file)));
11652
+ findings.push(...lintMockupHtml(html, path9.join(dir, screen.file)));
11547
11653
  }
11548
11654
  const inputsHash = await computeInputsHash(root, change);
11549
11655
  const stale = manifest.inputsHash !== void 0 && manifest.inputsHash !== inputsHash;
@@ -11558,7 +11664,7 @@ async function checkMockups(root, slug, change) {
11558
11664
  return { findings, stale, manifest, manifestPath };
11559
11665
  }
11560
11666
  async function writeMockupPlan(root, slug, plan, now = /* @__PURE__ */ new Date()) {
11561
- const file = path8.join(mockupsDir(root, slug), "plan.yaml");
11667
+ const file = path9.join(mockupsDir(root, slug), "plan.yaml");
11562
11668
  if (await exists(file)) return file;
11563
11669
  const doc = {
11564
11670
  schema_version: 1,
@@ -11566,11 +11672,11 @@ async function writeMockupPlan(root, slug, plan, now = /* @__PURE__ */ new Date(
11566
11672
  platform: plan.platform,
11567
11673
  screens: plan.screens.map((s) => ({ id: s.id, title: s.title, file: s.file, illustrates: s.illustrates, states: s.states, breakpoints: s.breakpoints }))
11568
11674
  };
11569
- await writeText(file, stringifyYaml4(doc, { lineWidth: 120 }));
11675
+ await writeText(file, stringifyYaml5(doc, { lineWidth: 120 }));
11570
11676
  return file;
11571
11677
  }
11572
11678
  async function writeMockupManifest(root, slug, plan, inputsHash, now = /* @__PURE__ */ new Date()) {
11573
- const file = path8.join(mockupsDir(root, slug), "manifest.yaml");
11679
+ const file = path9.join(mockupsDir(root, slug), "manifest.yaml");
11574
11680
  if (await exists(file)) return file;
11575
11681
  const doc = {
11576
11682
  schema_version: 1,
@@ -11582,16 +11688,16 @@ async function writeMockupManifest(root, slug, plan, inputsHash, now = /* @__PUR
11582
11688
  screens: plan.screens.map((s) => ({ id: s.id, title: s.title, file: s.file, illustrates: s.illustrates, states: s.states, breakpoints: s.breakpoints })),
11583
11689
  screenshots: []
11584
11690
  };
11585
- await writeText(file, stringifyYaml4(doc, { lineWidth: 120 }));
11691
+ await writeText(file, stringifyYaml5(doc, { lineWidth: 120 }));
11586
11692
  return file;
11587
11693
  }
11588
11694
  async function updateMockupScreenshots(root, slug, screenshots) {
11589
- const file = path8.join(mockupsDir(root, slug), "manifest.yaml");
11695
+ const file = path9.join(mockupsDir(root, slug), "manifest.yaml");
11590
11696
  const raw = await readTextIfExists(file);
11591
11697
  if (raw === void 0) return void 0;
11592
11698
  const data = parseYaml6(raw) ?? {};
11593
11699
  data["screenshots"] = screenshots;
11594
- await writeText(file, stringifyYaml4(data, { lineWidth: 120 }));
11700
+ await writeText(file, stringifyYaml5(data, { lineWidth: 120 }));
11595
11701
  return file;
11596
11702
  }
11597
11703
  async function captureMockups(root, slug, manifest, now = /* @__PURE__ */ new Date()) {
@@ -11616,9 +11722,9 @@ async function captureMockups(root, slug, manifest, now = /* @__PURE__ */ new Da
11616
11722
  const page2 = await browser.newPage();
11617
11723
  for (const bp of screen.breakpoints && screen.breakpoints.length > 0 ? screen.breakpoints : [1440]) {
11618
11724
  await page2.setViewportSize({ width: bp, height: 900 });
11619
- await page2.goto(`file://${path8.join(dir, screen.file).split(path8.sep).join("/")}`);
11620
- const rel = path8.posix.join("screens", `${screen.id}-${bp}.png`);
11621
- await page2.screenshot({ path: path8.join(dir, rel) });
11725
+ await page2.goto(`file://${path9.join(dir, screen.file).split(path9.sep).join("/")}`);
11726
+ const rel = path9.posix.join("screens", `${screen.id}-${bp}.png`);
11727
+ await page2.screenshot({ path: path9.join(dir, rel) });
11622
11728
  screenshots.push(rel);
11623
11729
  }
11624
11730
  await page2.close();
@@ -11634,7 +11740,7 @@ async function mockupsReady(root, slug, change) {
11634
11740
  return Boolean(check.manifest && check.manifest.screens.length > 0 && !check.stale);
11635
11741
  }
11636
11742
  async function setMockupRequirement(root, slug, value) {
11637
- const file = path8.join(root, ".sdd", "changes", slug, "meta.yaml");
11743
+ const file = path9.join(root, ".sdd", "changes", slug, "meta.yaml");
11638
11744
  const raw = await readTextIfExists(file) ?? `schema_version: 1
11639
11745
  slug: ${slug}
11640
11746
  lane: standard
@@ -11864,12 +11970,12 @@ ${texts.scenarios}`);
11864
11970
  }
11865
11971
  }
11866
11972
  async function loadProjectPacks(sddDir) {
11867
- const dir = path9.join(sddDir, "packs");
11973
+ const dir = path10.join(sddDir, "packs");
11868
11974
  const diagnostics = [];
11869
11975
  const packs = [];
11870
11976
  for (const entry of await listDir(dir)) {
11871
11977
  if (!/\.ya?ml$/i.test(entry)) continue;
11872
- const file = path9.join(dir, entry);
11978
+ const file = path10.join(dir, entry);
11873
11979
  const raw = await readTextIfExists(file);
11874
11980
  if (raw === void 0) continue;
11875
11981
  let data;
@@ -11956,7 +12062,7 @@ function packFindings(evaluations, change) {
11956
12062
  }
11957
12063
  var UI_DOMAINS = /* @__PURE__ */ new Set(["frontend", "mobile", "fullstack"]);
11958
12064
  async function runAnalyze(opts) {
11959
- const root = path10.resolve(opts.root);
12065
+ const root = path11.resolve(opts.root);
11960
12066
  const { workspace, config } = await loadWorkspace(root);
11961
12067
  const change = workspace.changes.find((c) => c.slug === opts.slug);
11962
12068
  if (!change) {
@@ -11974,7 +12080,7 @@ async function runAnalyze(opts) {
11974
12080
  for (const req of spec.spec.requirements) living.set(req.id, req);
11975
12081
  }
11976
12082
  if (change.delta) {
11977
- findings.push(...lintDelta(change.delta, living, path10.join(change.dir, "spec.md"), { language: config.spec.language }));
12083
+ findings.push(...lintDelta(change.delta, living, path11.join(change.dir, "spec.md"), { language: config.spec.language }));
11978
12084
  }
11979
12085
  const trace = checkTrace({ specs: workspace.specs, change, requireEvidence: false });
11980
12086
  findings.push(...trace.findings);
@@ -12023,7 +12129,7 @@ async function runAnalyze(opts) {
12023
12129
  if (mockups) result.mockups = mockups;
12024
12130
  if (packs) result.packs = packs;
12025
12131
  if (opts.write !== false) {
12026
- const file = path10.join(change.dir, "analyze.md");
12132
+ const file = path11.join(change.dir, "analyze.md");
12027
12133
  await writeText(file, renderAnalyze(change, result, localStamp(opts.now)));
12028
12134
  result.path = file;
12029
12135
  }
@@ -12065,7 +12171,7 @@ function renderAnalyze(change, result, generatedAt) {
12065
12171
  return lines.join("\n");
12066
12172
  }
12067
12173
  async function collectMetrics(root, now = /* @__PURE__ */ new Date()) {
12068
- const resolved = path11.resolve(root);
12174
+ const resolved = path12.resolve(root);
12069
12175
  const { workspace, config } = await loadWorkspace(resolved);
12070
12176
  const approvals = await loadApprovals(workspace.sddDir);
12071
12177
  const living = /* @__PURE__ */ new Map();
@@ -12080,7 +12186,7 @@ async function collectMetrics(root, now = /* @__PURE__ */ new Date()) {
12080
12186
  const deltaContent = await readDelta(change);
12081
12187
  const approval = verifyApproval(change, approvals.byArtifact, config, deltaContent);
12082
12188
  const findings = [];
12083
- if (change.delta) findings.push(...lintDelta(change.delta, living, path11.join(change.dir, "spec.md"), { language: config.spec.language }));
12189
+ if (change.delta) findings.push(...lintDelta(change.delta, living, path12.join(change.dir, "spec.md"), { language: config.spec.language }));
12084
12190
  const trace = checkTrace({
12085
12191
  specs: workspace.specs,
12086
12192
  change,
@@ -12145,7 +12251,7 @@ async function collectMetrics(root, now = /* @__PURE__ */ new Date()) {
12145
12251
  attention.push({ slug: change.slug, kind: "missing-evidence", detail: `evidencia ${change.scenariosPassed}/${change.scenariosTotal}` });
12146
12252
  }
12147
12253
  }
12148
- const archiveDir = path11.join(workspace.sddDir, "changes", "archive");
12254
+ const archiveDir = path12.join(workspace.sddDir, "changes", "archive");
12149
12255
  const archivedEntries = await listDirs(archiveDir);
12150
12256
  const throughputByMonth = {};
12151
12257
  for (const entry of archivedEntries) {
@@ -12183,10 +12289,10 @@ async function collectMetrics(root, now = /* @__PURE__ */ new Date()) {
12183
12289
  }
12184
12290
  async function readDelta(change) {
12185
12291
  const { readTextIfExists: readTextIfExists2 } = await import("./fsx-VF2P7ALA-XKEWTMDH.js");
12186
- return readTextIfExists2(path11.join(change.dir, "spec.md"));
12292
+ return readTextIfExists2(path12.join(change.dir, "spec.md"));
12187
12293
  }
12188
12294
  async function generatePresentation(opts) {
12189
- const root = path12.resolve(opts.root);
12295
+ const root = path13.resolve(opts.root);
12190
12296
  const { workspace, config } = await loadWorkspace(root);
12191
12297
  const language = config.project.language;
12192
12298
  const change = workspace.changes.find((c) => c.slug === opts.slug);
@@ -12196,17 +12302,17 @@ async function generatePresentation(opts) {
12196
12302
  if (!change.delta) {
12197
12303
  return { slug: opts.slug, diagnostics: [diag("ATLAS-PRESENT-001", "error", `El cambio "${opts.slug}" no tiene spec.md (delta)`)] };
12198
12304
  }
12199
- const deltaPath = path12.join(change.dir, "spec.md");
12305
+ const deltaPath = path13.join(change.dir, "spec.md");
12200
12306
  const deltaContent = await readTextIfExists(deltaPath) ?? "";
12201
12307
  const hash = artifactHash(deltaContent);
12202
12308
  const now = opts.now ?? /* @__PURE__ */ new Date();
12203
- const presentationDir = path12.join(change.dir, "presentation");
12204
- const mockupsCopyDir = path12.join(presentationDir, "mockups");
12309
+ const presentationDir = path13.join(change.dir, "presentation");
12310
+ const mockupsCopyDir = path13.join(presentationDir, "mockups");
12205
12311
  await ensureDir(presentationDir);
12206
12312
  const mockupInfo = await copyMockups(root, change, mockupsCopyDir);
12207
- const proposalRaw = await readTextIfExists(path12.join(change.dir, "proposal.md"));
12313
+ const proposalRaw = await readTextIfExists(path13.join(change.dir, "proposal.md"));
12208
12314
  const proposalBody = proposalRaw ? parseFrontmatter(proposalRaw).body : "";
12209
- const approvals = await loadApprovals(path12.join(root, ".sdd"));
12315
+ const approvals = await loadApprovals(path13.join(root, ".sdd"));
12210
12316
  const approval = verifyApproval(change, approvals.byArtifact, config, deltaContent);
12211
12317
  const labels = labelsFor(language);
12212
12318
  const html = page({
@@ -12227,7 +12333,7 @@ async function generatePresentation(opts) {
12227
12333
  approveHint: `satlas approve ${change.slug} --by "<nombre>" --channel presentation`,
12228
12334
  ...approval.status === "valid" && approval.approvedBy ? { approval: { by: approval.approvedBy, at: approval.approvedAt ?? "" } } : {}
12229
12335
  });
12230
- const outFile = path12.join(presentationDir, "index.html");
12336
+ const outFile = path13.join(presentationDir, "index.html");
12231
12337
  await writeText(outFile, html);
12232
12338
  return { slug: change.slug, path: outFile, hash, diagnostics: mockupInfo.diagnostics };
12233
12339
  }
@@ -12239,12 +12345,12 @@ async function copyMockups(root, change, destDir) {
12239
12345
  await ensureDir(destDir);
12240
12346
  const screens = [];
12241
12347
  for (const screen of manifest.screens) {
12242
- const src = path12.join(dir, screen.file);
12348
+ const src = path13.join(dir, screen.file);
12243
12349
  if (!await exists(src)) {
12244
12350
  diagnostics.push(diag("ATLAS-PRESENT-002", "warning", `El mockup ${screen.file} no existe y no se incluir\xE1`, { path: src }));
12245
12351
  continue;
12246
12352
  }
12247
- await copyFile(src, path12.join(destDir, screen.file));
12353
+ await copyFile(src, path13.join(destDir, screen.file));
12248
12354
  screens.push({
12249
12355
  id: screen.id,
12250
12356
  title: screen.title ?? screen.id,
@@ -12254,13 +12360,13 @@ async function copyMockups(root, change, destDir) {
12254
12360
  });
12255
12361
  }
12256
12362
  const screenshots = [];
12257
- const shotsDir = path12.join(dir, "screens");
12363
+ const shotsDir = path13.join(dir, "screens");
12258
12364
  if (await exists(shotsDir)) {
12259
- await ensureDir(path12.join(destDir, "screens"));
12365
+ await ensureDir(path13.join(destDir, "screens"));
12260
12366
  const { listDir: listDir4 } = await import("./fsx-VF2P7ALA-XKEWTMDH.js");
12261
12367
  for (const file of await listDir4(shotsDir)) {
12262
12368
  if (!/\.(png|jpe?g|webp)$/i.test(file)) continue;
12263
- await copyFile(path12.join(shotsDir, file), path12.join(destDir, "screens", file));
12369
+ await copyFile(path13.join(shotsDir, file), path13.join(destDir, "screens", file));
12264
12370
  screenshots.push(`mockups/screens/${file}`);
12265
12371
  }
12266
12372
  }
@@ -12476,11 +12582,11 @@ var SOURCE_EXT = /* @__PURE__ */ new Set([
12476
12582
  ".groovy"
12477
12583
  ]);
12478
12584
  async function adoptWorkspace(opts) {
12479
- const root = path13.resolve(opts.root);
12585
+ const root = path14.resolve(opts.root);
12480
12586
  const dryRun = opts.dryRun ?? false;
12481
12587
  const diagnostics = [];
12482
- const sddDir = path13.join(root, ".sdd");
12483
- if (!await exists(path13.join(sddDir, "config.yaml"))) {
12588
+ const sddDir = path14.join(root, ".sdd");
12589
+ if (!await exists(path14.join(sddDir, "config.yaml"))) {
12484
12590
  return {
12485
12591
  root,
12486
12592
  domains: [],
@@ -12491,8 +12597,8 @@ async function adoptWorkspace(opts) {
12491
12597
  }
12492
12598
  const { config } = await loadConfig(sddDir);
12493
12599
  const files = await walkFiles(root, { skipDirs: [...SKIP_DIRS] });
12494
- const inventoried = files.map((f) => toPosix(path13.relative(root, f.path)));
12495
- const relative = inventoried.filter((f) => SOURCE_EXT.has(path13.extname(f).toLowerCase()));
12600
+ const inventoried = files.map((f) => toPosix(path14.relative(root, f.path)));
12601
+ const relative = inventoried.filter((f) => SOURCE_EXT.has(path14.extname(f).toLowerCase()));
12496
12602
  if (relative.length === 0) {
12497
12603
  diagnostics.push(diag("ATLAS-ADOPT-002", "warning", "No se encontraron archivos de c\xF3digo para inventariar", { path: root, suggestion: "A\xF1ade el dominio a mano con --domains <nombre>" }));
12498
12604
  }
@@ -12520,13 +12626,13 @@ async function adoptWorkspace(opts) {
12520
12626
  const createdSpecs = [];
12521
12627
  for (const name of domainNames.sort()) {
12522
12628
  const domainFiles = (counts.get(name) ?? []).sort();
12523
- const existingSpec = await exists(path13.join(sddDir, "specs", name, "spec.md"));
12629
+ const existingSpec = await exists(path14.join(sddDir, "specs", name, "spec.md"));
12524
12630
  domains.push({ name, files: domainFiles.length, samples: domainFiles.slice(0, 8), existingSpec });
12525
12631
  if (existingSpec) {
12526
- diagnostics.push(diag("ATLAS-ADOPT-003", "info", `La spec de "${name}" ya existe; se conserva`, { path: path13.join(sddDir, "specs", name, "spec.md") }));
12632
+ diagnostics.push(diag("ATLAS-ADOPT-003", "info", `La spec de "${name}" ya existe; se conserva`, { path: path14.join(sddDir, "specs", name, "spec.md") }));
12527
12633
  continue;
12528
12634
  }
12529
- const specPath = path13.join(sddDir, "specs", name, "spec.md");
12635
+ const specPath = path14.join(sddDir, "specs", name, "spec.md");
12530
12636
  if (!dryRun) {
12531
12637
  await writeText(specPath, baselineSpec(name, domainFiles));
12532
12638
  createdSpecs.push(specPath);
@@ -12534,7 +12640,7 @@ async function adoptWorkspace(opts) {
12534
12640
  createdSpecs.push(specPath);
12535
12641
  }
12536
12642
  }
12537
- const reportPath = path13.join(sddDir, "adopt-report.md");
12643
+ const reportPath = path14.join(sddDir, "adopt-report.md");
12538
12644
  if (!dryRun) {
12539
12645
  await writeText(reportPath, adoptReport({ root, config, domains, detection: detection?.best, language: config.project.language, files: relative.length, inventoried: inventoried.length, now: opts.now ?? /* @__PURE__ */ new Date() }));
12540
12646
  }
@@ -12554,12 +12660,12 @@ async function loadProjectProfiles(root, dirs) {
12554
12660
  if (!await exists(dir)) continue;
12555
12661
  out.push(...await loadProfilesFromDir(dir));
12556
12662
  }
12557
- const custom = path13.join(root, ".sdd", "profiles", "custom");
12663
+ const custom = path14.join(root, ".sdd", "profiles", "custom");
12558
12664
  if (await exists(custom)) out.push(...await loadProfilesFromDir(custom));
12559
12665
  return out;
12560
12666
  }
12561
12667
  async function fallbackDomainName(root, projectName) {
12562
- const pkg = await readTextIfExists(path13.join(root, "package.json"));
12668
+ const pkg = await readTextIfExists(path14.join(root, "package.json"));
12563
12669
  if (pkg) {
12564
12670
  try {
12565
12671
  const data = JSON.parse(pkg);
@@ -12646,11 +12752,11 @@ function adoptReport(input) {
12646
12752
  return lines.join("\n");
12647
12753
  }
12648
12754
  async function initWorkspace(opts) {
12649
- const root = path14.resolve(opts.root);
12650
- const sddDir = path14.join(root, ".sdd");
12755
+ const root = path15.resolve(opts.root);
12756
+ const sddDir = path15.join(root, ".sdd");
12651
12757
  const diagnostics = [];
12652
12758
  const created = [];
12653
- if (await exists(path14.join(sddDir, "config.yaml"))) {
12759
+ if (await exists(path15.join(sddDir, "config.yaml"))) {
12654
12760
  return {
12655
12761
  sddDir,
12656
12762
  created,
@@ -12660,29 +12766,29 @@ async function initWorkspace(opts) {
12660
12766
  created.push(...await ensureSddDirs(sddDir));
12661
12767
  const language = opts.language ?? "es";
12662
12768
  const cfg = defaultConfig({ name: opts.name, language });
12663
- cfg.project.name = opts.name ?? path14.basename(root);
12769
+ cfg.project.name = opts.name ?? path15.basename(root);
12664
12770
  await writeConfig(sddDir, cfg);
12665
- created.push(path14.join(sddDir, "config.yaml"));
12771
+ created.push(path15.join(sddDir, "config.yaml"));
12666
12772
  const templates = templatesFor(language);
12667
- await writeText(path14.join(sddDir, "constitution.md"), templates.constitution);
12668
- created.push(path14.join(sddDir, "constitution.md"));
12669
- await writeText(path14.join(sddDir, "glossary.md"), templates.glossary);
12670
- created.push(path14.join(sddDir, "glossary.md"));
12773
+ await writeText(path15.join(sddDir, "constitution.md"), templates.constitution);
12774
+ created.push(path15.join(sddDir, "constitution.md"));
12775
+ await writeText(path15.join(sddDir, "glossary.md"), templates.glossary);
12776
+ created.push(path15.join(sddDir, "glossary.md"));
12671
12777
  const detected = await detectIfPossible(root, opts.profilesDir);
12672
12778
  if (detected) {
12673
12779
  const yaml2 = detectionToYaml(detected, localStamp(opts.now));
12674
- await writeText(path14.join(sddDir, "profiles", "detected.yaml"), yaml2);
12675
- created.push(path14.join(sddDir, "profiles", "detected.yaml"));
12780
+ await writeText(path15.join(sddDir, "profiles", "detected.yaml"), yaml2);
12781
+ created.push(path15.join(sddDir, "profiles", "detected.yaml"));
12676
12782
  if (detected.best) {
12677
12783
  diagnostics.push(diag("ATLAS-INIT-002", "info", `Stack detectado: ${detected.best.displayName} (${detected.best.score} puntos)`, { suggestion: "Revisa .sdd/profiles/detected.yaml" }));
12678
12784
  } else {
12679
12785
  diagnostics.push(diag("ATLAS-INIT-003", "info", "No se detect\xF3 un stack conocido: se usar\xE1 el perfil gen\xE9rico", { suggestion: "Crea un perfil propio con `satlas profile` (F2) o edita .sdd/profiles/custom/" }));
12680
12786
  }
12681
12787
  }
12682
- await writeText(path14.join(sddDir, "INDEX.md"), initialIndex(language, cfg.project.name));
12683
- created.push(path14.join(sddDir, "INDEX.md"));
12788
+ await writeText(path15.join(sddDir, "INDEX.md"), initialIndex(language, cfg.project.name));
12789
+ created.push(path15.join(sddDir, "INDEX.md"));
12684
12790
  if (opts.local) {
12685
- const gitignore = path14.join(root, ".gitignore");
12791
+ const gitignore = path15.join(root, ".gitignore");
12686
12792
  const current = await readTextIfExists(gitignore) ?? "";
12687
12793
  if (!/(^|\n)\.sdd\/?(\n|$)/.test(current)) {
12688
12794
  const next = current.endsWith("\n") || current === "" ? `${current}.sdd/
@@ -12720,7 +12826,7 @@ async function createChange(opts) {
12720
12826
  const diagnostics = [];
12721
12827
  const slug = opts.slug.trim().toLowerCase();
12722
12828
  const language = opts.language ?? opts.cfg?.project.language ?? "es";
12723
- const dir = path15.join(path15.resolve(opts.root), ".sdd", "changes", slug);
12829
+ const dir = path16.join(path16.resolve(opts.root), ".sdd", "changes", slug);
12724
12830
  if (!SLUG_RE.test(slug)) {
12725
12831
  return {
12726
12832
  slug,
@@ -12751,20 +12857,20 @@ async function createChange(opts) {
12751
12857
  await ensureDir(dir);
12752
12858
  const templates = templatesFor(language);
12753
12859
  const files = [];
12754
- const metaFile = path15.join(dir, "meta.yaml");
12860
+ const metaFile = path16.join(dir, "meta.yaml");
12755
12861
  await writeText(metaFile, changeMetaYaml(meta));
12756
12862
  files.push(metaFile);
12757
12863
  if (lane === "fix") {
12758
- const fixFile = path15.join(dir, "fix.md");
12864
+ const fixFile = path16.join(dir, "fix.md");
12759
12865
  await writeText(fixFile, renderTemplate(templates.fix, { TITLE: title }));
12760
12866
  files.push(fixFile);
12761
12867
  return { slug, dir, files, diagnostics };
12762
12868
  }
12763
- const proposalFile = path15.join(dir, "proposal.md");
12869
+ const proposalFile = path16.join(dir, "proposal.md");
12764
12870
  await writeText(proposalFile, renderTemplate(templates.proposal, { TITLE: title }));
12765
12871
  files.push(proposalFile);
12766
12872
  const domainUpper = domain.replace(/[^a-z0-9]/gi, "").toUpperCase() || "GEN";
12767
- const deltaFile = path15.join(dir, "spec.md");
12873
+ const deltaFile = path16.join(dir, "spec.md");
12768
12874
  await writeText(deltaFile, templates.specDelta({ title, domainUpper, domain }));
12769
12875
  files.push(deltaFile);
12770
12876
  return { slug, dir, files, diagnostics };
@@ -12877,7 +12983,7 @@ async function restoreFile(file, previous) {
12877
12983
  }
12878
12984
  }
12879
12985
  async function archiveChange(opts) {
12880
- const root = path16.resolve(opts.root);
12986
+ const root = path17.resolve(opts.root);
12881
12987
  const diagnostics = [];
12882
12988
  const { config } = await loadWorkspace(root);
12883
12989
  const language = opts.language ?? config.project.language;
@@ -12893,16 +12999,43 @@ async function archiveChange(opts) {
12893
12999
  return { slug: opts.slug, fold: emptyFold, diagnostics, dryRun: opts.dryRun ?? false };
12894
13000
  }
12895
13001
  const nowFix = opts.now ?? /* @__PURE__ */ new Date();
12896
- const targetFix = path16.join(root, ".sdd", "changes", "archive", `${localMonth(nowFix)}-${opts.slug}`);
13002
+ const targetFix = path17.join(root, ".sdd", "changes", "archive", `${localMonth(nowFix)}-${opts.slug}`);
12897
13003
  if (await exists(targetFix)) {
12898
13004
  diagnostics.push(diag("ATLAS-ARCH-002", "error", `Ya existe un cambio archivado en ${targetFix}`, { path: targetFix }));
12899
13005
  return { slug: opts.slug, fold: emptyFold, diagnostics, dryRun: opts.dryRun ?? false };
12900
13006
  }
13007
+ let livingFix;
13008
+ let livingCreated = false;
12901
13009
  if (!opts.dryRun) {
12902
- await ensureDir(path16.dirname(targetFix));
13010
+ const fixRaw = await readTextIfExists(path17.join(change.dir, "fix.md")) ?? "";
13011
+ try {
13012
+ const written = await writeLivingFix(root, {
13013
+ slug: opts.slug,
13014
+ date: localDate(nowFix),
13015
+ result: "pass",
13016
+ ...change.meta.domain !== void 0 ? { domain: change.meta.domain } : {},
13017
+ ...change.meta.title !== void 0 ? { title: change.meta.title } : {},
13018
+ covers: change.fixCovers ?? [],
13019
+ content: fixRaw
13020
+ });
13021
+ livingFix = written.relativePath;
13022
+ livingCreated = written.created;
13023
+ } catch (error) {
13024
+ diagnostics.push(
13025
+ diag("ATLAS-ARCH-006", "error", `No se pudo conservar el fix vivo: ${error.message}`, {
13026
+ path: path17.join(root, ".sdd", "fixes"),
13027
+ suggestion: "Revisa los permisos de .sdd/fixes y vuelve a intentar; el fix sigue sin archivar"
13028
+ })
13029
+ );
13030
+ return { slug: opts.slug, fold: emptyFold, diagnostics, dryRun: false };
13031
+ }
13032
+ }
13033
+ if (!opts.dryRun) {
13034
+ await ensureDir(path17.dirname(targetFix));
12903
13035
  try {
12904
13036
  await moveDirectory(change.dir, targetFix);
12905
13037
  } catch (error) {
13038
+ if (livingCreated && livingFix) await removeFile(path17.join(root, livingFix));
12906
13039
  diagnostics.push(
12907
13040
  diag("ATLAS-ARCH-004", "error", `No se pudo mover el cambio al hist\xF3rico: ${error.message}`, {
12908
13041
  path: change.dir,
@@ -12911,9 +13044,16 @@ async function archiveChange(opts) {
12911
13044
  );
12912
13045
  return { slug: opts.slug, fold: emptyFold, diagnostics, dryRun: false };
12913
13046
  }
12914
- await regenerateIndex(root, config);
13047
+ await regenerateIndex(root, config, nowFix);
12915
13048
  }
12916
- return { slug: opts.slug, archivedTo: targetFix, fold: emptyFold, diagnostics, dryRun: opts.dryRun ?? false };
13049
+ return {
13050
+ slug: opts.slug,
13051
+ archivedTo: targetFix,
13052
+ ...livingFix !== void 0 ? { livingFix } : {},
13053
+ fold: emptyFold,
13054
+ diagnostics,
13055
+ dryRun: opts.dryRun ?? false
13056
+ };
12917
13057
  }
12918
13058
  if (!change.delta) {
12919
13059
  diagnostics.push(diag("ATLAS-ARCH-003", "error", `El cambio "${opts.slug}" no tiene spec.md (delta): no hay nada que plegar`, { path: change.dir }));
@@ -12921,10 +13061,10 @@ async function archiveChange(opts) {
12921
13061
  }
12922
13062
  const domain = change.meta.domain;
12923
13063
  if (!domain) {
12924
- diagnostics.push(diag("ATLAS-ARCH-001", "error", `El cambio "${opts.slug}" no declara domain en meta.yaml`, { path: path16.join(change.dir, "meta.yaml") }));
13064
+ diagnostics.push(diag("ATLAS-ARCH-001", "error", `El cambio "${opts.slug}" no declara domain en meta.yaml`, { path: path17.join(change.dir, "meta.yaml") }));
12925
13065
  return { slug: opts.slug, fold: { content: "", applied: { added: [], modified: [], removed: [], renamed: [] }, diagnostics }, diagnostics, dryRun: opts.dryRun ?? false };
12926
13066
  }
12927
- const specFile = path16.join(root, ".sdd", "specs", domain, "spec.md");
13067
+ const specFile = path17.join(root, ".sdd", "specs", domain, "spec.md");
12928
13068
  const existing = await readTextIfExists(specFile);
12929
13069
  const base = existing ?? `---
12930
13070
  domain: ${domain}
@@ -12954,8 +13094,8 @@ ${Object.entries(nextFm).map(([k, v]) => `${k}: ${String(v)}`).join("\n")}
12954
13094
 
12955
13095
  ${body}`;
12956
13096
  const month = localMonth(now);
12957
- const archiveDir = path16.join(root, ".sdd", "changes", "archive");
12958
- const target = path16.join(archiveDir, `${month}-${opts.slug}`);
13097
+ const archiveDir = path17.join(root, ".sdd", "changes", "archive");
13098
+ const target = path17.join(archiveDir, `${month}-${opts.slug}`);
12959
13099
  if (await exists(target)) {
12960
13100
  diagnostics.push(diag("ATLAS-ARCH-002", "error", `Ya existe un cambio archivado en ${target}`, { path: target }));
12961
13101
  return { slug: opts.slug, domain, fold, diagnostics, dryRun: opts.dryRun ?? false };
@@ -12981,17 +13121,25 @@ ${body}`;
12981
13121
  }
12982
13122
  async function regenerateIndex(root, cfg, now = /* @__PURE__ */ new Date()) {
12983
13123
  const { workspace, config } = cfg ? { workspace: (await loadWorkspace(root)).workspace, config: cfg } : await loadWorkspace(root);
12984
- const archiveDir = path16.join(root, ".sdd", "changes", "archive");
13124
+ const archiveDir = path17.join(root, ".sdd", "changes", "archive");
12985
13125
  const archived = await exists(archiveDir) ? (await fs.readdir(archiveDir)).filter((e) => !e.startsWith(".")).length : 0;
13126
+ const fixes = await loadLivingFixes(root);
12986
13127
  const markdown2 = indexMarkdown({
12987
13128
  projectName: config.project.name,
12988
13129
  language: config.project.language,
12989
13130
  specs: workspace.specs.map((s) => ({ domain: s.domain, requirements: s.spec.requirements.length })),
12990
13131
  changes: workspace.changes.map((c) => ({ slug: c.slug, lane: c.meta?.lane ?? config.lanes.default })),
13132
+ fixes: fixes.map((fix) => ({ slug: fix.slug, date: fix.date, result: fix.result, ...fix.domain !== void 0 ? { domain: fix.domain } : {} })),
12991
13133
  archived
12992
13134
  });
12993
13135
  void now;
12994
- await writeText(path16.join(root, ".sdd", "INDEX.md"), markdown2);
13136
+ await writeText(path17.join(root, ".sdd", "INDEX.md"), markdown2);
13137
+ }
13138
+ async function removeFile(file) {
13139
+ try {
13140
+ await rm(file, { force: true });
13141
+ } catch {
13142
+ }
12995
13143
  }
12996
13144
  var SCHEMA_VERSION = 1;
12997
13145
  var BACKUP_DIRNAME = ".backup";
@@ -13004,28 +13152,28 @@ var SCHEMA_MIGRATIONS = [
13004
13152
  to: SCHEMA_VERSION
13005
13153
  }
13006
13154
  ];
13007
- var FIXED_ARTIFACTS = ["config.yaml", "approvals.yaml", path17.join("profiles", "detected.yaml")];
13008
- var CHANGE_ARTIFACTS = ["meta.yaml", path17.join("mockups", "manifest.yaml")];
13155
+ var FIXED_ARTIFACTS = ["config.yaml", "approvals.yaml", path18.join("profiles", "detected.yaml")];
13156
+ var CHANGE_ARTIFACTS = ["meta.yaml", path18.join("mockups", "manifest.yaml")];
13009
13157
  async function pushChangeArtifacts(out, sddDir, relDir) {
13010
13158
  for (const rel of CHANGE_ARTIFACTS) {
13011
- const abs = path17.join(sddDir, relDir, rel);
13012
- if (await exists(abs)) out.push({ artifact: toPosix(path17.join(relDir, rel)), path: abs });
13159
+ const abs = path18.join(sddDir, relDir, rel);
13160
+ if (await exists(abs)) out.push({ artifact: toPosix(path18.join(relDir, rel)), path: abs });
13013
13161
  }
13014
13162
  }
13015
13163
  async function collectVersionedArtifacts(root) {
13016
- const sddDir = path17.join(root, ".sdd");
13164
+ const sddDir = path18.join(root, ".sdd");
13017
13165
  const out = [];
13018
13166
  for (const rel of FIXED_ARTIFACTS) {
13019
- const abs = path17.join(sddDir, rel);
13167
+ const abs = path18.join(sddDir, rel);
13020
13168
  if (await exists(abs)) out.push({ artifact: toPosix(rel), path: abs });
13021
13169
  }
13022
- const changesDir = path17.join(sddDir, "changes");
13170
+ const changesDir = path18.join(sddDir, "changes");
13023
13171
  for (const slug of await listDirs(changesDir)) {
13024
13172
  if (slug === "archive") continue;
13025
- await pushChangeArtifacts(out, sddDir, path17.join("changes", slug));
13173
+ await pushChangeArtifacts(out, sddDir, path18.join("changes", slug));
13026
13174
  }
13027
- for (const entry of await listDirs(path17.join(changesDir, "archive"))) {
13028
- await pushChangeArtifacts(out, sddDir, path17.join("changes", "archive", entry));
13175
+ for (const entry of await listDirs(path18.join(changesDir, "archive"))) {
13176
+ await pushChangeArtifacts(out, sddDir, path18.join("changes", "archive", entry));
13029
13177
  }
13030
13178
  return out;
13031
13179
  }
@@ -13110,7 +13258,7 @@ async function removeDir(dir) {
13110
13258
  } catch {
13111
13259
  }
13112
13260
  }
13113
- async function removeFile(file) {
13261
+ async function removeFile2(file) {
13114
13262
  try {
13115
13263
  await fs2.rm(file, { force: true });
13116
13264
  } catch {
@@ -13122,22 +13270,22 @@ function backupName(now) {
13122
13270
  async function applyUpgrade(root, now = /* @__PURE__ */ new Date()) {
13123
13271
  const plan = await planUpgrade(root);
13124
13272
  if (plan.pending.length === 0) return { status: "up-to-date", plan, applied: [] };
13125
- const sddDir = path17.join(root, ".sdd");
13126
- const backupRoot = path17.join(sddDir, BACKUP_DIRNAME);
13273
+ const sddDir = path18.join(root, ".sdd");
13274
+ const backupRoot = path18.join(sddDir, BACKUP_DIRNAME);
13127
13275
  const name = backupName(now);
13128
- const backupDir = path17.join(backupRoot, name);
13129
- const pointerFile = path17.join(backupRoot, BACKUP_POINTER);
13276
+ const backupDir = path18.join(backupRoot, name);
13277
+ const pointerFile = path18.join(backupRoot, BACKUP_POINTER);
13130
13278
  const previous = [];
13131
13279
  try {
13132
13280
  for (const item of plan.pending) {
13133
13281
  const content = await readTextIfExists(item.path);
13134
13282
  if (content === void 0) throw new Error("el elemento desapareci\xF3 mientras se respaldaba");
13135
13283
  previous.push({ item, contents: content });
13136
- await writeText(path17.join(backupDir, "files", ...item.artifact.split("/")), content);
13284
+ await writeText(path18.join(backupDir, "files", ...item.artifact.split("/")), content);
13137
13285
  }
13138
13286
  await writeText(
13139
- path17.join(backupDir, "backup.yaml"),
13140
- stringifyYaml5(
13287
+ path18.join(backupDir, "backup.yaml"),
13288
+ stringifyYaml6(
13141
13289
  {
13142
13290
  schema_version: SCHEMA_VERSION,
13143
13291
  created_at: localStamp(now),
@@ -13151,7 +13299,7 @@ async function applyUpgrade(root, now = /* @__PURE__ */ new Date()) {
13151
13299
  `);
13152
13300
  } catch (err) {
13153
13301
  await removeDir(backupDir);
13154
- await removeFile(pointerFile);
13302
+ await removeFile2(pointerFile);
13155
13303
  return { status: "failed", plan, applied: [], failure: { artifact: "(respaldo)", message: err.message } };
13156
13304
  }
13157
13305
  const written = [];
@@ -13171,13 +13319,13 @@ async function applyUpgrade(root, now = /* @__PURE__ */ new Date()) {
13171
13319
  }
13172
13320
  }
13173
13321
  }
13174
- await removeFile(pointerFile);
13322
+ await removeFile2(pointerFile);
13175
13323
  await removeDir(backupDir);
13176
13324
  return { status: "failed", plan, applied: [], failure: { artifact: item.artifact, message: err.message } };
13177
13325
  }
13178
13326
  }
13179
13327
  for (const dir of await listDirs(backupRoot)) {
13180
- if (dir !== name) await removeDir(path17.join(backupRoot, dir));
13328
+ if (dir !== name) await removeDir(path18.join(backupRoot, dir));
13181
13329
  }
13182
13330
  return {
13183
13331
  status: "applied",
@@ -13186,20 +13334,20 @@ async function applyUpgrade(root, now = /* @__PURE__ */ new Date()) {
13186
13334
  backup: {
13187
13335
  name,
13188
13336
  dir: backupDir,
13189
- relativeDir: toPosix(path17.relative(root, backupDir)),
13337
+ relativeDir: toPosix(path18.relative(root, backupDir)),
13190
13338
  createdAt: localStamp(now),
13191
13339
  files: plan.pending.map((i) => i.artifact)
13192
13340
  }
13193
13341
  };
13194
13342
  }
13195
13343
  async function rollbackUpgrade(root) {
13196
- const sddDir = path17.join(root, ".sdd");
13197
- const backupRoot = path17.join(sddDir, BACKUP_DIRNAME);
13198
- const pointerFile = path17.join(backupRoot, BACKUP_POINTER);
13344
+ const sddDir = path18.join(root, ".sdd");
13345
+ const backupRoot = path18.join(sddDir, BACKUP_DIRNAME);
13346
+ const pointerFile = path18.join(backupRoot, BACKUP_POINTER);
13199
13347
  const name = (await readTextIfExists(pointerFile))?.trim();
13200
13348
  if (!name) return { status: "no-backup", restored: [] };
13201
- const backupDir = path17.join(backupRoot, name);
13202
- const manifestRaw = await readTextIfExists(path17.join(backupDir, "backup.yaml"));
13349
+ const backupDir = path18.join(backupRoot, name);
13350
+ const manifestRaw = await readTextIfExists(path18.join(backupDir, "backup.yaml"));
13203
13351
  if (manifestRaw === void 0) return { status: "no-backup", restored: [] };
13204
13352
  let files = [];
13205
13353
  try {
@@ -13210,8 +13358,8 @@ async function rollbackUpgrade(root) {
13210
13358
  }
13211
13359
  const restored = [];
13212
13360
  for (const file of files) {
13213
- const from = path17.join(backupDir, "files", ...file.artifact.split("/"));
13214
- const to = path17.join(sddDir, ...file.artifact.split("/"));
13361
+ const from = path18.join(backupDir, "files", ...file.artifact.split("/"));
13362
+ const to = path18.join(sddDir, ...file.artifact.split("/"));
13215
13363
  const content = await readTextIfExists(from);
13216
13364
  if (content === void 0) continue;
13217
13365
  try {
@@ -13222,7 +13370,7 @@ async function rollbackUpgrade(root) {
13222
13370
  }
13223
13371
  }
13224
13372
  await removeDir(backupDir);
13225
- await removeFile(pointerFile);
13373
+ await removeFile2(pointerFile);
13226
13374
  return { status: "restored", restored, backup: name };
13227
13375
  }
13228
13376
  async function upgradeAdvisory(root) {
@@ -13290,7 +13438,7 @@ function resultOf(diagnostic, root) {
13290
13438
  message: { text: diagnostic.message }
13291
13439
  };
13292
13440
  if (diagnostic.path) {
13293
- const rel = path18.relative(root, diagnostic.path);
13441
+ const rel = path19.relative(root, diagnostic.path);
13294
13442
  if (rel !== "") {
13295
13443
  const physicalLocation = {
13296
13444
  artifactLocation: { uri: toPosix(rel) },
@@ -13345,7 +13493,7 @@ async function runDoctor(root) {
13345
13493
  const approvals = await loadApprovals(workspace.sddDir);
13346
13494
  findings.push(...approvals.diagnostics);
13347
13495
  for (const change of workspace.changes) {
13348
- const deltaPath = path19.join(change.dir, "spec.md");
13496
+ const deltaPath = path20.join(change.dir, "spec.md");
13349
13497
  const deltaContent = await readTextIfExists(deltaPath);
13350
13498
  const approval = verifyApproval(change, approvals.byArtifact, config, deltaContent ?? void 0);
13351
13499
  if ((change.planPath || change.tasks) && (approval.status === "missing" || approval.status === "stale")) {
@@ -13367,7 +13515,7 @@ async function runDoctor(root) {
13367
13515
  }
13368
13516
  for (const override of change.meta?.overrides ?? []) {
13369
13517
  if (!override.reason.trim() || !override.by.trim()) {
13370
- findings.push(diag("ATLAS-LIFECYCLE-004", "error", `Override del gate "${override.gate}" sin motivo o autor`, { path: path19.join(change.dir, "meta.yaml") }));
13518
+ findings.push(diag("ATLAS-LIFECYCLE-004", "error", `Override del gate "${override.gate}" sin motivo o autor`, { path: path20.join(change.dir, "meta.yaml") }));
13371
13519
  }
13372
13520
  }
13373
13521
  }
@@ -13399,7 +13547,7 @@ function livingRequirementsMap(specs) {
13399
13547
  return map;
13400
13548
  }
13401
13549
  async function runCiGate(opts) {
13402
- const root = path20.resolve(opts.root);
13550
+ const root = path21.resolve(opts.root);
13403
13551
  const { workspace, config } = await loadWorkspace(root);
13404
13552
  const diagnostics = [];
13405
13553
  const checks = [];
@@ -13410,7 +13558,7 @@ async function runCiGate(opts) {
13410
13558
  let changesErrors = 0;
13411
13559
  let changesWarnings = 0;
13412
13560
  for (const change of workspace.changes) {
13413
- const lintFindings = change.delta ? lintDelta(change.delta, living, path20.join(change.dir, "spec.md"), { language: config.spec.language }) : [];
13561
+ const lintFindings = change.delta ? lintDelta(change.delta, living, path21.join(change.dir, "spec.md"), { language: config.spec.language }) : [];
13414
13562
  const trace = checkTrace({
13415
13563
  specs: workspace.specs,
13416
13564
  change,
@@ -13552,6 +13700,7 @@ var ES2 = {
13552
13700
  "status.title": "Estado de los cambios",
13553
13701
  "status.changes": "Cambios activos",
13554
13702
  "status.specs": "Specs vivas",
13703
+ "status.fixes": "Fixes vivos",
13555
13704
  "status.next": "siguiente:",
13556
13705
  "status.tasks": "tareas",
13557
13706
  "status.evidence": "evidencia",
@@ -13586,6 +13735,7 @@ var EN2 = {
13586
13735
  "status.title": "Change status",
13587
13736
  "status.changes": "Active changes",
13588
13737
  "status.specs": "Living specs",
13738
+ "status.fixes": "Living fixes",
13589
13739
  "status.next": "next:",
13590
13740
  "status.tasks": "tasks",
13591
13741
  "status.evidence": "evidence",
@@ -13630,26 +13780,26 @@ function cliVersion() {
13630
13780
  }
13631
13781
 
13632
13782
  // src/commands/adapters.ts
13633
- import path24 from "path";
13783
+ import path25 from "path";
13634
13784
 
13635
13785
  // ../adapters/dist/index.js
13636
- import path21 from "path";
13637
- import { stringify as stringifyYaml6 } from "yaml";
13638
13786
  import path22 from "path";
13787
+ import { stringify as stringifyYaml7 } from "yaml";
13788
+ import path23 from "path";
13639
13789
  async function loadWorkflow(workflowDir) {
13640
- const phasesDir = path21.join(workflowDir, "phases");
13641
- const snippetsDir = path21.join(workflowDir, "snippets");
13790
+ const phasesDir = path22.join(workflowDir, "phases");
13791
+ const snippetsDir = path22.join(workflowDir, "snippets");
13642
13792
  const phases = [];
13643
13793
  const snippets = /* @__PURE__ */ new Map();
13644
13794
  const hashParts = [];
13645
13795
  for (const entry of (await listDir(phasesDir)).sort()) {
13646
13796
  if (!entry.endsWith(".md")) continue;
13647
- const filePath = path21.join(phasesDir, entry);
13797
+ const filePath = path22.join(phasesDir, entry);
13648
13798
  const raw = await readText(filePath);
13649
13799
  hashParts.push(raw);
13650
13800
  const fm = parseFrontmatter(raw, filePath);
13651
13801
  const data = fm.data;
13652
- const id = typeof data["id"] === "string" ? data["id"] : path21.basename(entry, ".md");
13802
+ const id = typeof data["id"] === "string" ? data["id"] : path22.basename(entry, ".md");
13653
13803
  phases.push({
13654
13804
  id,
13655
13805
  title: typeof data["title"] === "string" ? data["title"] : id,
@@ -13663,9 +13813,9 @@ async function loadWorkflow(workflowDir) {
13663
13813
  }
13664
13814
  for (const entry of (await listDir(snippetsDir)).sort()) {
13665
13815
  if (!entry.endsWith(".md")) continue;
13666
- const raw = await readTextIfExists(path21.join(snippetsDir, entry)) ?? "";
13816
+ const raw = await readTextIfExists(path22.join(snippetsDir, entry)) ?? "";
13667
13817
  hashParts.push(raw);
13668
- snippets.set(path21.basename(entry, ".md"), raw.trim());
13818
+ snippets.set(path22.basename(entry, ".md"), raw.trim());
13669
13819
  }
13670
13820
  return { phases: phases.sort((a, b) => a.id.localeCompare(b.id)), snippets, sourceHash: sha256(hashParts.join("\n---\n")) };
13671
13821
  }
@@ -13689,7 +13839,7 @@ function vars(language, slugToken, commandPrefix) {
13689
13839
  return { SLUG: slugToken, LANGUAGE: language, LANGUAGE_NAME: LANGUAGE_NAMES[language], SDD_DIR: ".sdd", COMMAND_PREFIX: commandPrefix };
13690
13840
  }
13691
13841
  function frontmatter(data, body) {
13692
- const yaml2 = stringifyYaml6(data, { lineWidth: 0 }).trimEnd();
13842
+ const yaml2 = stringifyYaml7(data, { lineWidth: 0 }).trimEnd();
13693
13843
  return `---
13694
13844
  ${yaml2}
13695
13845
  ---
@@ -13847,11 +13997,11 @@ Reglas: la spec es funcional y de negocio (sin tecnolog\xEDa); la trazabilidad e
13847
13997
  function tomlString(value) {
13848
13998
  return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
13849
13999
  }
13850
- var MANIFEST_REL = path22.join(".sdd", ".generated", "manifest.json");
14000
+ var MANIFEST_REL = path23.join(".sdd", ".generated", "manifest.json");
13851
14001
  var BEGIN = "<!-- BEGIN specatlas -->";
13852
14002
  var END = "<!-- END specatlas -->";
13853
14003
  async function compileTargets(opts) {
13854
- const root = path22.resolve(opts.root);
14004
+ const root = path23.resolve(opts.root);
13855
14005
  const language = opts.language ?? "es";
13856
14006
  const diagnostics = [];
13857
14007
  const sources = await loadWorkflow(opts.workflowDir);
@@ -13859,7 +14009,7 @@ async function compileTargets(opts) {
13859
14009
  diagnostics.push({
13860
14010
  code: "ATLAS-ADAPTERS-002",
13861
14011
  severity: "error",
13862
- message: `No hay fases en ${path22.join(opts.workflowDir, "phases")}`,
14012
+ message: `No hay fases en ${path23.join(opts.workflowDir, "phases")}`,
13863
14013
  suggestion: "Revisa la carpeta workflow/phases del proyecto"
13864
14014
  });
13865
14015
  }
@@ -13871,7 +14021,7 @@ async function compileTargets(opts) {
13871
14021
  }
13872
14022
  }
13873
14023
  const compiled = [...deduped.values()];
13874
- const manifestPath = path22.join(root, MANIFEST_REL);
14024
+ const manifestPath = path23.join(root, MANIFEST_REL);
13875
14025
  const previous = await readManifest(manifestPath);
13876
14026
  const previousHashes = /* @__PURE__ */ new Map();
13877
14027
  for (const files2 of Object.values(previous?.targets ?? {})) {
@@ -13882,7 +14032,7 @@ async function compileTargets(opts) {
13882
14032
  const stale = [];
13883
14033
  const missing = [];
13884
14034
  for (const file of compiled) {
13885
- const abs = path22.join(root, file.path);
14035
+ const abs = path23.join(root, file.path);
13886
14036
  const finalContent = file.path === "AGENTS.md" ? mergeAgentsBlock(await readTextIfExists(abs) ?? "", file.content) : file.content;
13887
14037
  const hash = artifactHash(finalContent);
13888
14038
  const existing = await readTextIfExists(abs);
@@ -13898,7 +14048,7 @@ async function compileTargets(opts) {
13898
14048
  stale.push(file.path);
13899
14049
  }
13900
14050
  if (!opts.check && status !== "unchanged") {
13901
- await ensureDir(path22.dirname(abs));
14051
+ await ensureDir(path23.dirname(abs));
13902
14052
  await writeText(abs, finalContent);
13903
14053
  written.push(file.path);
13904
14054
  }
@@ -13952,7 +14102,7 @@ async function readManifest(manifestPath) {
13952
14102
  }
13953
14103
  }
13954
14104
  async function checkAdapters(opts) {
13955
- const manifestPath = path22.join(path22.resolve(opts.root), MANIFEST_REL);
14105
+ const manifestPath = path23.join(path23.resolve(opts.root), MANIFEST_REL);
13956
14106
  const manifestRaw = await readTextIfExists(manifestPath);
13957
14107
  if (manifestRaw === void 0) return { ok: false, manifest: false, stale: [], missing: [], orphaned: [] };
13958
14108
  const manifest = await readManifest(manifestPath);
@@ -13969,7 +14119,7 @@ async function checkAdapters(opts) {
13969
14119
  }
13970
14120
 
13971
14121
  // src/paths.ts
13972
- import path23 from "path";
14122
+ import path24 from "path";
13973
14123
  import { fileURLToPath as fileURLToPath2 } from "url";
13974
14124
  async function resolveProfilesDir() {
13975
14125
  const env = process.env["SPECATLAS_PROFILES_DIR"];
@@ -13979,16 +14129,16 @@ async function resolveProfilesDir() {
13979
14129
  async function resolveWorkflowDir() {
13980
14130
  const env = process.env["SPECATLAS_WORKFLOW_DIR"];
13981
14131
  if (env && await exists(env)) return env;
13982
- return walkUpFor(path23.join("workflow", "phases"));
14132
+ return walkUpFor(path24.join("workflow", "phases"));
13983
14133
  }
13984
14134
  async function walkUpFor(relative) {
13985
- let dir = path23.dirname(fileURLToPath2(import.meta.url));
14135
+ let dir = path24.dirname(fileURLToPath2(import.meta.url));
13986
14136
  for (let i = 0; i < 8; i += 1) {
13987
- const candidate = path23.join(dir, relative);
14137
+ const candidate = path24.join(dir, relative);
13988
14138
  if (await exists(candidate)) {
13989
- return relative.includes(path23.sep) ? path23.dirname(candidate) : candidate;
14139
+ return relative.includes(path24.sep) ? path24.dirname(candidate) : candidate;
13990
14140
  }
13991
- const parent = path23.dirname(dir);
14141
+ const parent = path24.dirname(dir);
13992
14142
  if (parent === dir) break;
13993
14143
  dir = parent;
13994
14144
  }
@@ -14066,7 +14216,7 @@ async function runAdapters(ctx) {
14066
14216
  for (const [target, count2] of byTarget) lines.push(` ${target}: ${count2} archivo(s)`);
14067
14217
  if (report.written.length > 0) {
14068
14218
  lines.push("");
14069
- for (const p of report.written) lines.push(` + ${path24.relative(ctx.cwd, path24.join(root, p))}`);
14219
+ for (const p of report.written) lines.push(` + ${path25.relative(ctx.cwd, path25.join(root, p))}`);
14070
14220
  } else {
14071
14221
  lines.push("");
14072
14222
  lines.push("Sin cambios: los adaptadores ya estaban al d\xEDa.");
@@ -14077,7 +14227,7 @@ async function runAdapters(ctx) {
14077
14227
  }
14078
14228
 
14079
14229
  // src/commands/adopt.ts
14080
- import path25 from "path";
14230
+ import path26 from "path";
14081
14231
  async function runAdopt(ctx) {
14082
14232
  const { root } = await requireWorkspace(ctx);
14083
14233
  const domainsFlag = flagString(ctx.flags, "domains") ?? flagString(ctx.flags, "domain");
@@ -14097,11 +14247,11 @@ async function runAdopt(ctx) {
14097
14247
  }
14098
14248
  if (result.createdSpecs.length > 0) {
14099
14249
  lines.push("");
14100
- for (const spec of result.createdSpecs) lines.push(` ${result.dryRun ? "[dry-run] " : "+ "}${path25.relative(ctx.cwd, spec)}`);
14250
+ for (const spec of result.createdSpecs) lines.push(` ${result.dryRun ? "[dry-run] " : "+ "}${path26.relative(ctx.cwd, spec)}`);
14101
14251
  }
14102
14252
  if (result.reportPath && !result.dryRun) {
14103
14253
  lines.push("");
14104
- lines.push(` Informe: ${path25.relative(ctx.cwd, result.reportPath)}`);
14254
+ lines.push(` Informe: ${path26.relative(ctx.cwd, result.reportPath)}`);
14105
14255
  }
14106
14256
  lines.push("");
14107
14257
  lines.push("Siguiente: fase /satlas-adopt por dominio (requisitos AS-IS) y luego:");
@@ -14114,8 +14264,8 @@ async function runAdopt(ctx) {
14114
14264
  diagnostics: result.diagnostics,
14115
14265
  data: {
14116
14266
  domains: result.domains.map((d) => ({ name: d.name, files: d.files, existingSpec: d.existingSpec })),
14117
- createdSpecs: result.createdSpecs.map((s) => path25.relative(ctx.cwd, s)),
14118
- reportPath: result.reportPath ? path25.relative(ctx.cwd, result.reportPath) : void 0,
14267
+ createdSpecs: result.createdSpecs.map((s) => path26.relative(ctx.cwd, s)),
14268
+ reportPath: result.reportPath ? path26.relative(ctx.cwd, result.reportPath) : void 0,
14119
14269
  stack: result.stack,
14120
14270
  dryRun: result.dryRun
14121
14271
  },
@@ -14124,7 +14274,7 @@ async function runAdopt(ctx) {
14124
14274
  }
14125
14275
 
14126
14276
  // src/commands/analyze.ts
14127
- import path26 from "path";
14277
+ import path27 from "path";
14128
14278
  async function runAnalyzeCommand(ctx) {
14129
14279
  const { root } = await requireWorkspace(ctx);
14130
14280
  const slug = ctx.positionals[0];
@@ -14135,23 +14285,23 @@ async function runAnalyzeCommand(ctx) {
14135
14285
  const lines = [`An\xE1lisis \u2014 ${slug}`, "", ` estado: ${result.status}`, ` hallazgos: ${result.summary.errors} errores, ${result.summary.warnings} avisos`, ` evidencia: ${result.evidence.done}/${result.evidence.total} escenarios`];
14136
14286
  if (result.waves) lines.push(` olas: ${result.waves.blocks} bloque(s), ${result.waves.waves} ola(s), ${result.waves.tasks} tarea(s)`);
14137
14287
  if (result.mockups) lines.push(` mockups: ${result.mockups.screens} pantalla(s)${result.mockups.stale ? " (desactualizados)" : ""}`);
14138
- if (result.path) lines.push("", ` informe: ${path26.relative(ctx.cwd, result.path)}`);
14288
+ if (result.path) lines.push("", ` informe: ${path27.relative(ctx.cwd, result.path)}`);
14139
14289
  for (const finding of result.findings) {
14140
14290
  lines.push(` ${finding.severity.toUpperCase()} ${finding.code} \u2014 ${finding.message}`);
14141
14291
  }
14142
14292
  return {
14143
14293
  exitCode: result.status === "blocked" ? 1 : 0,
14144
14294
  diagnostics: result.findings,
14145
- data: result.path ? { ...result, path: path26.relative(ctx.cwd, result.path) } : result,
14295
+ data: result.path ? { ...result, path: path27.relative(ctx.cwd, result.path) } : result,
14146
14296
  text: lines
14147
14297
  };
14148
14298
  }
14149
14299
 
14150
14300
  // src/commands/approve.ts
14151
- import path28 from "path";
14301
+ import path29 from "path";
14152
14302
 
14153
14303
  // src/commands/issue.ts
14154
- import path27 from "path";
14304
+ import path28 from "path";
14155
14305
  async function runIssue(ctx) {
14156
14306
  const action = ctx.positionals[0] ?? "status";
14157
14307
  const slug = ctx.positionals[1];
@@ -14179,7 +14329,7 @@ async function runIssue(ctx) {
14179
14329
  return { exitCode: 2, diagnostics: [{ code: "ATLAS-ISSUE-000", severity: "error", message: "Falta el slug: satlas issue status <slug>" }] };
14180
14330
  }
14181
14331
  const change = await loadChange(root, slug);
14182
- const { config } = await loadConfig(path27.join(root, ".sdd"));
14332
+ const { config } = await loadConfig(path28.join(root, ".sdd"));
14183
14333
  const tracker = change.meta?.tracker;
14184
14334
  const lines = [`Issue de ${slug}`, ""];
14185
14335
  if (!tracker || tracker.provider !== "github") {
@@ -14250,7 +14400,7 @@ async function runApprove(ctx) {
14250
14400
  };
14251
14401
  }
14252
14402
  const { root, workspace, config } = await requireWorkspace(ctx);
14253
- const artifact = target.includes("/") || target.includes("\\") ? target : path28.join("changes", target, "spec.md");
14403
+ const artifact = target.includes("/") || target.includes("\\") ? target : path29.join("changes", target, "spec.md");
14254
14404
  const channelFlag = flagString(ctx.flags, "channel");
14255
14405
  const channel = channelFlag === "presentation" || channelFlag === "editor" || channelFlag === "pr" || channelFlag === "tracker" ? channelFlag : "cli";
14256
14406
  const change = workspace.changes.find(
@@ -14291,13 +14441,13 @@ async function runApprove(ctx) {
14291
14441
  return {
14292
14442
  exitCode: hasErrors2 ? 1 : 0,
14293
14443
  diagnostics: result.diagnostics,
14294
- data: result.approval ? { ...result.approval, file: path28.relative(ctx.cwd, result.file) } : void 0,
14444
+ data: result.approval ? { ...result.approval, file: path29.relative(ctx.cwd, result.file) } : void 0,
14295
14445
  text: lines
14296
14446
  };
14297
14447
  }
14298
14448
 
14299
14449
  // src/commands/archive.ts
14300
- import path29 from "path";
14450
+ import path30 from "path";
14301
14451
  async function runArchive(ctx) {
14302
14452
  const slug = ctx.positionals[0];
14303
14453
  if (!slug) {
@@ -14321,27 +14471,31 @@ async function runArchive(ctx) {
14321
14471
  }
14322
14472
  const result = await archiveChange({ root, slug, language: config.project.language, dryRun });
14323
14473
  const lines = [msg("archive.title", ctx.language), ""];
14324
- if (result.fold.applied.added.length + result.fold.applied.modified.length + result.fold.applied.removed.length + result.fold.applied.renamed.length > 0) {
14474
+ const operations = result.fold.applied.added.length + result.fold.applied.modified.length + result.fold.applied.removed.length + result.fold.applied.renamed.length;
14475
+ if (operations > 0) {
14325
14476
  lines.push(`${dryRun ? "[dry-run] " : ""}${msg("archive.done", ctx.language)}`);
14326
14477
  lines.push(` agregados: ${result.fold.applied.added.join(", ") || "\u2014"}`);
14327
14478
  lines.push(` modificados: ${result.fold.applied.modified.join(", ") || "\u2014"}`);
14328
14479
  lines.push(` eliminados: ${result.fold.applied.removed.join(", ") || "\u2014"}`);
14329
14480
  lines.push(` renombrados: ${result.fold.applied.renamed.join(", ") || "\u2014"}`);
14330
- if (result.archivedTo) lines.push(` archivado en: ${path29.relative(ctx.cwd, result.archivedTo)}`);
14481
+ } else if (result.livingFix) {
14482
+ lines.push(`${dryRun ? "[dry-run] " : ""}Fix archivado y conservado como fix vivo.`);
14483
+ lines.push(` fix vivo: ${result.livingFix}`);
14331
14484
  } else {
14332
14485
  lines.push("El delta no contiene operaciones (ADDED/MODIFIED/REMOVED/RENAMED).");
14333
14486
  }
14487
+ if (result.archivedTo) lines.push(` archivado en: ${path30.relative(ctx.cwd, result.archivedTo)}`);
14334
14488
  const hasErrors2 = result.diagnostics.some((d) => d.severity === "error");
14335
14489
  return {
14336
14490
  exitCode: hasErrors2 ? 1 : 0,
14337
14491
  diagnostics: result.diagnostics,
14338
- data: { slug: result.slug, domain: result.domain, archivedTo: result.archivedTo, applied: result.fold.applied, dryRun: result.dryRun },
14492
+ data: { slug: result.slug, domain: result.domain, archivedTo: result.archivedTo, livingFix: result.livingFix, applied: result.fold.applied, dryRun: result.dryRun },
14339
14493
  text: lines
14340
14494
  };
14341
14495
  }
14342
14496
 
14343
14497
  // src/commands/ci.ts
14344
- import path30 from "path";
14498
+ import path31 from "path";
14345
14499
  async function runCi(ctx) {
14346
14500
  const { root, config } = await requireWorkspace(ctx);
14347
14501
  const strict = flagBool(ctx.flags, "strict");
@@ -14367,10 +14521,10 @@ async function runCi(ctx) {
14367
14521
  const lines = ["CI de SpecAtlas", ""];
14368
14522
  let sarifWritten;
14369
14523
  if (sarifPath) {
14370
- const target = path30.resolve(ctx.cwd, sarifPath);
14524
+ const target = path31.resolve(ctx.cwd, sarifPath);
14371
14525
  try {
14372
14526
  await writeText(target, toSarifText({ diagnostics: gate.diagnostics, root, version: cliVersion(), failed: gate.failed }));
14373
- sarifWritten = path30.relative(ctx.cwd, target);
14527
+ sarifWritten = path31.relative(ctx.cwd, target);
14374
14528
  } catch (err) {
14375
14529
  diagnostics.push({
14376
14530
  code: "ATLAS-CI-SARIF-001",
@@ -14405,12 +14559,12 @@ async function runCi(ctx) {
14405
14559
  }
14406
14560
 
14407
14561
  // src/commands/profile.ts
14408
- import path31 from "path";
14562
+ import path32 from "path";
14409
14563
  async function runProfile(ctx) {
14410
14564
  const action = ctx.positionals[0] ?? "detect";
14411
14565
  const { root, workspace } = await requireWorkspace(ctx);
14412
14566
  const officialDir = await resolveProfilesDir();
14413
- const customDir = path31.join(workspace.sddDir, "profiles", "custom");
14567
+ const customDir = path32.join(workspace.sddDir, "profiles", "custom");
14414
14568
  const profiles = [...officialDir ? await loadProfilesFromDir(officialDir) : [], ...await loadProfilesFromDir(customDir)];
14415
14569
  if (action === "detect") {
14416
14570
  const result = await detectProfiles(root, profiles);
@@ -14439,7 +14593,7 @@ async function runProfile(ctx) {
14439
14593
  if (!name) {
14440
14594
  return { exitCode: 2, diagnostics: [{ code: "ATLAS-PROFILE-001", severity: "error", message: "Falta el nombre del perfil: satlas profile create <nombre>" }] };
14441
14595
  }
14442
- const file = path31.join(customDir, `${name}.yaml`);
14596
+ const file = path32.join(customDir, `${name}.yaml`);
14443
14597
  if (await exists(file)) {
14444
14598
  return { exitCode: 2, diagnostics: [{ code: "ATLAS-PROFILE-002", severity: "error", message: `El perfil ya existe: ${file}` }] };
14445
14599
  }
@@ -14448,7 +14602,7 @@ async function runProfile(ctx) {
14448
14602
  exitCode: 0,
14449
14603
  diagnostics: [],
14450
14604
  data: { file },
14451
- text: [`Perfil creado: ${path31.relative(ctx.cwd, file)}`, "", "Ed\xEDtalo con los comandos reales de tu stack (build/lint/test) y tus anti-patrones."]
14605
+ text: [`Perfil creado: ${path32.relative(ctx.cwd, file)}`, "", "Ed\xEDtalo con los comandos reales de tu stack (build/lint/test) y tus anti-patrones."]
14452
14606
  };
14453
14607
  }
14454
14608
  return {
@@ -14457,7 +14611,7 @@ async function runProfile(ctx) {
14457
14611
  };
14458
14612
  }
14459
14613
  async function isInside(dir, name) {
14460
- return exists(path31.join(dir, `${name}.yaml`));
14614
+ return exists(path32.join(dir, `${name}.yaml`));
14461
14615
  }
14462
14616
  function profileTemplate(name) {
14463
14617
  return `name: ${name}
@@ -14485,7 +14639,7 @@ assumptions: []
14485
14639
  }
14486
14640
 
14487
14641
  // src/commands/doctor.ts
14488
- import path32 from "path";
14642
+ import path33 from "path";
14489
14643
  async function runDoctorCommand(ctx) {
14490
14644
  const { root, config } = await requireWorkspace(ctx);
14491
14645
  const report = await runDoctor(root);
@@ -14510,7 +14664,7 @@ async function runDoctorCommand(ctx) {
14510
14664
  lines.push("Sin hallazgos. El workspace est\xE1 sano.");
14511
14665
  } else {
14512
14666
  for (const finding of report.findings) {
14513
- const location = finding.path ? ` ${path32.relative(ctx.cwd, finding.path)}${finding.line ? `:${finding.line}` : ""}` : "";
14667
+ const location = finding.path ? ` ${path33.relative(ctx.cwd, finding.path)}${finding.line ? `:${finding.line}` : ""}` : "";
14514
14668
  lines.push(` ${finding.severity.toUpperCase()} ${finding.code}${location} \u2014 ${finding.message}`);
14515
14669
  if (finding.suggestion) lines.push(` \u21B3 ${finding.suggestion}`);
14516
14670
  }
@@ -14584,7 +14738,7 @@ async function runHelp(ctx, commandName) {
14584
14738
  }
14585
14739
 
14586
14740
  // src/commands/init.ts
14587
- import path33 from "path";
14741
+ import path34 from "path";
14588
14742
  async function runInit(ctx) {
14589
14743
  const languageFlag = flagString(ctx.flags, "language");
14590
14744
  const language = languageFlag === "en" ? "en" : languageFlag === "es" ? "es" : void 0;
@@ -14631,11 +14785,11 @@ async function runInit(ctx) {
14631
14785
  const lines = [msg("init.title", ctx.language), ""];
14632
14786
  if (result.created.length > 0) {
14633
14787
  lines.push(msg("init.done", ctx.language));
14634
- for (const file of result.created) lines.push(` + ${path33.relative(ctx.cwd, file)}`);
14788
+ for (const file of result.created) lines.push(` + ${path34.relative(ctx.cwd, file)}`);
14635
14789
  if (compiled.length > 0) {
14636
14790
  lines.push("");
14637
14791
  lines.push("Adaptadores compilados:");
14638
- for (const file of compiled) lines.push(` + ${path33.relative(ctx.cwd, path33.join(ctx.cwd, file))}`);
14792
+ for (const file of compiled) lines.push(` + ${path34.relative(ctx.cwd, path34.join(ctx.cwd, file))}`);
14639
14793
  }
14640
14794
  lines.push("");
14641
14795
  lines.push("Siguiente: crea un cambio con `satlas new <slug>` y escribe la spec funcional.");
@@ -14645,8 +14799,8 @@ async function runInit(ctx) {
14645
14799
  exitCode: hasErrors2 ? 1 : 0,
14646
14800
  diagnostics: result.diagnostics,
14647
14801
  data: {
14648
- sddDir: path33.relative(ctx.cwd, result.sddDir),
14649
- created: result.created.map((f) => path33.relative(ctx.cwd, f)),
14802
+ sddDir: path34.relative(ctx.cwd, result.sddDir),
14803
+ created: result.created.map((f) => path34.relative(ctx.cwd, f)),
14650
14804
  detected: result.detected?.matches.map((m) => ({ name: m.name, score: m.score })),
14651
14805
  best: result.detected?.best?.name
14652
14806
  },
@@ -14687,7 +14841,7 @@ async function runMetrics(ctx) {
14687
14841
  }
14688
14842
 
14689
14843
  // src/commands/mockup.ts
14690
- import path34 from "path";
14844
+ import path35 from "path";
14691
14845
  async function runMockup(ctx) {
14692
14846
  const { root, workspace, config } = await requireWorkspace(ctx);
14693
14847
  const slug = ctx.positionals[0];
@@ -14736,17 +14890,17 @@ async function runMockup(ctx) {
14736
14890
  ` pantallas: ${plan.screens.length}`,
14737
14891
  ...plan.screens.map((s) => ` - ${s.id}: ${s.title} (${s.illustrates.length} escenario(s))`),
14738
14892
  "",
14739
- ` plan: ${path34.relative(ctx.cwd, planFile)}`,
14740
- ` manifiesto: ${path34.relative(ctx.cwd, manifestFile)}`,
14893
+ ` plan: ${path35.relative(ctx.cwd, planFile)}`,
14894
+ ` manifiesto: ${path35.relative(ctx.cwd, manifestFile)}`,
14741
14895
  "",
14742
14896
  "Siguiente: genera el HTML de cada pantalla (fase /satlas-mockup) y valida con `satlas mockup " + slug + " --check`."
14743
14897
  ];
14744
- return { exitCode: 0, diagnostics: [], data: { plan, planFile: path34.relative(ctx.cwd, planFile), manifestFile: path34.relative(ctx.cwd, manifestFile) }, text: lines };
14898
+ return { exitCode: 0, diagnostics: [], data: { plan, planFile: path35.relative(ctx.cwd, planFile), manifestFile: path35.relative(ctx.cwd, manifestFile) }, text: lines };
14745
14899
  }
14746
14900
 
14747
14901
  // src/mcp/server.ts
14748
14902
  import { promises as fs3 } from "fs";
14749
- import path37 from "path";
14903
+ import path38 from "path";
14750
14904
  import { createInterface } from "readline/promises";
14751
14905
 
14752
14906
  // src/mcp/protocol.ts
@@ -14807,13 +14961,39 @@ function stringArg(args, key) {
14807
14961
  return typeof value === "string" && value.trim() !== "" ? value.trim() : void 0;
14808
14962
  }
14809
14963
 
14964
+ // src/mcp/tools/fixes.ts
14965
+ async function runAtlasFixes(_args, host) {
14966
+ const resolved = await host.getWorkspace();
14967
+ if (!resolved) return noWorkspaceResult();
14968
+ const fixes = await loadLivingFixes(resolved.root);
14969
+ if (fixes.length === 0) {
14970
+ const activeFix = resolved.workspace.changes.find((change) => change.meta?.lane === "fix");
14971
+ return jsonResult({
14972
+ fixes: [],
14973
+ message: "No hay fixes vivos registrados.",
14974
+ action: activeFix ? `satlas archive ${activeFix.slug}` : "satlas new <slug> --lane fix"
14975
+ });
14976
+ }
14977
+ return jsonResult({
14978
+ fixes: fixes.map((fix) => ({
14979
+ slug: fix.slug,
14980
+ date: fix.date,
14981
+ result: fix.result,
14982
+ domain: fix.domain,
14983
+ title: fix.title,
14984
+ covers: fix.covers,
14985
+ content: fix.content
14986
+ }))
14987
+ });
14988
+ }
14989
+
14810
14990
  // src/mcp/tools/glossary.ts
14811
- import path35 from "path";
14991
+ import path36 from "path";
14812
14992
  async function runAtlasGlossary(_args, host) {
14813
14993
  const resolved = await host.getWorkspace();
14814
14994
  if (!resolved) return noWorkspaceResult();
14815
14995
  const { root, config } = resolved;
14816
- const glossaryPath = path35.resolve(root, config.spec.glossary);
14996
+ const glossaryPath = path36.resolve(root, config.spec.glossary);
14817
14997
  const raw = await readTextIfExists(glossaryPath);
14818
14998
  if (raw === void 0) {
14819
14999
  return jsonResult({
@@ -14859,7 +15039,7 @@ async function runAtlasImpact(args, host) {
14859
15039
  }
14860
15040
 
14861
15041
  // src/evaluate.ts
14862
- import path36 from "path";
15042
+ import path37 from "path";
14863
15043
  function livingRequirementsMap2(specs) {
14864
15044
  const map = /* @__PURE__ */ new Map();
14865
15045
  for (const spec of specs) {
@@ -14868,7 +15048,7 @@ function livingRequirementsMap2(specs) {
14868
15048
  return map;
14869
15049
  }
14870
15050
  async function evaluateChange(workspace, config, change, approvals) {
14871
- const deltaPath = path36.join(change.dir, "spec.md");
15051
+ const deltaPath = path37.join(change.dir, "spec.md");
14872
15052
  const deltaContent = await readTextIfExists(deltaPath);
14873
15053
  const approval = verifyApproval(change, approvals, config, deltaContent ?? void 0);
14874
15054
  const living = livingRequirementsMap2(workspace.specs);
@@ -15044,7 +15224,8 @@ var HANDLERS = {
15044
15224
  atlas_validate: runAtlasValidate,
15045
15225
  atlas_trace: runAtlasTrace,
15046
15226
  atlas_impact: runAtlasImpact,
15047
- atlas_glossary: runAtlasGlossary
15227
+ atlas_glossary: runAtlasGlossary,
15228
+ atlas_fixes: runAtlasFixes
15048
15229
  };
15049
15230
  async function runToolByName(name, args, host) {
15050
15231
  const handler = HANDLERS[name];
@@ -15113,6 +15294,15 @@ function listTools() {
15113
15294
  properties: {},
15114
15295
  additionalProperties: false
15115
15296
  }
15297
+ },
15298
+ {
15299
+ name: "atlas_fixes",
15300
+ description: "Fixes vivos (carril express ya archivado): identidad, resultado de su evidencia, contenido y requisitos que declaran. Solo lectura.",
15301
+ inputSchema: {
15302
+ type: "object",
15303
+ properties: {},
15304
+ additionalProperties: false
15305
+ }
15116
15306
  }
15117
15307
  ];
15118
15308
  }
@@ -15130,8 +15320,8 @@ var WorkspaceCache = class {
15130
15320
  }
15131
15321
  };
15132
15322
  async function workspaceStamp(root) {
15133
- const sddDir = path37.join(root, ".sdd");
15134
- const markers = [sddDir, path37.join(sddDir, "config.yaml"), path37.join(sddDir, "changes"), path37.join(sddDir, "specs"), path37.join(sddDir, "approvals.yaml")];
15323
+ const sddDir = path38.join(root, ".sdd");
15324
+ const markers = [sddDir, path38.join(sddDir, "config.yaml"), path38.join(sddDir, "changes"), path38.join(sddDir, "specs"), path38.join(sddDir, "approvals.yaml")];
15135
15325
  let stamp = 0;
15136
15326
  for (const marker of markers) {
15137
15327
  try {
@@ -15149,7 +15339,7 @@ async function resolveMcpWorkspace(cwd, cache) {
15149
15339
  const cached2 = cache.get(root);
15150
15340
  if (cached2 && cached2.mtimeMs === stamp) return cached2.value;
15151
15341
  const { workspace, config } = await loadWorkspace(root);
15152
- const approvals = await loadApprovals(path37.join(root, ".sdd"));
15342
+ const approvals = await loadApprovals(path38.join(root, ".sdd"));
15153
15343
  const value = { root, workspace, config, approvals: approvals.byArtifact };
15154
15344
  cache.set(root, stamp, value);
15155
15345
  return value;
@@ -15241,7 +15431,7 @@ async function runMcp(ctx) {
15241
15431
  }
15242
15432
 
15243
15433
  // src/commands/packs.ts
15244
- import path38 from "path";
15434
+ import path39 from "path";
15245
15435
  async function runPacks(ctx) {
15246
15436
  const { root, config, workspace } = await requireWorkspace(ctx);
15247
15437
  const slug = flagString(ctx.flags, "check");
@@ -15299,14 +15489,14 @@ async function runPacks(ctx) {
15299
15489
  data: {
15300
15490
  slug,
15301
15491
  packs: evaluations.map((evaluation) => ({ id: evaluation.pack.id, status: evaluation.status, passed: evaluation.passed, failed: evaluation.failed, results: evaluation.results })),
15302
- analyzePath: path38.posix.join(".sdd", "changes", slug, "analyze.md")
15492
+ analyzePath: path39.posix.join(".sdd", "changes", slug, "analyze.md")
15303
15493
  },
15304
15494
  text: lines
15305
15495
  };
15306
15496
  }
15307
15497
 
15308
15498
  // src/commands/new.ts
15309
- import path39 from "path";
15499
+ import path40 from "path";
15310
15500
  async function runNew(ctx) {
15311
15501
  const slug = ctx.positionals[0];
15312
15502
  if (!slug) {
@@ -15332,14 +15522,14 @@ async function runNew(ctx) {
15332
15522
  msg("new.title", ctx.language),
15333
15523
  "",
15334
15524
  msg("new.done", ctx.language),
15335
- ...result.files.map((f) => ` + ${path39.relative(ctx.cwd, f)}`),
15525
+ ...result.files.map((f) => ` + ${path40.relative(ctx.cwd, f)}`),
15336
15526
  "",
15337
15527
  `Siguiente: /satlas.specify ${result.slug} \u2014 escribe la especificaci\xF3n 100% funcional y de negocio.`
15338
15528
  ];
15339
15529
  return {
15340
15530
  exitCode: hasErrors2 ? 1 : 0,
15341
15531
  diagnostics: result.diagnostics,
15342
- data: { slug: result.slug, files: result.files.map((f) => path39.relative(ctx.cwd, f)) },
15532
+ data: { slug: result.slug, files: result.files.map((f) => path40.relative(ctx.cwd, f)) },
15343
15533
  text: lines
15344
15534
  };
15345
15535
  }
@@ -15374,7 +15564,7 @@ async function runNext(ctx) {
15374
15564
  }
15375
15565
 
15376
15566
  // src/commands/present.ts
15377
- import path40 from "path";
15567
+ import path41 from "path";
15378
15568
  async function runPresentCommand(ctx) {
15379
15569
  const { root } = await requireWorkspace(ctx);
15380
15570
  const slug = ctx.positionals[0];
@@ -15386,7 +15576,7 @@ async function runPresentCommand(ctx) {
15386
15576
  if (result.path) {
15387
15577
  lines.push("Propuesta generada");
15388
15578
  lines.push("");
15389
- lines.push(` archivo: ${path40.relative(ctx.cwd, result.path)}`);
15579
+ lines.push(` archivo: ${path41.relative(ctx.cwd, result.path)}`);
15390
15580
  if (result.hash) lines.push(` hash de la spec: ${shortHash(result.hash)}`);
15391
15581
  lines.push("");
15392
15582
  lines.push("\xC1brela en el navegador y comp\xE1rtela con el stakeholder.");
@@ -15396,7 +15586,7 @@ async function runPresentCommand(ctx) {
15396
15586
  return {
15397
15587
  exitCode: hasErrors2 ? 1 : 0,
15398
15588
  diagnostics: result.diagnostics,
15399
- data: result.path ? { path: path40.relative(ctx.cwd, result.path), hash: result.hash } : void 0,
15589
+ data: result.path ? { path: path41.relative(ctx.cwd, result.path), hash: result.hash } : void 0,
15400
15590
  text: lines
15401
15591
  };
15402
15592
  }
@@ -15505,6 +15695,16 @@ async function runStatus(ctx) {
15505
15695
  for (const spec of workspace.specs) {
15506
15696
  lines.push(` ${spec.domain} \u2014 ${spec.spec.requirements.length} requisitos`);
15507
15697
  }
15698
+ const fixes = await loadLivingFixes(root);
15699
+ lines.push("");
15700
+ lines.push(`${msg("status.fixes", ctx.language)}: ${fixes.length}`);
15701
+ if (fixes.length === 0) {
15702
+ lines.push(" (se llenan al archivar un fix)");
15703
+ } else {
15704
+ for (const fix of fixes) {
15705
+ lines.push(` ${fix.date || "\u2014"} ${(fix.domain ?? "\u2014").padEnd(10)} ${fix.slug} \u2014 ${fix.result}`);
15706
+ }
15707
+ }
15508
15708
  const diagnostics = [...workspace.diagnostics, ...advisory.diagnostics];
15509
15709
  const errors = diagnostics.filter((d) => d.severity === "error").length;
15510
15710
  return {
@@ -15513,6 +15713,13 @@ async function runStatus(ctx) {
15513
15713
  data: {
15514
15714
  changes,
15515
15715
  specs: workspace.specs.map((s) => ({ domain: s.domain, requirements: s.spec.requirements.length })),
15716
+ fixes: fixes.map((fix) => ({
15717
+ slug: fix.slug,
15718
+ date: fix.date,
15719
+ result: fix.result,
15720
+ ...fix.domain !== void 0 ? { domain: fix.domain } : {},
15721
+ covers: fix.covers
15722
+ })),
15516
15723
  upgrade: {
15517
15724
  currentVersion: advisory.plan.currentVersion,
15518
15725
  pending: advisory.plan.pending.length,
@@ -15525,7 +15732,7 @@ async function runStatus(ctx) {
15525
15732
  }
15526
15733
 
15527
15734
  // src/commands/trace.ts
15528
- import path41 from "path";
15735
+ import path42 from "path";
15529
15736
  async function runTrace(ctx) {
15530
15737
  const { workspace, config } = await requireWorkspace(ctx);
15531
15738
  const slug = flagString(ctx.flags, "change");
@@ -15545,7 +15752,7 @@ async function runTrace(ctx) {
15545
15752
  data.push({ slug: change.slug, nodes: result.graph.nodes.length, edges: result.graph.edges.length, summary: result.summary, findings: result.findings });
15546
15753
  lines.push(` ${change.slug}: ${result.graph.nodes.length} nodos, ${result.graph.edges.length} aristas, ${errors} errores, ${result.summary.warnings} avisos`);
15547
15754
  for (const finding of result.findings) {
15548
- const location = finding.path ? ` (${path41.relative(ctx.cwd, finding.path)}${finding.line ? `:${finding.line}` : ""})` : "";
15755
+ const location = finding.path ? ` (${path42.relative(ctx.cwd, finding.path)}${finding.line ? `:${finding.line}` : ""})` : "";
15549
15756
  lines.push(` ${finding.severity.toUpperCase()} ${finding.code} \u2014 ${finding.message}${location}`);
15550
15757
  }
15551
15758
  }
@@ -15555,7 +15762,7 @@ async function runTrace(ctx) {
15555
15762
  }
15556
15763
 
15557
15764
  // src/commands/upgrade.ts
15558
- import path42 from "path";
15765
+ import path43 from "path";
15559
15766
  async function runUpgrade(ctx) {
15560
15767
  const { root } = await requireWorkspace(ctx);
15561
15768
  const apply = flagBool(ctx.flags, "apply");
@@ -15609,7 +15816,7 @@ function renderPreview(plan, root, ctx) {
15609
15816
  }
15610
15817
  for (const item of plan.unreadable) {
15611
15818
  lines.push(` Ilegible: "${item.artifact}" \u2014 ${item.reason}`);
15612
- lines.push(` \u21B3 ${path42.relative(ctx.cwd, item.path)}`);
15819
+ lines.push(` \u21B3 ${path43.relative(ctx.cwd, item.path)}`);
15613
15820
  }
15614
15821
  return { exitCode: 0, diagnostics: [], data: summarizable(plan), text: lines };
15615
15822
  }
@@ -15672,7 +15879,7 @@ function renderRollback(report, ctx) {
15672
15879
  }
15673
15880
 
15674
15881
  // src/commands/validate.ts
15675
- import path43 from "path";
15882
+ import path44 from "path";
15676
15883
  async function runValidate(ctx) {
15677
15884
  const { root, workspace, config, approvals } = await requireWorkspace(ctx);
15678
15885
  const slug = flagString(ctx.flags, "change");
@@ -15699,7 +15906,7 @@ async function runValidate(ctx) {
15699
15906
  for (const finding of evaluation.lintFindings) {
15700
15907
  lines.push(` ${finding.severity.toUpperCase()} ${finding.code}${finding.line ? `:${finding.line}` : ""} \u2014 ${finding.message}`);
15701
15908
  }
15702
- lines.push(` archivo: ${path43.relative(ctx.cwd, path43.join(change.dir, "spec.md"))}`);
15909
+ lines.push(` archivo: ${path44.relative(ctx.cwd, path44.join(change.dir, "spec.md"))}`);
15703
15910
  }
15704
15911
  if (targets.length === 0) lines.push("Sin cambios activos.");
15705
15912
  if (advisory && advisory.plan.pending.length > 0) {
@@ -15727,7 +15934,7 @@ async function runValidate(ctx) {
15727
15934
  }
15728
15935
 
15729
15936
  // src/commands/verify.ts
15730
- import path44 from "path";
15937
+ import path45 from "path";
15731
15938
  async function runVerify(ctx) {
15732
15939
  const { root, workspace } = await requireWorkspace(ctx);
15733
15940
  const slug = ctx.positionals[0];
@@ -15770,7 +15977,7 @@ async function runVerify(ctx) {
15770
15977
  return { exitCode: 2, diagnostics: [{ code: "ATLAS-VERIFY-001", severity: "error", message: "Falta --by <nombre> (o define meta.owner)" }] };
15771
15978
  }
15772
15979
  const profilesDir = await resolveProfilesDir();
15773
- const profile = await loadActiveProfile(path44.join(root, ".sdd"), [profilesDir ?? "", path44.join(root, ".sdd", "profiles", "custom")].filter(Boolean));
15980
+ const profile = await loadActiveProfile(path45.join(root, ".sdd"), [profilesDir ?? "", path45.join(root, ".sdd", "profiles", "custom")].filter(Boolean));
15774
15981
  const record = await recordEvidence({
15775
15982
  root,
15776
15983
  slug,
@@ -15792,7 +15999,7 @@ async function runVerify(ctx) {
15792
15999
  if (record.evidence.command) lines.push(` comando: ${record.evidence.command}`);
15793
16000
  lines.push(` resultado: ${record.evidence.result}`);
15794
16001
  if (record.evidence.outputHash) lines.push(` hash: ${record.evidence.outputHash}`);
15795
- lines.push(` archivo: ${path44.relative(ctx.cwd, record.path)}`);
16002
+ lines.push(` archivo: ${path45.relative(ctx.cwd, record.path)}`);
15796
16003
  if (record.stdout) {
15797
16004
  lines.push("");
15798
16005
  lines.push(" salida (\xFAltimas l\xEDneas):");
@@ -15807,7 +16014,7 @@ async function runVerify(ctx) {
15807
16014
  return {
15808
16015
  exitCode: record.exitCode === 0 ? 0 : 1,
15809
16016
  diagnostics: record.diagnostics,
15810
- data: { evidence: record.evidence, path: path44.relative(ctx.cwd, record.path), exitCode: record.exitCode },
16017
+ data: { evidence: record.evidence, path: path45.relative(ctx.cwd, record.path), exitCode: record.exitCode },
15811
16018
  text: lines
15812
16019
  };
15813
16020
  }
@@ -15907,7 +16114,7 @@ async function requireWorkspace(ctx) {
15907
16114
  throw new CliError("ATLAS-WS-001", msg("cli.noWorkspace", ctx.language), 2);
15908
16115
  }
15909
16116
  const { workspace, config } = await loadWorkspace(root);
15910
- const approvals = await loadApprovals(path45.join(root, ".sdd"));
16117
+ const approvals = await loadApprovals(path46.join(root, ".sdd"));
15911
16118
  return { root, workspace, config, approvals: approvals.byArtifact };
15912
16119
  }
15913
16120
  var CliError = class extends Error {
@@ -15974,7 +16181,7 @@ function printResult(command, result, json2, language) {
15974
16181
  }
15975
16182
  const lines = [...result.text ?? []];
15976
16183
  for (const d of result.diagnostics) {
15977
- const location = d.path ? ` ${path45.relative(process.cwd(), d.path)}${d.line ? `:${d.line}` : ""}` : "";
16184
+ const location = d.path ? ` ${path46.relative(process.cwd(), d.path)}${d.line ? `:${d.line}` : ""}` : "";
15978
16185
  lines.push(`${d.severity === "error" ? "ERROR" : d.severity === "warning" ? "AVISO" : "NOTA"} ${d.code}${location} \u2014 ${d.message}`);
15979
16186
  if (d.suggestion) lines.push(` \u21B3 ${d.suggestion}`);
15980
16187
  }