specatlas 0.1.27 → 0.1.29

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 +517 -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,142 @@ 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
+ source: "living"
10448
+ };
10449
+ if (typeof data["domain"] === "string") fix.domain = data["domain"];
10450
+ if (typeof data["title"] === "string") fix.title = data["title"];
10451
+ return fix;
10452
+ }
10453
+ async function archivedFixes(root, known) {
10454
+ const archiveDir = path4.join(root, ".sdd", "changes", "archive");
10455
+ const fixes = [];
10456
+ for (const entry of await listDirs(archiveDir)) {
10457
+ const dir = path4.join(archiveDir, entry);
10458
+ const fixFile = path4.join(dir, "fix.md");
10459
+ const fixRaw = await readTextIfExists(fixFile);
10460
+ if (fixRaw === void 0) continue;
10461
+ const metaFile = path4.join(dir, "meta.yaml");
10462
+ const metaRaw = await readTextIfExists(metaFile);
10463
+ const meta = metaRaw !== void 0 ? parseChangeMeta(metaRaw, metaFile).meta : void 0;
10464
+ if (meta?.lane !== "fix") continue;
10465
+ const slug = entry.replace(/^\d{4}-\d{2}-/, "");
10466
+ if (known.has(slug)) continue;
10467
+ const evidence = parseVerifyFile(fixRaw, fixFile).evidence;
10468
+ const fix = {
10469
+ slug,
10470
+ file: fixFile,
10471
+ date: /^(\d{4}-\d{2})/.exec(entry)?.[1] ?? "",
10472
+ result: evidence.some((e) => e.result === "pass") ? "pass" : evidence.length > 0 ? "fail" : "pending",
10473
+ covers: parseFixCovers(fixRaw),
10474
+ content: parseFrontmatter(fixRaw, fixFile).body.trimStart(),
10475
+ source: "archive"
10476
+ };
10477
+ if (meta.domain !== void 0) fix.domain = meta.domain;
10478
+ if (meta.title !== void 0) fix.title = meta.title;
10479
+ fixes.push(fix);
10480
+ }
10481
+ return fixes;
10482
+ }
10483
+ async function loadLivingFixes(root) {
10484
+ const dir = path4.join(root, LIVING_FIXES_DIR);
10485
+ const entries = (await listDir(dir)).filter((entry) => entry.toLowerCase().endsWith(".md")).sort();
10486
+ const fixes = [];
10487
+ for (const entry of entries) {
10488
+ const file = path4.join(dir, entry);
10489
+ const content = await readTextIfExists(file);
10490
+ if (content === void 0) continue;
10491
+ fixes.push(parseLivingFix(content, file));
10492
+ }
10493
+ fixes.push(...await archivedFixes(root, new Set(fixes.map((fix) => fix.slug))));
10494
+ return fixes.sort((a, b) => b.date.localeCompare(a.date) || b.slug.localeCompare(a.slug));
10495
+ }
10496
+ async function writeLivingFix(root, input) {
10497
+ const dir = path4.join(root, LIVING_FIXES_DIR);
10498
+ const month = input.date.slice(0, 7);
10499
+ const file = path4.join(dir, `${month}-${input.slug}.md`);
10500
+ const relativePath = toPosix(path4.relative(root, file));
10501
+ if (await exists(file)) return { file, relativePath, created: false };
10502
+ const data = {
10503
+ slug: input.slug,
10504
+ date: input.date,
10505
+ result: input.result ?? "pass"
10506
+ };
10507
+ if (input.domain) data["domain"] = input.domain;
10508
+ if (input.title) data["title"] = input.title;
10509
+ if (input.covers && input.covers.length > 0) data["covers"] = input.covers;
10510
+ const body = input.content.replace(/^[\r\n]+/, "").trimEnd();
10511
+ const text = `---
10512
+ ${stringifyYaml3(data, { lineWidth: 120 })}---
10513
+
10514
+ ${body}
10515
+ `;
10516
+ await writeText(file, text);
10517
+ return { file, relativePath, created: true };
10518
+ }
10399
10519
  var SDD_DIR = ".sdd";
10400
10520
  async function findWorkspaceRoot(start) {
10401
- let current = path4.resolve(start);
10521
+ let current = path5.resolve(start);
10402
10522
  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);
10523
+ if (await isDirectory(path5.join(current, SDD_DIR))) return current;
10524
+ const parent = path5.dirname(current);
10405
10525
  if (parent === current) return void 0;
10406
10526
  current = parent;
10407
10527
  }
10408
10528
  return void 0;
10409
10529
  }
10410
10530
  async function loadApprovals(sddDir) {
10411
- const file = path4.join(sddDir, "approvals.yaml");
10531
+ const file = path5.join(sddDir, "approvals.yaml");
10412
10532
  const raw = await readTextIfExists(file);
10413
10533
  if (raw === void 0) return { byArtifact: /* @__PURE__ */ new Map(), diagnostics: [] };
10414
10534
  const parsed = parseApprovals(raw, file);
10415
10535
  const map = /* @__PURE__ */ new Map();
10416
10536
  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 });
10537
+ map.set(a.artifact.split(path5.sep).join("/"), { hash: a.artifactHash, by: a.approvedBy, at: a.approvedAt });
10418
10538
  }
10419
10539
  return { byArtifact: map, diagnostics: parsed.diagnostics };
10420
10540
  }
10421
10541
  async function loadSpecs(sddDir) {
10422
- const specsDir = path4.join(sddDir, "specs");
10542
+ const specsDir = path5.join(sddDir, "specs");
10423
10543
  const domains = await listDirs(specsDir);
10424
10544
  const out = [];
10425
10545
  for (const domain of domains) {
10426
- const file = path4.join(specsDir, domain, "spec.md");
10546
+ const file = path5.join(specsDir, domain, "spec.md");
10427
10547
  if (!await exists(file)) continue;
10428
10548
  const content = await readText(file);
10429
10549
  out.push({ domain, path: file, spec: parseSpecFile(content, file) });
@@ -10431,10 +10551,10 @@ async function loadSpecs(sddDir) {
10431
10551
  return out;
10432
10552
  }
10433
10553
  async function loadChange(root, slug, relDir) {
10434
- const dir = path4.join(root, SDD_DIR, "changes", relDir ?? slug);
10554
+ const dir = path5.join(root, SDD_DIR, "changes", relDir ?? slug);
10435
10555
  const diagnostics = [];
10436
10556
  const change = { slug, dir, diagnostics };
10437
- const metaFile = path4.join(dir, "meta.yaml");
10557
+ const metaFile = path5.join(dir, "meta.yaml");
10438
10558
  const metaRaw = await readTextIfExists(metaFile);
10439
10559
  if (metaRaw === void 0) {
10440
10560
  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 +10563,7 @@ async function loadChange(root, slug, relDir) {
10443
10563
  diagnostics.push(...parsed.diagnostics);
10444
10564
  if (parsed.meta) change.meta = parsed.meta;
10445
10565
  }
10446
- const deltaFile = path4.join(dir, "spec.md");
10566
+ const deltaFile = path5.join(dir, "spec.md");
10447
10567
  const deltaRaw = await readTextIfExists(deltaFile);
10448
10568
  if (deltaRaw !== void 0) {
10449
10569
  change.delta = parseDelta(deltaRaw, deltaFile);
@@ -10451,41 +10571,42 @@ async function loadChange(root, slug, relDir) {
10451
10571
  } else {
10452
10572
  diagnostics.push(diag("ATLAS-FILES-002", "warning", `El cambio "${slug}" no tiene spec.md (delta)`, { path: deltaFile }));
10453
10573
  }
10454
- const planFile = path4.join(dir, "plan.md");
10574
+ const planFile = path5.join(dir, "plan.md");
10455
10575
  if (await exists(planFile)) change.planPath = planFile;
10456
- const reviewFile = path4.join(dir, "review.md");
10576
+ const reviewFile = path5.join(dir, "review.md");
10457
10577
  if (await exists(reviewFile)) change.reviewPath = reviewFile;
10458
- const presentationFile = path4.join(dir, "presentation", "index.html");
10578
+ const presentationFile = path5.join(dir, "presentation", "index.html");
10459
10579
  if (await exists(presentationFile)) change.presentationPath = presentationFile;
10460
- const tasksFile = path4.join(dir, "tasks.md");
10580
+ const tasksFile = path5.join(dir, "tasks.md");
10461
10581
  const tasksRaw = await readTextIfExists(tasksFile);
10462
10582
  if (tasksRaw !== void 0) {
10463
10583
  change.tasks = parseTasksFile(tasksRaw, tasksFile);
10464
10584
  diagnostics.push(...change.tasks.diagnostics);
10465
10585
  }
10466
- const verifyFile = path4.join(dir, "verify.md");
10586
+ const verifyFile = path5.join(dir, "verify.md");
10467
10587
  const verifyRaw = await readTextIfExists(verifyFile);
10468
10588
  if (verifyRaw !== void 0) {
10469
10589
  change.verify = parseVerifyFile(verifyRaw, verifyFile);
10470
10590
  diagnostics.push(...change.verify.diagnostics);
10471
10591
  }
10472
- const fixFile = path4.join(dir, "fix.md");
10592
+ const fixFile = path5.join(dir, "fix.md");
10473
10593
  const fixRaw = await readTextIfExists(fixFile);
10474
10594
  if (fixRaw !== void 0) {
10475
10595
  change.fix = parseVerifyFile(fixRaw, fixFile);
10476
10596
  diagnostics.push(...change.fix.diagnostics);
10597
+ change.fixCovers = parseFixCovers(fixRaw);
10477
10598
  }
10478
- const mockupManifest = path4.join(dir, "mockups", "manifest.yaml");
10599
+ const mockupManifest = path5.join(dir, "mockups", "manifest.yaml");
10479
10600
  if (await exists(mockupManifest)) change.mockupManifestPath = mockupManifest;
10480
10601
  return change;
10481
10602
  }
10482
10603
  async function listChangeSlugs(root, opts = {}) {
10483
- const changesDir = path4.join(root, SDD_DIR, "changes");
10604
+ const changesDir = path5.join(root, SDD_DIR, "changes");
10484
10605
  const dirs = await listDirs(changesDir);
10485
10606
  return dirs.filter((d) => opts.includeArchived ? true : d !== "archive");
10486
10607
  }
10487
10608
  async function loadWorkspace(root) {
10488
- const sddDir = path4.join(root, SDD_DIR);
10609
+ const sddDir = path5.join(root, SDD_DIR);
10489
10610
  const { config, diagnostics: configDiags } = await loadConfig(sddDir);
10490
10611
  const diagnostics = [...configDiags];
10491
10612
  const specs = await loadSpecs(sddDir);
@@ -10498,20 +10619,20 @@ async function loadWorkspace(root) {
10498
10619
  diagnostics.push(...change.diagnostics);
10499
10620
  }
10500
10621
  const archived = [];
10501
- const archivedDir = path4.join(sddDir, "changes", "archive");
10622
+ const archivedDir = path5.join(sddDir, "changes", "archive");
10502
10623
  if (await isDirectory(archivedDir)) {
10503
10624
  for (const entry of await listDirs(archivedDir)) {
10504
- archived.push(await loadChange(root, entry.replace(/^\d{4}-\d{2}-/, ""), path4.join("archive", entry)));
10625
+ archived.push(await loadChange(root, entry.replace(/^\d{4}-\d{2}-/, ""), path5.join("archive", entry)));
10505
10626
  }
10506
10627
  }
10507
10628
  return { workspace: { root, sddDir, specs, changes, archived, diagnostics }, config };
10508
10629
  }
10509
10630
  async function ensureSddDirs(sddDir) {
10510
10631
  const created = [];
10511
- const dirs = ["specs", "changes", "runs", "metrics", path4.join("profiles", "custom")];
10632
+ const dirs = ["specs", "changes", "runs", "metrics", "fixes", path5.join("profiles", "custom")];
10512
10633
  const { ensureDir: ensureDir2 } = await import("./fsx-VF2P7ALA-XKEWTMDH.js");
10513
10634
  for (const d of dirs) {
10514
- const full = path4.join(sddDir, d);
10635
+ const full = path5.join(sddDir, d);
10515
10636
  if (!await exists(full)) {
10516
10637
  await ensureDir2(full);
10517
10638
  created.push(full);
@@ -10679,6 +10800,10 @@ notes: 12/12 casos
10679
10800
 
10680
10801
  ## Evidencia
10681
10802
 
10803
+ <!-- Opcional: declara los requisitos que este fix afecta (aparecen en su trazabilidad).
10804
+ Cubre: REQ-DOMINIO-001
10805
+ -->
10806
+
10682
10807
  <!-- Registra la evidencia real con:
10683
10808
  satlas verify <slug> --file fix --scenario REQ-DOMINIO-001-S1 --command "<comando>" --by "<nombre>"
10684
10809
  o, si es manual:
@@ -10802,6 +10927,10 @@ by: your-name
10802
10927
  ## Rollback
10803
10928
  ## Evidence
10804
10929
 
10930
+ <!-- Optional: declare the requirements this fix affects (they show up in its traceability).
10931
+ Cubre: REQ-DOMAIN-001
10932
+ -->
10933
+
10805
10934
  <!-- Register real evidence with:
10806
10935
  satlas verify <slug> --file fix --scenario REQ-DOMAIN-001-S1 --command "<command>" --by "<name>"
10807
10936
  -->
@@ -10826,7 +10955,7 @@ function changeMetaYaml(meta) {
10826
10955
  if (meta.owner) doc["owner"] = meta.owner;
10827
10956
  if (meta.mockups) doc["mockups"] = meta.mockups;
10828
10957
  return `# Estado del cambio. La fase se DERIVA de los artefactos; aqu\xED solo hechos.
10829
- ` + stringifyYaml3(doc, { lineWidth: 120 });
10958
+ ` + stringifyYaml4(doc, { lineWidth: 120 });
10830
10959
  }
10831
10960
  function indexMarkdown(input) {
10832
10961
  const es = input.language !== "en";
@@ -10840,6 +10969,15 @@ function indexMarkdown(input) {
10840
10969
  if (input.specs.length === 0) lines.push(es ? "_Sin specs todav\xEDa._" : "_No specs yet._");
10841
10970
  for (const spec of input.specs) lines.push(`- \`${spec.domain}\` \u2014 ${spec.requirements} ${es ? "requisitos" : "requirements"}`);
10842
10971
  lines.push("");
10972
+ lines.push(es ? "## Fixes vivos" : "## Living fixes");
10973
+ lines.push("");
10974
+ const fixes = input.fixes ?? [];
10975
+ if (fixes.length === 0) lines.push(es ? "_Sin fixes archivados todav\xEDa._" : "_No archived fixes yet._");
10976
+ for (const fix of fixes) {
10977
+ const parts = [fix.date, fix.domain ? `\`${fix.domain}\`` : "", `\`${fix.slug}\``, fix.result].filter((part) => part !== "");
10978
+ lines.push(`- ${parts.join(" \xB7 ")}`);
10979
+ }
10980
+ lines.push("");
10843
10981
  lines.push(es ? "## Cambios activos" : "## Active changes");
10844
10982
  lines.push("");
10845
10983
  if (input.changes.length === 0) lines.push(es ? "_Sin cambios activos._" : "_No active changes._");
@@ -10991,8 +11129,8 @@ function issueBody(input) {
10991
11129
  lines.push(`- Siguiente: \`${input.next}\` \u2014 ${input.nextDescription}`);
10992
11130
  lines.push("");
10993
11131
  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")}\``);
11132
+ lines.push(`- Spec: \`${path6.posix.join(".sdd", "changes", change.slug, "spec.md")}\``);
11133
+ if (change.planPath) lines.push(`- Plan: \`${path6.posix.join(".sdd", "changes", change.slug, "plan.md")}\``);
10996
11134
  if (change.tasks) lines.push(`- Tareas: ${change.tasks.counts.done}/${change.tasks.counts.total}`);
10997
11135
  if (change.verify) lines.push(`- Evidencia registrada: ${change.verify.evidence.length}`);
10998
11136
  lines.push("");
@@ -11056,10 +11194,10 @@ async function issueLabels(root, number, runner = defaultRunner) {
11056
11194
  }
11057
11195
  }
11058
11196
  async function patchChangeMeta(root, slug, patch) {
11059
- const file = path5.join(path5.resolve(root), ".sdd", "changes", slug, "meta.yaml");
11197
+ const file = path6.join(path6.resolve(root), ".sdd", "changes", slug, "meta.yaml");
11060
11198
  const raw = await readTextIfExists(file);
11061
11199
  if (raw === void 0) {
11062
- return { path: file, diagnostics: [diag("ATLAS-GH-007", "error", `No existe ${path5.relative(root, file)}`)] };
11200
+ return { path: file, diagnostics: [diag("ATLAS-GH-007", "error", `No existe ${path6.relative(root, file)}`)] };
11063
11201
  }
11064
11202
  let data;
11065
11203
  try {
@@ -11113,7 +11251,7 @@ async function approveFromGithub(opts) {
11113
11251
  const { signApproval: signApproval2 } = await import("./approvals-2PO6223I-DENKLEXU.js");
11114
11252
  const signed = await signApproval2({
11115
11253
  root: opts.root,
11116
- artifact: path5.posix.join("changes", opts.slug, "spec.md"),
11254
+ artifact: path6.posix.join("changes", opts.slug, "spec.md"),
11117
11255
  by,
11118
11256
  channel: "tracker",
11119
11257
  note: `Aprobado v\xEDa GitHub issue #${number} (etiqueta "${label}")`
@@ -11208,10 +11346,10 @@ function buildEvidenceYaml(evidence) {
11208
11346
  return lines.join("\n");
11209
11347
  }
11210
11348
  async function recordEvidence(opts) {
11211
- const root = path6.resolve(opts.root);
11349
+ const root = path7.resolve(opts.root);
11212
11350
  const slug = opts.slug;
11213
11351
  const fileKind = opts.file ?? "verify";
11214
- const file = path6.join(root, ".sdd", "changes", slug, fileKind === "fix" ? "fix.md" : "verify.md");
11352
+ const file = path7.join(root, ".sdd", "changes", slug, fileKind === "fix" ? "fix.md" : "verify.md");
11215
11353
  const diagnostics = [];
11216
11354
  const scenario = opts.scenario.toUpperCase();
11217
11355
  if (!SCENARIO_ID_RE.test(scenario)) {
@@ -11301,7 +11439,7 @@ function generateRunId(now = /* @__PURE__ */ new Date()) {
11301
11439
  return `${iso.slice(0, 8)}-${iso.slice(8, 14)}-${randomBytes(3).toString("hex")}`;
11302
11440
  }
11303
11441
  function runsDir(root) {
11304
- return path7.join(path7.resolve(root), ".sdd", "runs");
11442
+ return path8.join(path8.resolve(root), ".sdd", "runs");
11305
11443
  }
11306
11444
  async function createRun(root, slug, phase, inputs, now = /* @__PURE__ */ new Date()) {
11307
11445
  const runId = generateRunId(now);
@@ -11314,25 +11452,25 @@ async function createRun(root, slug, phase, inputs, now = /* @__PURE__ */ new Da
11314
11452
  updatedAt: localStamp(now)
11315
11453
  };
11316
11454
  if (inputs) state.inputs = inputs;
11317
- const dir = path7.join(runsDir(root), runId);
11455
+ const dir = path8.join(runsDir(root), runId);
11318
11456
  await ensureDir(dir);
11319
- await writeText(path7.join(dir, "state.json"), `${JSON.stringify(state, null, 2)}
11457
+ await writeText(path8.join(dir, "state.json"), `${JSON.stringify(state, null, 2)}
11320
11458
  `);
11321
- await writeText(path7.join(dir, "events.jsonl"), "");
11459
+ await writeText(path8.join(dir, "events.jsonl"), "");
11322
11460
  await appendRunEvent(root, runId, "run_started", { slug, phase });
11323
11461
  return state;
11324
11462
  }
11325
11463
  async function appendRunEvent(root, runId, type, data, now = /* @__PURE__ */ new Date()) {
11326
11464
  const event = { eventId: randomBytes(4).toString("hex"), at: localStamp(now), type };
11327
11465
  if (data) event.data = data;
11328
- const file = path7.join(runsDir(root), runId, "events.jsonl");
11466
+ const file = path8.join(runsDir(root), runId, "events.jsonl");
11329
11467
  const previous = await readTextIfExists(file) ?? "";
11330
11468
  await writeText(file, `${previous}${JSON.stringify(event)}
11331
11469
  `);
11332
11470
  return event;
11333
11471
  }
11334
11472
  async function updateRunStatus(root, runId, status, now = /* @__PURE__ */ new Date()) {
11335
- const file = path7.join(runsDir(root), runId, "state.json");
11473
+ const file = path8.join(runsDir(root), runId, "state.json");
11336
11474
  const raw = await readTextIfExists(file);
11337
11475
  if (raw === void 0) return;
11338
11476
  const state = JSON.parse(raw);
@@ -11342,13 +11480,13 @@ async function updateRunStatus(root, runId, status, now = /* @__PURE__ */ new Da
11342
11480
  `);
11343
11481
  }
11344
11482
  async function readRun(root, runId) {
11345
- const dir = path7.join(runsDir(root), runId);
11346
- const stateRaw = await readTextIfExists(path7.join(dir, "state.json"));
11483
+ const dir = path8.join(runsDir(root), runId);
11484
+ const stateRaw = await readTextIfExists(path8.join(dir, "state.json"));
11347
11485
  if (stateRaw === void 0) {
11348
11486
  return { diagnostics: [diag("ATLAS-RUN-001", "error", `No existe el run ${runId}`, { path: dir })] };
11349
11487
  }
11350
11488
  const state = JSON.parse(stateRaw);
11351
- const eventsRaw = await readTextIfExists(path7.join(dir, "events.jsonl")) ?? "";
11489
+ const eventsRaw = await readTextIfExists(path8.join(dir, "events.jsonl")) ?? "";
11352
11490
  const events = [];
11353
11491
  for (const line of eventsRaw.split("\n")) {
11354
11492
  if (line.trim() === "") continue;
@@ -11365,7 +11503,7 @@ async function listRuns(root, filter = {}) {
11365
11503
  const ids = await listDirs(dir);
11366
11504
  const out = [];
11367
11505
  for (const id of ids) {
11368
- const raw = await readTextIfExists(path7.join(dir, id, "state.json"));
11506
+ const raw = await readTextIfExists(path8.join(dir, id, "state.json"));
11369
11507
  if (raw === void 0) continue;
11370
11508
  try {
11371
11509
  const state = JSON.parse(raw);
@@ -11400,20 +11538,20 @@ var mockupManifestSchema = z4.object({
11400
11538
  });
11401
11539
  var TOKEN_CANDIDATES = ["design/tokens.json", "DESIGN.md", "design/DESIGN.md", ".sdd/design/tokens.json"];
11402
11540
  function mockupsDir(root, slug) {
11403
- return path8.join(path8.resolve(root), ".sdd", "changes", slug, "mockups");
11541
+ return path9.join(path9.resolve(root), ".sdd", "changes", slug, "mockups");
11404
11542
  }
11405
11543
  async function tokensFile(root) {
11406
11544
  for (const candidate of TOKEN_CANDIDATES) {
11407
- const abs = path8.join(root, candidate);
11545
+ const abs = path9.join(root, candidate);
11408
11546
  if (await exists(abs)) return candidate;
11409
11547
  }
11410
11548
  return void 0;
11411
11549
  }
11412
11550
  async function computeInputsHash(root, change) {
11413
- const deltaPath = path8.join(change.dir, "spec.md");
11551
+ const deltaPath = path9.join(change.dir, "spec.md");
11414
11552
  const delta = await readTextIfExists(deltaPath) ?? "";
11415
11553
  const tokens = await tokensFile(root);
11416
- const tokensContent = tokens ? await readTextIfExists(path8.join(root, tokens)) ?? "" : "";
11554
+ const tokensContent = tokens ? await readTextIfExists(path9.join(root, tokens)) ?? "" : "";
11417
11555
  return artifactHash(`${delta}
11418
11556
  ---tokens---
11419
11557
  ${tokensContent}`);
@@ -11438,7 +11576,7 @@ function planMockups(change, platform = "web") {
11438
11576
  return { platform, screens };
11439
11577
  }
11440
11578
  async function readMockupManifest(root, slug) {
11441
- const file = path8.join(mockupsDir(root, slug), "manifest.yaml");
11579
+ const file = path9.join(mockupsDir(root, slug), "manifest.yaml");
11442
11580
  const raw = await readTextIfExists(file);
11443
11581
  if (raw === void 0) return { diagnostics: [] };
11444
11582
  let data;
@@ -11530,7 +11668,7 @@ async function checkMockups(root, slug, change) {
11530
11668
  const { manifest, path: manifestPath, diagnostics } = await readMockupManifest(root, slug);
11531
11669
  const findings = [...diagnostics];
11532
11670
  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 };
11671
+ return { findings: [...findings, diag("LINT-MKP-004", "error", "No existe mockups/manifest.yaml", { path: path9.join(dir, "manifest.yaml") })], stale: false };
11534
11672
  }
11535
11673
  const known = /* @__PURE__ */ new Set();
11536
11674
  for (const req of [...change.delta?.added ?? [], ...change.delta?.modified ?? []]) {
@@ -11538,12 +11676,12 @@ async function checkMockups(root, slug, change) {
11538
11676
  }
11539
11677
  findings.push(...lintMockupManifest(manifest, known, manifestPath));
11540
11678
  for (const screen of manifest.screens) {
11541
- const html = await readTextIfExists(path8.join(dir, screen.file));
11679
+ const html = await readTextIfExists(path9.join(dir, screen.file));
11542
11680
  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) }));
11681
+ findings.push(diag("ATLAS-MKP-004", "error", `Falta el archivo del mockup: ${screen.file}`, { path: path9.join(dir, screen.file) }));
11544
11682
  continue;
11545
11683
  }
11546
- findings.push(...lintMockupHtml(html, path8.join(dir, screen.file)));
11684
+ findings.push(...lintMockupHtml(html, path9.join(dir, screen.file)));
11547
11685
  }
11548
11686
  const inputsHash = await computeInputsHash(root, change);
11549
11687
  const stale = manifest.inputsHash !== void 0 && manifest.inputsHash !== inputsHash;
@@ -11558,7 +11696,7 @@ async function checkMockups(root, slug, change) {
11558
11696
  return { findings, stale, manifest, manifestPath };
11559
11697
  }
11560
11698
  async function writeMockupPlan(root, slug, plan, now = /* @__PURE__ */ new Date()) {
11561
- const file = path8.join(mockupsDir(root, slug), "plan.yaml");
11699
+ const file = path9.join(mockupsDir(root, slug), "plan.yaml");
11562
11700
  if (await exists(file)) return file;
11563
11701
  const doc = {
11564
11702
  schema_version: 1,
@@ -11566,11 +11704,11 @@ async function writeMockupPlan(root, slug, plan, now = /* @__PURE__ */ new Date(
11566
11704
  platform: plan.platform,
11567
11705
  screens: plan.screens.map((s) => ({ id: s.id, title: s.title, file: s.file, illustrates: s.illustrates, states: s.states, breakpoints: s.breakpoints }))
11568
11706
  };
11569
- await writeText(file, stringifyYaml4(doc, { lineWidth: 120 }));
11707
+ await writeText(file, stringifyYaml5(doc, { lineWidth: 120 }));
11570
11708
  return file;
11571
11709
  }
11572
11710
  async function writeMockupManifest(root, slug, plan, inputsHash, now = /* @__PURE__ */ new Date()) {
11573
- const file = path8.join(mockupsDir(root, slug), "manifest.yaml");
11711
+ const file = path9.join(mockupsDir(root, slug), "manifest.yaml");
11574
11712
  if (await exists(file)) return file;
11575
11713
  const doc = {
11576
11714
  schema_version: 1,
@@ -11582,16 +11720,16 @@ async function writeMockupManifest(root, slug, plan, inputsHash, now = /* @__PUR
11582
11720
  screens: plan.screens.map((s) => ({ id: s.id, title: s.title, file: s.file, illustrates: s.illustrates, states: s.states, breakpoints: s.breakpoints })),
11583
11721
  screenshots: []
11584
11722
  };
11585
- await writeText(file, stringifyYaml4(doc, { lineWidth: 120 }));
11723
+ await writeText(file, stringifyYaml5(doc, { lineWidth: 120 }));
11586
11724
  return file;
11587
11725
  }
11588
11726
  async function updateMockupScreenshots(root, slug, screenshots) {
11589
- const file = path8.join(mockupsDir(root, slug), "manifest.yaml");
11727
+ const file = path9.join(mockupsDir(root, slug), "manifest.yaml");
11590
11728
  const raw = await readTextIfExists(file);
11591
11729
  if (raw === void 0) return void 0;
11592
11730
  const data = parseYaml6(raw) ?? {};
11593
11731
  data["screenshots"] = screenshots;
11594
- await writeText(file, stringifyYaml4(data, { lineWidth: 120 }));
11732
+ await writeText(file, stringifyYaml5(data, { lineWidth: 120 }));
11595
11733
  return file;
11596
11734
  }
11597
11735
  async function captureMockups(root, slug, manifest, now = /* @__PURE__ */ new Date()) {
@@ -11616,9 +11754,9 @@ async function captureMockups(root, slug, manifest, now = /* @__PURE__ */ new Da
11616
11754
  const page2 = await browser.newPage();
11617
11755
  for (const bp of screen.breakpoints && screen.breakpoints.length > 0 ? screen.breakpoints : [1440]) {
11618
11756
  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) });
11757
+ await page2.goto(`file://${path9.join(dir, screen.file).split(path9.sep).join("/")}`);
11758
+ const rel = path9.posix.join("screens", `${screen.id}-${bp}.png`);
11759
+ await page2.screenshot({ path: path9.join(dir, rel) });
11622
11760
  screenshots.push(rel);
11623
11761
  }
11624
11762
  await page2.close();
@@ -11634,7 +11772,7 @@ async function mockupsReady(root, slug, change) {
11634
11772
  return Boolean(check.manifest && check.manifest.screens.length > 0 && !check.stale);
11635
11773
  }
11636
11774
  async function setMockupRequirement(root, slug, value) {
11637
- const file = path8.join(root, ".sdd", "changes", slug, "meta.yaml");
11775
+ const file = path9.join(root, ".sdd", "changes", slug, "meta.yaml");
11638
11776
  const raw = await readTextIfExists(file) ?? `schema_version: 1
11639
11777
  slug: ${slug}
11640
11778
  lane: standard
@@ -11864,12 +12002,12 @@ ${texts.scenarios}`);
11864
12002
  }
11865
12003
  }
11866
12004
  async function loadProjectPacks(sddDir) {
11867
- const dir = path9.join(sddDir, "packs");
12005
+ const dir = path10.join(sddDir, "packs");
11868
12006
  const diagnostics = [];
11869
12007
  const packs = [];
11870
12008
  for (const entry of await listDir(dir)) {
11871
12009
  if (!/\.ya?ml$/i.test(entry)) continue;
11872
- const file = path9.join(dir, entry);
12010
+ const file = path10.join(dir, entry);
11873
12011
  const raw = await readTextIfExists(file);
11874
12012
  if (raw === void 0) continue;
11875
12013
  let data;
@@ -11956,7 +12094,7 @@ function packFindings(evaluations, change) {
11956
12094
  }
11957
12095
  var UI_DOMAINS = /* @__PURE__ */ new Set(["frontend", "mobile", "fullstack"]);
11958
12096
  async function runAnalyze(opts) {
11959
- const root = path10.resolve(opts.root);
12097
+ const root = path11.resolve(opts.root);
11960
12098
  const { workspace, config } = await loadWorkspace(root);
11961
12099
  const change = workspace.changes.find((c) => c.slug === opts.slug);
11962
12100
  if (!change) {
@@ -11974,7 +12112,7 @@ async function runAnalyze(opts) {
11974
12112
  for (const req of spec.spec.requirements) living.set(req.id, req);
11975
12113
  }
11976
12114
  if (change.delta) {
11977
- findings.push(...lintDelta(change.delta, living, path10.join(change.dir, "spec.md"), { language: config.spec.language }));
12115
+ findings.push(...lintDelta(change.delta, living, path11.join(change.dir, "spec.md"), { language: config.spec.language }));
11978
12116
  }
11979
12117
  const trace = checkTrace({ specs: workspace.specs, change, requireEvidence: false });
11980
12118
  findings.push(...trace.findings);
@@ -12023,7 +12161,7 @@ async function runAnalyze(opts) {
12023
12161
  if (mockups) result.mockups = mockups;
12024
12162
  if (packs) result.packs = packs;
12025
12163
  if (opts.write !== false) {
12026
- const file = path10.join(change.dir, "analyze.md");
12164
+ const file = path11.join(change.dir, "analyze.md");
12027
12165
  await writeText(file, renderAnalyze(change, result, localStamp(opts.now)));
12028
12166
  result.path = file;
12029
12167
  }
@@ -12065,7 +12203,7 @@ function renderAnalyze(change, result, generatedAt) {
12065
12203
  return lines.join("\n");
12066
12204
  }
12067
12205
  async function collectMetrics(root, now = /* @__PURE__ */ new Date()) {
12068
- const resolved = path11.resolve(root);
12206
+ const resolved = path12.resolve(root);
12069
12207
  const { workspace, config } = await loadWorkspace(resolved);
12070
12208
  const approvals = await loadApprovals(workspace.sddDir);
12071
12209
  const living = /* @__PURE__ */ new Map();
@@ -12080,7 +12218,7 @@ async function collectMetrics(root, now = /* @__PURE__ */ new Date()) {
12080
12218
  const deltaContent = await readDelta(change);
12081
12219
  const approval = verifyApproval(change, approvals.byArtifact, config, deltaContent);
12082
12220
  const findings = [];
12083
- if (change.delta) findings.push(...lintDelta(change.delta, living, path11.join(change.dir, "spec.md"), { language: config.spec.language }));
12221
+ if (change.delta) findings.push(...lintDelta(change.delta, living, path12.join(change.dir, "spec.md"), { language: config.spec.language }));
12084
12222
  const trace = checkTrace({
12085
12223
  specs: workspace.specs,
12086
12224
  change,
@@ -12145,7 +12283,7 @@ async function collectMetrics(root, now = /* @__PURE__ */ new Date()) {
12145
12283
  attention.push({ slug: change.slug, kind: "missing-evidence", detail: `evidencia ${change.scenariosPassed}/${change.scenariosTotal}` });
12146
12284
  }
12147
12285
  }
12148
- const archiveDir = path11.join(workspace.sddDir, "changes", "archive");
12286
+ const archiveDir = path12.join(workspace.sddDir, "changes", "archive");
12149
12287
  const archivedEntries = await listDirs(archiveDir);
12150
12288
  const throughputByMonth = {};
12151
12289
  for (const entry of archivedEntries) {
@@ -12183,10 +12321,10 @@ async function collectMetrics(root, now = /* @__PURE__ */ new Date()) {
12183
12321
  }
12184
12322
  async function readDelta(change) {
12185
12323
  const { readTextIfExists: readTextIfExists2 } = await import("./fsx-VF2P7ALA-XKEWTMDH.js");
12186
- return readTextIfExists2(path11.join(change.dir, "spec.md"));
12324
+ return readTextIfExists2(path12.join(change.dir, "spec.md"));
12187
12325
  }
12188
12326
  async function generatePresentation(opts) {
12189
- const root = path12.resolve(opts.root);
12327
+ const root = path13.resolve(opts.root);
12190
12328
  const { workspace, config } = await loadWorkspace(root);
12191
12329
  const language = config.project.language;
12192
12330
  const change = workspace.changes.find((c) => c.slug === opts.slug);
@@ -12196,17 +12334,17 @@ async function generatePresentation(opts) {
12196
12334
  if (!change.delta) {
12197
12335
  return { slug: opts.slug, diagnostics: [diag("ATLAS-PRESENT-001", "error", `El cambio "${opts.slug}" no tiene spec.md (delta)`)] };
12198
12336
  }
12199
- const deltaPath = path12.join(change.dir, "spec.md");
12337
+ const deltaPath = path13.join(change.dir, "spec.md");
12200
12338
  const deltaContent = await readTextIfExists(deltaPath) ?? "";
12201
12339
  const hash = artifactHash(deltaContent);
12202
12340
  const now = opts.now ?? /* @__PURE__ */ new Date();
12203
- const presentationDir = path12.join(change.dir, "presentation");
12204
- const mockupsCopyDir = path12.join(presentationDir, "mockups");
12341
+ const presentationDir = path13.join(change.dir, "presentation");
12342
+ const mockupsCopyDir = path13.join(presentationDir, "mockups");
12205
12343
  await ensureDir(presentationDir);
12206
12344
  const mockupInfo = await copyMockups(root, change, mockupsCopyDir);
12207
- const proposalRaw = await readTextIfExists(path12.join(change.dir, "proposal.md"));
12345
+ const proposalRaw = await readTextIfExists(path13.join(change.dir, "proposal.md"));
12208
12346
  const proposalBody = proposalRaw ? parseFrontmatter(proposalRaw).body : "";
12209
- const approvals = await loadApprovals(path12.join(root, ".sdd"));
12347
+ const approvals = await loadApprovals(path13.join(root, ".sdd"));
12210
12348
  const approval = verifyApproval(change, approvals.byArtifact, config, deltaContent);
12211
12349
  const labels = labelsFor(language);
12212
12350
  const html = page({
@@ -12227,7 +12365,7 @@ async function generatePresentation(opts) {
12227
12365
  approveHint: `satlas approve ${change.slug} --by "<nombre>" --channel presentation`,
12228
12366
  ...approval.status === "valid" && approval.approvedBy ? { approval: { by: approval.approvedBy, at: approval.approvedAt ?? "" } } : {}
12229
12367
  });
12230
- const outFile = path12.join(presentationDir, "index.html");
12368
+ const outFile = path13.join(presentationDir, "index.html");
12231
12369
  await writeText(outFile, html);
12232
12370
  return { slug: change.slug, path: outFile, hash, diagnostics: mockupInfo.diagnostics };
12233
12371
  }
@@ -12239,12 +12377,12 @@ async function copyMockups(root, change, destDir) {
12239
12377
  await ensureDir(destDir);
12240
12378
  const screens = [];
12241
12379
  for (const screen of manifest.screens) {
12242
- const src = path12.join(dir, screen.file);
12380
+ const src = path13.join(dir, screen.file);
12243
12381
  if (!await exists(src)) {
12244
12382
  diagnostics.push(diag("ATLAS-PRESENT-002", "warning", `El mockup ${screen.file} no existe y no se incluir\xE1`, { path: src }));
12245
12383
  continue;
12246
12384
  }
12247
- await copyFile(src, path12.join(destDir, screen.file));
12385
+ await copyFile(src, path13.join(destDir, screen.file));
12248
12386
  screens.push({
12249
12387
  id: screen.id,
12250
12388
  title: screen.title ?? screen.id,
@@ -12254,13 +12392,13 @@ async function copyMockups(root, change, destDir) {
12254
12392
  });
12255
12393
  }
12256
12394
  const screenshots = [];
12257
- const shotsDir = path12.join(dir, "screens");
12395
+ const shotsDir = path13.join(dir, "screens");
12258
12396
  if (await exists(shotsDir)) {
12259
- await ensureDir(path12.join(destDir, "screens"));
12397
+ await ensureDir(path13.join(destDir, "screens"));
12260
12398
  const { listDir: listDir4 } = await import("./fsx-VF2P7ALA-XKEWTMDH.js");
12261
12399
  for (const file of await listDir4(shotsDir)) {
12262
12400
  if (!/\.(png|jpe?g|webp)$/i.test(file)) continue;
12263
- await copyFile(path12.join(shotsDir, file), path12.join(destDir, "screens", file));
12401
+ await copyFile(path13.join(shotsDir, file), path13.join(destDir, "screens", file));
12264
12402
  screenshots.push(`mockups/screens/${file}`);
12265
12403
  }
12266
12404
  }
@@ -12476,11 +12614,11 @@ var SOURCE_EXT = /* @__PURE__ */ new Set([
12476
12614
  ".groovy"
12477
12615
  ]);
12478
12616
  async function adoptWorkspace(opts) {
12479
- const root = path13.resolve(opts.root);
12617
+ const root = path14.resolve(opts.root);
12480
12618
  const dryRun = opts.dryRun ?? false;
12481
12619
  const diagnostics = [];
12482
- const sddDir = path13.join(root, ".sdd");
12483
- if (!await exists(path13.join(sddDir, "config.yaml"))) {
12620
+ const sddDir = path14.join(root, ".sdd");
12621
+ if (!await exists(path14.join(sddDir, "config.yaml"))) {
12484
12622
  return {
12485
12623
  root,
12486
12624
  domains: [],
@@ -12491,8 +12629,8 @@ async function adoptWorkspace(opts) {
12491
12629
  }
12492
12630
  const { config } = await loadConfig(sddDir);
12493
12631
  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()));
12632
+ const inventoried = files.map((f) => toPosix(path14.relative(root, f.path)));
12633
+ const relative = inventoried.filter((f) => SOURCE_EXT.has(path14.extname(f).toLowerCase()));
12496
12634
  if (relative.length === 0) {
12497
12635
  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
12636
  }
@@ -12520,13 +12658,13 @@ async function adoptWorkspace(opts) {
12520
12658
  const createdSpecs = [];
12521
12659
  for (const name of domainNames.sort()) {
12522
12660
  const domainFiles = (counts.get(name) ?? []).sort();
12523
- const existingSpec = await exists(path13.join(sddDir, "specs", name, "spec.md"));
12661
+ const existingSpec = await exists(path14.join(sddDir, "specs", name, "spec.md"));
12524
12662
  domains.push({ name, files: domainFiles.length, samples: domainFiles.slice(0, 8), existingSpec });
12525
12663
  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") }));
12664
+ diagnostics.push(diag("ATLAS-ADOPT-003", "info", `La spec de "${name}" ya existe; se conserva`, { path: path14.join(sddDir, "specs", name, "spec.md") }));
12527
12665
  continue;
12528
12666
  }
12529
- const specPath = path13.join(sddDir, "specs", name, "spec.md");
12667
+ const specPath = path14.join(sddDir, "specs", name, "spec.md");
12530
12668
  if (!dryRun) {
12531
12669
  await writeText(specPath, baselineSpec(name, domainFiles));
12532
12670
  createdSpecs.push(specPath);
@@ -12534,7 +12672,7 @@ async function adoptWorkspace(opts) {
12534
12672
  createdSpecs.push(specPath);
12535
12673
  }
12536
12674
  }
12537
- const reportPath = path13.join(sddDir, "adopt-report.md");
12675
+ const reportPath = path14.join(sddDir, "adopt-report.md");
12538
12676
  if (!dryRun) {
12539
12677
  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
12678
  }
@@ -12554,12 +12692,12 @@ async function loadProjectProfiles(root, dirs) {
12554
12692
  if (!await exists(dir)) continue;
12555
12693
  out.push(...await loadProfilesFromDir(dir));
12556
12694
  }
12557
- const custom = path13.join(root, ".sdd", "profiles", "custom");
12695
+ const custom = path14.join(root, ".sdd", "profiles", "custom");
12558
12696
  if (await exists(custom)) out.push(...await loadProfilesFromDir(custom));
12559
12697
  return out;
12560
12698
  }
12561
12699
  async function fallbackDomainName(root, projectName) {
12562
- const pkg = await readTextIfExists(path13.join(root, "package.json"));
12700
+ const pkg = await readTextIfExists(path14.join(root, "package.json"));
12563
12701
  if (pkg) {
12564
12702
  try {
12565
12703
  const data = JSON.parse(pkg);
@@ -12646,11 +12784,11 @@ function adoptReport(input) {
12646
12784
  return lines.join("\n");
12647
12785
  }
12648
12786
  async function initWorkspace(opts) {
12649
- const root = path14.resolve(opts.root);
12650
- const sddDir = path14.join(root, ".sdd");
12787
+ const root = path15.resolve(opts.root);
12788
+ const sddDir = path15.join(root, ".sdd");
12651
12789
  const diagnostics = [];
12652
12790
  const created = [];
12653
- if (await exists(path14.join(sddDir, "config.yaml"))) {
12791
+ if (await exists(path15.join(sddDir, "config.yaml"))) {
12654
12792
  return {
12655
12793
  sddDir,
12656
12794
  created,
@@ -12660,29 +12798,29 @@ async function initWorkspace(opts) {
12660
12798
  created.push(...await ensureSddDirs(sddDir));
12661
12799
  const language = opts.language ?? "es";
12662
12800
  const cfg = defaultConfig({ name: opts.name, language });
12663
- cfg.project.name = opts.name ?? path14.basename(root);
12801
+ cfg.project.name = opts.name ?? path15.basename(root);
12664
12802
  await writeConfig(sddDir, cfg);
12665
- created.push(path14.join(sddDir, "config.yaml"));
12803
+ created.push(path15.join(sddDir, "config.yaml"));
12666
12804
  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"));
12805
+ await writeText(path15.join(sddDir, "constitution.md"), templates.constitution);
12806
+ created.push(path15.join(sddDir, "constitution.md"));
12807
+ await writeText(path15.join(sddDir, "glossary.md"), templates.glossary);
12808
+ created.push(path15.join(sddDir, "glossary.md"));
12671
12809
  const detected = await detectIfPossible(root, opts.profilesDir);
12672
12810
  if (detected) {
12673
12811
  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"));
12812
+ await writeText(path15.join(sddDir, "profiles", "detected.yaml"), yaml2);
12813
+ created.push(path15.join(sddDir, "profiles", "detected.yaml"));
12676
12814
  if (detected.best) {
12677
12815
  diagnostics.push(diag("ATLAS-INIT-002", "info", `Stack detectado: ${detected.best.displayName} (${detected.best.score} puntos)`, { suggestion: "Revisa .sdd/profiles/detected.yaml" }));
12678
12816
  } else {
12679
12817
  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
12818
  }
12681
12819
  }
12682
- await writeText(path14.join(sddDir, "INDEX.md"), initialIndex(language, cfg.project.name));
12683
- created.push(path14.join(sddDir, "INDEX.md"));
12820
+ await writeText(path15.join(sddDir, "INDEX.md"), initialIndex(language, cfg.project.name));
12821
+ created.push(path15.join(sddDir, "INDEX.md"));
12684
12822
  if (opts.local) {
12685
- const gitignore = path14.join(root, ".gitignore");
12823
+ const gitignore = path15.join(root, ".gitignore");
12686
12824
  const current = await readTextIfExists(gitignore) ?? "";
12687
12825
  if (!/(^|\n)\.sdd\/?(\n|$)/.test(current)) {
12688
12826
  const next = current.endsWith("\n") || current === "" ? `${current}.sdd/
@@ -12720,7 +12858,7 @@ async function createChange(opts) {
12720
12858
  const diagnostics = [];
12721
12859
  const slug = opts.slug.trim().toLowerCase();
12722
12860
  const language = opts.language ?? opts.cfg?.project.language ?? "es";
12723
- const dir = path15.join(path15.resolve(opts.root), ".sdd", "changes", slug);
12861
+ const dir = path16.join(path16.resolve(opts.root), ".sdd", "changes", slug);
12724
12862
  if (!SLUG_RE.test(slug)) {
12725
12863
  return {
12726
12864
  slug,
@@ -12751,20 +12889,20 @@ async function createChange(opts) {
12751
12889
  await ensureDir(dir);
12752
12890
  const templates = templatesFor(language);
12753
12891
  const files = [];
12754
- const metaFile = path15.join(dir, "meta.yaml");
12892
+ const metaFile = path16.join(dir, "meta.yaml");
12755
12893
  await writeText(metaFile, changeMetaYaml(meta));
12756
12894
  files.push(metaFile);
12757
12895
  if (lane === "fix") {
12758
- const fixFile = path15.join(dir, "fix.md");
12896
+ const fixFile = path16.join(dir, "fix.md");
12759
12897
  await writeText(fixFile, renderTemplate(templates.fix, { TITLE: title }));
12760
12898
  files.push(fixFile);
12761
12899
  return { slug, dir, files, diagnostics };
12762
12900
  }
12763
- const proposalFile = path15.join(dir, "proposal.md");
12901
+ const proposalFile = path16.join(dir, "proposal.md");
12764
12902
  await writeText(proposalFile, renderTemplate(templates.proposal, { TITLE: title }));
12765
12903
  files.push(proposalFile);
12766
12904
  const domainUpper = domain.replace(/[^a-z0-9]/gi, "").toUpperCase() || "GEN";
12767
- const deltaFile = path15.join(dir, "spec.md");
12905
+ const deltaFile = path16.join(dir, "spec.md");
12768
12906
  await writeText(deltaFile, templates.specDelta({ title, domainUpper, domain }));
12769
12907
  files.push(deltaFile);
12770
12908
  return { slug, dir, files, diagnostics };
@@ -12877,7 +13015,7 @@ async function restoreFile(file, previous) {
12877
13015
  }
12878
13016
  }
12879
13017
  async function archiveChange(opts) {
12880
- const root = path16.resolve(opts.root);
13018
+ const root = path17.resolve(opts.root);
12881
13019
  const diagnostics = [];
12882
13020
  const { config } = await loadWorkspace(root);
12883
13021
  const language = opts.language ?? config.project.language;
@@ -12893,16 +13031,43 @@ async function archiveChange(opts) {
12893
13031
  return { slug: opts.slug, fold: emptyFold, diagnostics, dryRun: opts.dryRun ?? false };
12894
13032
  }
12895
13033
  const nowFix = opts.now ?? /* @__PURE__ */ new Date();
12896
- const targetFix = path16.join(root, ".sdd", "changes", "archive", `${localMonth(nowFix)}-${opts.slug}`);
13034
+ const targetFix = path17.join(root, ".sdd", "changes", "archive", `${localMonth(nowFix)}-${opts.slug}`);
12897
13035
  if (await exists(targetFix)) {
12898
13036
  diagnostics.push(diag("ATLAS-ARCH-002", "error", `Ya existe un cambio archivado en ${targetFix}`, { path: targetFix }));
12899
13037
  return { slug: opts.slug, fold: emptyFold, diagnostics, dryRun: opts.dryRun ?? false };
12900
13038
  }
13039
+ let livingFix;
13040
+ let livingCreated = false;
13041
+ if (!opts.dryRun) {
13042
+ const fixRaw = await readTextIfExists(path17.join(change.dir, "fix.md")) ?? "";
13043
+ try {
13044
+ const written = await writeLivingFix(root, {
13045
+ slug: opts.slug,
13046
+ date: localDate(nowFix),
13047
+ result: "pass",
13048
+ ...change.meta.domain !== void 0 ? { domain: change.meta.domain } : {},
13049
+ ...change.meta.title !== void 0 ? { title: change.meta.title } : {},
13050
+ covers: change.fixCovers ?? [],
13051
+ content: fixRaw
13052
+ });
13053
+ livingFix = written.relativePath;
13054
+ livingCreated = written.created;
13055
+ } catch (error) {
13056
+ diagnostics.push(
13057
+ diag("ATLAS-ARCH-006", "error", `No se pudo conservar el fix vivo: ${error.message}`, {
13058
+ path: path17.join(root, ".sdd", "fixes"),
13059
+ suggestion: "Revisa los permisos de .sdd/fixes y vuelve a intentar; el fix sigue sin archivar"
13060
+ })
13061
+ );
13062
+ return { slug: opts.slug, fold: emptyFold, diagnostics, dryRun: false };
13063
+ }
13064
+ }
12901
13065
  if (!opts.dryRun) {
12902
- await ensureDir(path16.dirname(targetFix));
13066
+ await ensureDir(path17.dirname(targetFix));
12903
13067
  try {
12904
13068
  await moveDirectory(change.dir, targetFix);
12905
13069
  } catch (error) {
13070
+ if (livingCreated && livingFix) await removeFile(path17.join(root, livingFix));
12906
13071
  diagnostics.push(
12907
13072
  diag("ATLAS-ARCH-004", "error", `No se pudo mover el cambio al hist\xF3rico: ${error.message}`, {
12908
13073
  path: change.dir,
@@ -12911,9 +13076,16 @@ async function archiveChange(opts) {
12911
13076
  );
12912
13077
  return { slug: opts.slug, fold: emptyFold, diagnostics, dryRun: false };
12913
13078
  }
12914
- await regenerateIndex(root, config);
13079
+ await regenerateIndex(root, config, nowFix);
12915
13080
  }
12916
- return { slug: opts.slug, archivedTo: targetFix, fold: emptyFold, diagnostics, dryRun: opts.dryRun ?? false };
13081
+ return {
13082
+ slug: opts.slug,
13083
+ archivedTo: targetFix,
13084
+ ...livingFix !== void 0 ? { livingFix } : {},
13085
+ fold: emptyFold,
13086
+ diagnostics,
13087
+ dryRun: opts.dryRun ?? false
13088
+ };
12917
13089
  }
12918
13090
  if (!change.delta) {
12919
13091
  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 +13093,10 @@ async function archiveChange(opts) {
12921
13093
  }
12922
13094
  const domain = change.meta.domain;
12923
13095
  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") }));
13096
+ 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
13097
  return { slug: opts.slug, fold: { content: "", applied: { added: [], modified: [], removed: [], renamed: [] }, diagnostics }, diagnostics, dryRun: opts.dryRun ?? false };
12926
13098
  }
12927
- const specFile = path16.join(root, ".sdd", "specs", domain, "spec.md");
13099
+ const specFile = path17.join(root, ".sdd", "specs", domain, "spec.md");
12928
13100
  const existing = await readTextIfExists(specFile);
12929
13101
  const base = existing ?? `---
12930
13102
  domain: ${domain}
@@ -12954,8 +13126,8 @@ ${Object.entries(nextFm).map(([k, v]) => `${k}: ${String(v)}`).join("\n")}
12954
13126
 
12955
13127
  ${body}`;
12956
13128
  const month = localMonth(now);
12957
- const archiveDir = path16.join(root, ".sdd", "changes", "archive");
12958
- const target = path16.join(archiveDir, `${month}-${opts.slug}`);
13129
+ const archiveDir = path17.join(root, ".sdd", "changes", "archive");
13130
+ const target = path17.join(archiveDir, `${month}-${opts.slug}`);
12959
13131
  if (await exists(target)) {
12960
13132
  diagnostics.push(diag("ATLAS-ARCH-002", "error", `Ya existe un cambio archivado en ${target}`, { path: target }));
12961
13133
  return { slug: opts.slug, domain, fold, diagnostics, dryRun: opts.dryRun ?? false };
@@ -12981,17 +13153,25 @@ ${body}`;
12981
13153
  }
12982
13154
  async function regenerateIndex(root, cfg, now = /* @__PURE__ */ new Date()) {
12983
13155
  const { workspace, config } = cfg ? { workspace: (await loadWorkspace(root)).workspace, config: cfg } : await loadWorkspace(root);
12984
- const archiveDir = path16.join(root, ".sdd", "changes", "archive");
13156
+ const archiveDir = path17.join(root, ".sdd", "changes", "archive");
12985
13157
  const archived = await exists(archiveDir) ? (await fs.readdir(archiveDir)).filter((e) => !e.startsWith(".")).length : 0;
13158
+ const fixes = await loadLivingFixes(root);
12986
13159
  const markdown2 = indexMarkdown({
12987
13160
  projectName: config.project.name,
12988
13161
  language: config.project.language,
12989
13162
  specs: workspace.specs.map((s) => ({ domain: s.domain, requirements: s.spec.requirements.length })),
12990
13163
  changes: workspace.changes.map((c) => ({ slug: c.slug, lane: c.meta?.lane ?? config.lanes.default })),
13164
+ fixes: fixes.map((fix) => ({ slug: fix.slug, date: fix.date, result: fix.result, ...fix.domain !== void 0 ? { domain: fix.domain } : {} })),
12991
13165
  archived
12992
13166
  });
12993
13167
  void now;
12994
- await writeText(path16.join(root, ".sdd", "INDEX.md"), markdown2);
13168
+ await writeText(path17.join(root, ".sdd", "INDEX.md"), markdown2);
13169
+ }
13170
+ async function removeFile(file) {
13171
+ try {
13172
+ await rm(file, { force: true });
13173
+ } catch {
13174
+ }
12995
13175
  }
12996
13176
  var SCHEMA_VERSION = 1;
12997
13177
  var BACKUP_DIRNAME = ".backup";
@@ -13004,28 +13184,28 @@ var SCHEMA_MIGRATIONS = [
13004
13184
  to: SCHEMA_VERSION
13005
13185
  }
13006
13186
  ];
13007
- var FIXED_ARTIFACTS = ["config.yaml", "approvals.yaml", path17.join("profiles", "detected.yaml")];
13008
- var CHANGE_ARTIFACTS = ["meta.yaml", path17.join("mockups", "manifest.yaml")];
13187
+ var FIXED_ARTIFACTS = ["config.yaml", "approvals.yaml", path18.join("profiles", "detected.yaml")];
13188
+ var CHANGE_ARTIFACTS = ["meta.yaml", path18.join("mockups", "manifest.yaml")];
13009
13189
  async function pushChangeArtifacts(out, sddDir, relDir) {
13010
13190
  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 });
13191
+ const abs = path18.join(sddDir, relDir, rel);
13192
+ if (await exists(abs)) out.push({ artifact: toPosix(path18.join(relDir, rel)), path: abs });
13013
13193
  }
13014
13194
  }
13015
13195
  async function collectVersionedArtifacts(root) {
13016
- const sddDir = path17.join(root, ".sdd");
13196
+ const sddDir = path18.join(root, ".sdd");
13017
13197
  const out = [];
13018
13198
  for (const rel of FIXED_ARTIFACTS) {
13019
- const abs = path17.join(sddDir, rel);
13199
+ const abs = path18.join(sddDir, rel);
13020
13200
  if (await exists(abs)) out.push({ artifact: toPosix(rel), path: abs });
13021
13201
  }
13022
- const changesDir = path17.join(sddDir, "changes");
13202
+ const changesDir = path18.join(sddDir, "changes");
13023
13203
  for (const slug of await listDirs(changesDir)) {
13024
13204
  if (slug === "archive") continue;
13025
- await pushChangeArtifacts(out, sddDir, path17.join("changes", slug));
13205
+ await pushChangeArtifacts(out, sddDir, path18.join("changes", slug));
13026
13206
  }
13027
- for (const entry of await listDirs(path17.join(changesDir, "archive"))) {
13028
- await pushChangeArtifacts(out, sddDir, path17.join("changes", "archive", entry));
13207
+ for (const entry of await listDirs(path18.join(changesDir, "archive"))) {
13208
+ await pushChangeArtifacts(out, sddDir, path18.join("changes", "archive", entry));
13029
13209
  }
13030
13210
  return out;
13031
13211
  }
@@ -13110,7 +13290,7 @@ async function removeDir(dir) {
13110
13290
  } catch {
13111
13291
  }
13112
13292
  }
13113
- async function removeFile(file) {
13293
+ async function removeFile2(file) {
13114
13294
  try {
13115
13295
  await fs2.rm(file, { force: true });
13116
13296
  } catch {
@@ -13122,22 +13302,22 @@ function backupName(now) {
13122
13302
  async function applyUpgrade(root, now = /* @__PURE__ */ new Date()) {
13123
13303
  const plan = await planUpgrade(root);
13124
13304
  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);
13305
+ const sddDir = path18.join(root, ".sdd");
13306
+ const backupRoot = path18.join(sddDir, BACKUP_DIRNAME);
13127
13307
  const name = backupName(now);
13128
- const backupDir = path17.join(backupRoot, name);
13129
- const pointerFile = path17.join(backupRoot, BACKUP_POINTER);
13308
+ const backupDir = path18.join(backupRoot, name);
13309
+ const pointerFile = path18.join(backupRoot, BACKUP_POINTER);
13130
13310
  const previous = [];
13131
13311
  try {
13132
13312
  for (const item of plan.pending) {
13133
13313
  const content = await readTextIfExists(item.path);
13134
13314
  if (content === void 0) throw new Error("el elemento desapareci\xF3 mientras se respaldaba");
13135
13315
  previous.push({ item, contents: content });
13136
- await writeText(path17.join(backupDir, "files", ...item.artifact.split("/")), content);
13316
+ await writeText(path18.join(backupDir, "files", ...item.artifact.split("/")), content);
13137
13317
  }
13138
13318
  await writeText(
13139
- path17.join(backupDir, "backup.yaml"),
13140
- stringifyYaml5(
13319
+ path18.join(backupDir, "backup.yaml"),
13320
+ stringifyYaml6(
13141
13321
  {
13142
13322
  schema_version: SCHEMA_VERSION,
13143
13323
  created_at: localStamp(now),
@@ -13151,7 +13331,7 @@ async function applyUpgrade(root, now = /* @__PURE__ */ new Date()) {
13151
13331
  `);
13152
13332
  } catch (err) {
13153
13333
  await removeDir(backupDir);
13154
- await removeFile(pointerFile);
13334
+ await removeFile2(pointerFile);
13155
13335
  return { status: "failed", plan, applied: [], failure: { artifact: "(respaldo)", message: err.message } };
13156
13336
  }
13157
13337
  const written = [];
@@ -13171,13 +13351,13 @@ async function applyUpgrade(root, now = /* @__PURE__ */ new Date()) {
13171
13351
  }
13172
13352
  }
13173
13353
  }
13174
- await removeFile(pointerFile);
13354
+ await removeFile2(pointerFile);
13175
13355
  await removeDir(backupDir);
13176
13356
  return { status: "failed", plan, applied: [], failure: { artifact: item.artifact, message: err.message } };
13177
13357
  }
13178
13358
  }
13179
13359
  for (const dir of await listDirs(backupRoot)) {
13180
- if (dir !== name) await removeDir(path17.join(backupRoot, dir));
13360
+ if (dir !== name) await removeDir(path18.join(backupRoot, dir));
13181
13361
  }
13182
13362
  return {
13183
13363
  status: "applied",
@@ -13186,20 +13366,20 @@ async function applyUpgrade(root, now = /* @__PURE__ */ new Date()) {
13186
13366
  backup: {
13187
13367
  name,
13188
13368
  dir: backupDir,
13189
- relativeDir: toPosix(path17.relative(root, backupDir)),
13369
+ relativeDir: toPosix(path18.relative(root, backupDir)),
13190
13370
  createdAt: localStamp(now),
13191
13371
  files: plan.pending.map((i) => i.artifact)
13192
13372
  }
13193
13373
  };
13194
13374
  }
13195
13375
  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);
13376
+ const sddDir = path18.join(root, ".sdd");
13377
+ const backupRoot = path18.join(sddDir, BACKUP_DIRNAME);
13378
+ const pointerFile = path18.join(backupRoot, BACKUP_POINTER);
13199
13379
  const name = (await readTextIfExists(pointerFile))?.trim();
13200
13380
  if (!name) return { status: "no-backup", restored: [] };
13201
- const backupDir = path17.join(backupRoot, name);
13202
- const manifestRaw = await readTextIfExists(path17.join(backupDir, "backup.yaml"));
13381
+ const backupDir = path18.join(backupRoot, name);
13382
+ const manifestRaw = await readTextIfExists(path18.join(backupDir, "backup.yaml"));
13203
13383
  if (manifestRaw === void 0) return { status: "no-backup", restored: [] };
13204
13384
  let files = [];
13205
13385
  try {
@@ -13210,8 +13390,8 @@ async function rollbackUpgrade(root) {
13210
13390
  }
13211
13391
  const restored = [];
13212
13392
  for (const file of files) {
13213
- const from = path17.join(backupDir, "files", ...file.artifact.split("/"));
13214
- const to = path17.join(sddDir, ...file.artifact.split("/"));
13393
+ const from = path18.join(backupDir, "files", ...file.artifact.split("/"));
13394
+ const to = path18.join(sddDir, ...file.artifact.split("/"));
13215
13395
  const content = await readTextIfExists(from);
13216
13396
  if (content === void 0) continue;
13217
13397
  try {
@@ -13222,7 +13402,7 @@ async function rollbackUpgrade(root) {
13222
13402
  }
13223
13403
  }
13224
13404
  await removeDir(backupDir);
13225
- await removeFile(pointerFile);
13405
+ await removeFile2(pointerFile);
13226
13406
  return { status: "restored", restored, backup: name };
13227
13407
  }
13228
13408
  async function upgradeAdvisory(root) {
@@ -13290,7 +13470,7 @@ function resultOf(diagnostic, root) {
13290
13470
  message: { text: diagnostic.message }
13291
13471
  };
13292
13472
  if (diagnostic.path) {
13293
- const rel = path18.relative(root, diagnostic.path);
13473
+ const rel = path19.relative(root, diagnostic.path);
13294
13474
  if (rel !== "") {
13295
13475
  const physicalLocation = {
13296
13476
  artifactLocation: { uri: toPosix(rel) },
@@ -13345,7 +13525,7 @@ async function runDoctor(root) {
13345
13525
  const approvals = await loadApprovals(workspace.sddDir);
13346
13526
  findings.push(...approvals.diagnostics);
13347
13527
  for (const change of workspace.changes) {
13348
- const deltaPath = path19.join(change.dir, "spec.md");
13528
+ const deltaPath = path20.join(change.dir, "spec.md");
13349
13529
  const deltaContent = await readTextIfExists(deltaPath);
13350
13530
  const approval = verifyApproval(change, approvals.byArtifact, config, deltaContent ?? void 0);
13351
13531
  if ((change.planPath || change.tasks) && (approval.status === "missing" || approval.status === "stale")) {
@@ -13367,7 +13547,7 @@ async function runDoctor(root) {
13367
13547
  }
13368
13548
  for (const override of change.meta?.overrides ?? []) {
13369
13549
  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") }));
13550
+ findings.push(diag("ATLAS-LIFECYCLE-004", "error", `Override del gate "${override.gate}" sin motivo o autor`, { path: path20.join(change.dir, "meta.yaml") }));
13371
13551
  }
13372
13552
  }
13373
13553
  }
@@ -13399,7 +13579,7 @@ function livingRequirementsMap(specs) {
13399
13579
  return map;
13400
13580
  }
13401
13581
  async function runCiGate(opts) {
13402
- const root = path20.resolve(opts.root);
13582
+ const root = path21.resolve(opts.root);
13403
13583
  const { workspace, config } = await loadWorkspace(root);
13404
13584
  const diagnostics = [];
13405
13585
  const checks = [];
@@ -13410,7 +13590,7 @@ async function runCiGate(opts) {
13410
13590
  let changesErrors = 0;
13411
13591
  let changesWarnings = 0;
13412
13592
  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 }) : [];
13593
+ const lintFindings = change.delta ? lintDelta(change.delta, living, path21.join(change.dir, "spec.md"), { language: config.spec.language }) : [];
13414
13594
  const trace = checkTrace({
13415
13595
  specs: workspace.specs,
13416
13596
  change,
@@ -13552,6 +13732,7 @@ var ES2 = {
13552
13732
  "status.title": "Estado de los cambios",
13553
13733
  "status.changes": "Cambios activos",
13554
13734
  "status.specs": "Specs vivas",
13735
+ "status.fixes": "Fixes vivos",
13555
13736
  "status.next": "siguiente:",
13556
13737
  "status.tasks": "tareas",
13557
13738
  "status.evidence": "evidencia",
@@ -13586,6 +13767,7 @@ var EN2 = {
13586
13767
  "status.title": "Change status",
13587
13768
  "status.changes": "Active changes",
13588
13769
  "status.specs": "Living specs",
13770
+ "status.fixes": "Living fixes",
13589
13771
  "status.next": "next:",
13590
13772
  "status.tasks": "tasks",
13591
13773
  "status.evidence": "evidence",
@@ -13630,26 +13812,26 @@ function cliVersion() {
13630
13812
  }
13631
13813
 
13632
13814
  // src/commands/adapters.ts
13633
- import path24 from "path";
13815
+ import path25 from "path";
13634
13816
 
13635
13817
  // ../adapters/dist/index.js
13636
- import path21 from "path";
13637
- import { stringify as stringifyYaml6 } from "yaml";
13638
13818
  import path22 from "path";
13819
+ import { stringify as stringifyYaml7 } from "yaml";
13820
+ import path23 from "path";
13639
13821
  async function loadWorkflow(workflowDir) {
13640
- const phasesDir = path21.join(workflowDir, "phases");
13641
- const snippetsDir = path21.join(workflowDir, "snippets");
13822
+ const phasesDir = path22.join(workflowDir, "phases");
13823
+ const snippetsDir = path22.join(workflowDir, "snippets");
13642
13824
  const phases = [];
13643
13825
  const snippets = /* @__PURE__ */ new Map();
13644
13826
  const hashParts = [];
13645
13827
  for (const entry of (await listDir(phasesDir)).sort()) {
13646
13828
  if (!entry.endsWith(".md")) continue;
13647
- const filePath = path21.join(phasesDir, entry);
13829
+ const filePath = path22.join(phasesDir, entry);
13648
13830
  const raw = await readText(filePath);
13649
13831
  hashParts.push(raw);
13650
13832
  const fm = parseFrontmatter(raw, filePath);
13651
13833
  const data = fm.data;
13652
- const id = typeof data["id"] === "string" ? data["id"] : path21.basename(entry, ".md");
13834
+ const id = typeof data["id"] === "string" ? data["id"] : path22.basename(entry, ".md");
13653
13835
  phases.push({
13654
13836
  id,
13655
13837
  title: typeof data["title"] === "string" ? data["title"] : id,
@@ -13663,9 +13845,9 @@ async function loadWorkflow(workflowDir) {
13663
13845
  }
13664
13846
  for (const entry of (await listDir(snippetsDir)).sort()) {
13665
13847
  if (!entry.endsWith(".md")) continue;
13666
- const raw = await readTextIfExists(path21.join(snippetsDir, entry)) ?? "";
13848
+ const raw = await readTextIfExists(path22.join(snippetsDir, entry)) ?? "";
13667
13849
  hashParts.push(raw);
13668
- snippets.set(path21.basename(entry, ".md"), raw.trim());
13850
+ snippets.set(path22.basename(entry, ".md"), raw.trim());
13669
13851
  }
13670
13852
  return { phases: phases.sort((a, b) => a.id.localeCompare(b.id)), snippets, sourceHash: sha256(hashParts.join("\n---\n")) };
13671
13853
  }
@@ -13689,7 +13871,7 @@ function vars(language, slugToken, commandPrefix) {
13689
13871
  return { SLUG: slugToken, LANGUAGE: language, LANGUAGE_NAME: LANGUAGE_NAMES[language], SDD_DIR: ".sdd", COMMAND_PREFIX: commandPrefix };
13690
13872
  }
13691
13873
  function frontmatter(data, body) {
13692
- const yaml2 = stringifyYaml6(data, { lineWidth: 0 }).trimEnd();
13874
+ const yaml2 = stringifyYaml7(data, { lineWidth: 0 }).trimEnd();
13693
13875
  return `---
13694
13876
  ${yaml2}
13695
13877
  ---
@@ -13847,11 +14029,11 @@ Reglas: la spec es funcional y de negocio (sin tecnolog\xEDa); la trazabilidad e
13847
14029
  function tomlString(value) {
13848
14030
  return `"${value.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
13849
14031
  }
13850
- var MANIFEST_REL = path22.join(".sdd", ".generated", "manifest.json");
14032
+ var MANIFEST_REL = path23.join(".sdd", ".generated", "manifest.json");
13851
14033
  var BEGIN = "<!-- BEGIN specatlas -->";
13852
14034
  var END = "<!-- END specatlas -->";
13853
14035
  async function compileTargets(opts) {
13854
- const root = path22.resolve(opts.root);
14036
+ const root = path23.resolve(opts.root);
13855
14037
  const language = opts.language ?? "es";
13856
14038
  const diagnostics = [];
13857
14039
  const sources = await loadWorkflow(opts.workflowDir);
@@ -13859,7 +14041,7 @@ async function compileTargets(opts) {
13859
14041
  diagnostics.push({
13860
14042
  code: "ATLAS-ADAPTERS-002",
13861
14043
  severity: "error",
13862
- message: `No hay fases en ${path22.join(opts.workflowDir, "phases")}`,
14044
+ message: `No hay fases en ${path23.join(opts.workflowDir, "phases")}`,
13863
14045
  suggestion: "Revisa la carpeta workflow/phases del proyecto"
13864
14046
  });
13865
14047
  }
@@ -13871,7 +14053,7 @@ async function compileTargets(opts) {
13871
14053
  }
13872
14054
  }
13873
14055
  const compiled = [...deduped.values()];
13874
- const manifestPath = path22.join(root, MANIFEST_REL);
14056
+ const manifestPath = path23.join(root, MANIFEST_REL);
13875
14057
  const previous = await readManifest(manifestPath);
13876
14058
  const previousHashes = /* @__PURE__ */ new Map();
13877
14059
  for (const files2 of Object.values(previous?.targets ?? {})) {
@@ -13882,7 +14064,7 @@ async function compileTargets(opts) {
13882
14064
  const stale = [];
13883
14065
  const missing = [];
13884
14066
  for (const file of compiled) {
13885
- const abs = path22.join(root, file.path);
14067
+ const abs = path23.join(root, file.path);
13886
14068
  const finalContent = file.path === "AGENTS.md" ? mergeAgentsBlock(await readTextIfExists(abs) ?? "", file.content) : file.content;
13887
14069
  const hash = artifactHash(finalContent);
13888
14070
  const existing = await readTextIfExists(abs);
@@ -13898,7 +14080,7 @@ async function compileTargets(opts) {
13898
14080
  stale.push(file.path);
13899
14081
  }
13900
14082
  if (!opts.check && status !== "unchanged") {
13901
- await ensureDir(path22.dirname(abs));
14083
+ await ensureDir(path23.dirname(abs));
13902
14084
  await writeText(abs, finalContent);
13903
14085
  written.push(file.path);
13904
14086
  }
@@ -13952,7 +14134,7 @@ async function readManifest(manifestPath) {
13952
14134
  }
13953
14135
  }
13954
14136
  async function checkAdapters(opts) {
13955
- const manifestPath = path22.join(path22.resolve(opts.root), MANIFEST_REL);
14137
+ const manifestPath = path23.join(path23.resolve(opts.root), MANIFEST_REL);
13956
14138
  const manifestRaw = await readTextIfExists(manifestPath);
13957
14139
  if (manifestRaw === void 0) return { ok: false, manifest: false, stale: [], missing: [], orphaned: [] };
13958
14140
  const manifest = await readManifest(manifestPath);
@@ -13969,7 +14151,7 @@ async function checkAdapters(opts) {
13969
14151
  }
13970
14152
 
13971
14153
  // src/paths.ts
13972
- import path23 from "path";
14154
+ import path24 from "path";
13973
14155
  import { fileURLToPath as fileURLToPath2 } from "url";
13974
14156
  async function resolveProfilesDir() {
13975
14157
  const env = process.env["SPECATLAS_PROFILES_DIR"];
@@ -13979,16 +14161,16 @@ async function resolveProfilesDir() {
13979
14161
  async function resolveWorkflowDir() {
13980
14162
  const env = process.env["SPECATLAS_WORKFLOW_DIR"];
13981
14163
  if (env && await exists(env)) return env;
13982
- return walkUpFor(path23.join("workflow", "phases"));
14164
+ return walkUpFor(path24.join("workflow", "phases"));
13983
14165
  }
13984
14166
  async function walkUpFor(relative) {
13985
- let dir = path23.dirname(fileURLToPath2(import.meta.url));
14167
+ let dir = path24.dirname(fileURLToPath2(import.meta.url));
13986
14168
  for (let i = 0; i < 8; i += 1) {
13987
- const candidate = path23.join(dir, relative);
14169
+ const candidate = path24.join(dir, relative);
13988
14170
  if (await exists(candidate)) {
13989
- return relative.includes(path23.sep) ? path23.dirname(candidate) : candidate;
14171
+ return relative.includes(path24.sep) ? path24.dirname(candidate) : candidate;
13990
14172
  }
13991
- const parent = path23.dirname(dir);
14173
+ const parent = path24.dirname(dir);
13992
14174
  if (parent === dir) break;
13993
14175
  dir = parent;
13994
14176
  }
@@ -14066,7 +14248,7 @@ async function runAdapters(ctx) {
14066
14248
  for (const [target, count2] of byTarget) lines.push(` ${target}: ${count2} archivo(s)`);
14067
14249
  if (report.written.length > 0) {
14068
14250
  lines.push("");
14069
- for (const p of report.written) lines.push(` + ${path24.relative(ctx.cwd, path24.join(root, p))}`);
14251
+ for (const p of report.written) lines.push(` + ${path25.relative(ctx.cwd, path25.join(root, p))}`);
14070
14252
  } else {
14071
14253
  lines.push("");
14072
14254
  lines.push("Sin cambios: los adaptadores ya estaban al d\xEDa.");
@@ -14077,7 +14259,7 @@ async function runAdapters(ctx) {
14077
14259
  }
14078
14260
 
14079
14261
  // src/commands/adopt.ts
14080
- import path25 from "path";
14262
+ import path26 from "path";
14081
14263
  async function runAdopt(ctx) {
14082
14264
  const { root } = await requireWorkspace(ctx);
14083
14265
  const domainsFlag = flagString(ctx.flags, "domains") ?? flagString(ctx.flags, "domain");
@@ -14097,11 +14279,11 @@ async function runAdopt(ctx) {
14097
14279
  }
14098
14280
  if (result.createdSpecs.length > 0) {
14099
14281
  lines.push("");
14100
- for (const spec of result.createdSpecs) lines.push(` ${result.dryRun ? "[dry-run] " : "+ "}${path25.relative(ctx.cwd, spec)}`);
14282
+ for (const spec of result.createdSpecs) lines.push(` ${result.dryRun ? "[dry-run] " : "+ "}${path26.relative(ctx.cwd, spec)}`);
14101
14283
  }
14102
14284
  if (result.reportPath && !result.dryRun) {
14103
14285
  lines.push("");
14104
- lines.push(` Informe: ${path25.relative(ctx.cwd, result.reportPath)}`);
14286
+ lines.push(` Informe: ${path26.relative(ctx.cwd, result.reportPath)}`);
14105
14287
  }
14106
14288
  lines.push("");
14107
14289
  lines.push("Siguiente: fase /satlas-adopt por dominio (requisitos AS-IS) y luego:");
@@ -14114,8 +14296,8 @@ async function runAdopt(ctx) {
14114
14296
  diagnostics: result.diagnostics,
14115
14297
  data: {
14116
14298
  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,
14299
+ createdSpecs: result.createdSpecs.map((s) => path26.relative(ctx.cwd, s)),
14300
+ reportPath: result.reportPath ? path26.relative(ctx.cwd, result.reportPath) : void 0,
14119
14301
  stack: result.stack,
14120
14302
  dryRun: result.dryRun
14121
14303
  },
@@ -14124,7 +14306,7 @@ async function runAdopt(ctx) {
14124
14306
  }
14125
14307
 
14126
14308
  // src/commands/analyze.ts
14127
- import path26 from "path";
14309
+ import path27 from "path";
14128
14310
  async function runAnalyzeCommand(ctx) {
14129
14311
  const { root } = await requireWorkspace(ctx);
14130
14312
  const slug = ctx.positionals[0];
@@ -14135,23 +14317,23 @@ async function runAnalyzeCommand(ctx) {
14135
14317
  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
14318
  if (result.waves) lines.push(` olas: ${result.waves.blocks} bloque(s), ${result.waves.waves} ola(s), ${result.waves.tasks} tarea(s)`);
14137
14319
  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)}`);
14320
+ if (result.path) lines.push("", ` informe: ${path27.relative(ctx.cwd, result.path)}`);
14139
14321
  for (const finding of result.findings) {
14140
14322
  lines.push(` ${finding.severity.toUpperCase()} ${finding.code} \u2014 ${finding.message}`);
14141
14323
  }
14142
14324
  return {
14143
14325
  exitCode: result.status === "blocked" ? 1 : 0,
14144
14326
  diagnostics: result.findings,
14145
- data: result.path ? { ...result, path: path26.relative(ctx.cwd, result.path) } : result,
14327
+ data: result.path ? { ...result, path: path27.relative(ctx.cwd, result.path) } : result,
14146
14328
  text: lines
14147
14329
  };
14148
14330
  }
14149
14331
 
14150
14332
  // src/commands/approve.ts
14151
- import path28 from "path";
14333
+ import path29 from "path";
14152
14334
 
14153
14335
  // src/commands/issue.ts
14154
- import path27 from "path";
14336
+ import path28 from "path";
14155
14337
  async function runIssue(ctx) {
14156
14338
  const action = ctx.positionals[0] ?? "status";
14157
14339
  const slug = ctx.positionals[1];
@@ -14179,7 +14361,7 @@ async function runIssue(ctx) {
14179
14361
  return { exitCode: 2, diagnostics: [{ code: "ATLAS-ISSUE-000", severity: "error", message: "Falta el slug: satlas issue status <slug>" }] };
14180
14362
  }
14181
14363
  const change = await loadChange(root, slug);
14182
- const { config } = await loadConfig(path27.join(root, ".sdd"));
14364
+ const { config } = await loadConfig(path28.join(root, ".sdd"));
14183
14365
  const tracker = change.meta?.tracker;
14184
14366
  const lines = [`Issue de ${slug}`, ""];
14185
14367
  if (!tracker || tracker.provider !== "github") {
@@ -14250,7 +14432,7 @@ async function runApprove(ctx) {
14250
14432
  };
14251
14433
  }
14252
14434
  const { root, workspace, config } = await requireWorkspace(ctx);
14253
- const artifact = target.includes("/") || target.includes("\\") ? target : path28.join("changes", target, "spec.md");
14435
+ const artifact = target.includes("/") || target.includes("\\") ? target : path29.join("changes", target, "spec.md");
14254
14436
  const channelFlag = flagString(ctx.flags, "channel");
14255
14437
  const channel = channelFlag === "presentation" || channelFlag === "editor" || channelFlag === "pr" || channelFlag === "tracker" ? channelFlag : "cli";
14256
14438
  const change = workspace.changes.find(
@@ -14291,13 +14473,13 @@ async function runApprove(ctx) {
14291
14473
  return {
14292
14474
  exitCode: hasErrors2 ? 1 : 0,
14293
14475
  diagnostics: result.diagnostics,
14294
- data: result.approval ? { ...result.approval, file: path28.relative(ctx.cwd, result.file) } : void 0,
14476
+ data: result.approval ? { ...result.approval, file: path29.relative(ctx.cwd, result.file) } : void 0,
14295
14477
  text: lines
14296
14478
  };
14297
14479
  }
14298
14480
 
14299
14481
  // src/commands/archive.ts
14300
- import path29 from "path";
14482
+ import path30 from "path";
14301
14483
  async function runArchive(ctx) {
14302
14484
  const slug = ctx.positionals[0];
14303
14485
  if (!slug) {
@@ -14321,27 +14503,31 @@ async function runArchive(ctx) {
14321
14503
  }
14322
14504
  const result = await archiveChange({ root, slug, language: config.project.language, dryRun });
14323
14505
  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) {
14506
+ const operations = result.fold.applied.added.length + result.fold.applied.modified.length + result.fold.applied.removed.length + result.fold.applied.renamed.length;
14507
+ if (operations > 0) {
14325
14508
  lines.push(`${dryRun ? "[dry-run] " : ""}${msg("archive.done", ctx.language)}`);
14326
14509
  lines.push(` agregados: ${result.fold.applied.added.join(", ") || "\u2014"}`);
14327
14510
  lines.push(` modificados: ${result.fold.applied.modified.join(", ") || "\u2014"}`);
14328
14511
  lines.push(` eliminados: ${result.fold.applied.removed.join(", ") || "\u2014"}`);
14329
14512
  lines.push(` renombrados: ${result.fold.applied.renamed.join(", ") || "\u2014"}`);
14330
- if (result.archivedTo) lines.push(` archivado en: ${path29.relative(ctx.cwd, result.archivedTo)}`);
14513
+ } else if (result.livingFix) {
14514
+ lines.push(`${dryRun ? "[dry-run] " : ""}Fix archivado y conservado como fix vivo.`);
14515
+ lines.push(` fix vivo: ${result.livingFix}`);
14331
14516
  } else {
14332
14517
  lines.push("El delta no contiene operaciones (ADDED/MODIFIED/REMOVED/RENAMED).");
14333
14518
  }
14519
+ if (result.archivedTo) lines.push(` archivado en: ${path30.relative(ctx.cwd, result.archivedTo)}`);
14334
14520
  const hasErrors2 = result.diagnostics.some((d) => d.severity === "error");
14335
14521
  return {
14336
14522
  exitCode: hasErrors2 ? 1 : 0,
14337
14523
  diagnostics: result.diagnostics,
14338
- data: { slug: result.slug, domain: result.domain, archivedTo: result.archivedTo, applied: result.fold.applied, dryRun: result.dryRun },
14524
+ data: { slug: result.slug, domain: result.domain, archivedTo: result.archivedTo, livingFix: result.livingFix, applied: result.fold.applied, dryRun: result.dryRun },
14339
14525
  text: lines
14340
14526
  };
14341
14527
  }
14342
14528
 
14343
14529
  // src/commands/ci.ts
14344
- import path30 from "path";
14530
+ import path31 from "path";
14345
14531
  async function runCi(ctx) {
14346
14532
  const { root, config } = await requireWorkspace(ctx);
14347
14533
  const strict = flagBool(ctx.flags, "strict");
@@ -14367,10 +14553,10 @@ async function runCi(ctx) {
14367
14553
  const lines = ["CI de SpecAtlas", ""];
14368
14554
  let sarifWritten;
14369
14555
  if (sarifPath) {
14370
- const target = path30.resolve(ctx.cwd, sarifPath);
14556
+ const target = path31.resolve(ctx.cwd, sarifPath);
14371
14557
  try {
14372
14558
  await writeText(target, toSarifText({ diagnostics: gate.diagnostics, root, version: cliVersion(), failed: gate.failed }));
14373
- sarifWritten = path30.relative(ctx.cwd, target);
14559
+ sarifWritten = path31.relative(ctx.cwd, target);
14374
14560
  } catch (err) {
14375
14561
  diagnostics.push({
14376
14562
  code: "ATLAS-CI-SARIF-001",
@@ -14405,12 +14591,12 @@ async function runCi(ctx) {
14405
14591
  }
14406
14592
 
14407
14593
  // src/commands/profile.ts
14408
- import path31 from "path";
14594
+ import path32 from "path";
14409
14595
  async function runProfile(ctx) {
14410
14596
  const action = ctx.positionals[0] ?? "detect";
14411
14597
  const { root, workspace } = await requireWorkspace(ctx);
14412
14598
  const officialDir = await resolveProfilesDir();
14413
- const customDir = path31.join(workspace.sddDir, "profiles", "custom");
14599
+ const customDir = path32.join(workspace.sddDir, "profiles", "custom");
14414
14600
  const profiles = [...officialDir ? await loadProfilesFromDir(officialDir) : [], ...await loadProfilesFromDir(customDir)];
14415
14601
  if (action === "detect") {
14416
14602
  const result = await detectProfiles(root, profiles);
@@ -14439,7 +14625,7 @@ async function runProfile(ctx) {
14439
14625
  if (!name) {
14440
14626
  return { exitCode: 2, diagnostics: [{ code: "ATLAS-PROFILE-001", severity: "error", message: "Falta el nombre del perfil: satlas profile create <nombre>" }] };
14441
14627
  }
14442
- const file = path31.join(customDir, `${name}.yaml`);
14628
+ const file = path32.join(customDir, `${name}.yaml`);
14443
14629
  if (await exists(file)) {
14444
14630
  return { exitCode: 2, diagnostics: [{ code: "ATLAS-PROFILE-002", severity: "error", message: `El perfil ya existe: ${file}` }] };
14445
14631
  }
@@ -14448,7 +14634,7 @@ async function runProfile(ctx) {
14448
14634
  exitCode: 0,
14449
14635
  diagnostics: [],
14450
14636
  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."]
14637
+ 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
14638
  };
14453
14639
  }
14454
14640
  return {
@@ -14457,7 +14643,7 @@ async function runProfile(ctx) {
14457
14643
  };
14458
14644
  }
14459
14645
  async function isInside(dir, name) {
14460
- return exists(path31.join(dir, `${name}.yaml`));
14646
+ return exists(path32.join(dir, `${name}.yaml`));
14461
14647
  }
14462
14648
  function profileTemplate(name) {
14463
14649
  return `name: ${name}
@@ -14485,7 +14671,7 @@ assumptions: []
14485
14671
  }
14486
14672
 
14487
14673
  // src/commands/doctor.ts
14488
- import path32 from "path";
14674
+ import path33 from "path";
14489
14675
  async function runDoctorCommand(ctx) {
14490
14676
  const { root, config } = await requireWorkspace(ctx);
14491
14677
  const report = await runDoctor(root);
@@ -14510,7 +14696,7 @@ async function runDoctorCommand(ctx) {
14510
14696
  lines.push("Sin hallazgos. El workspace est\xE1 sano.");
14511
14697
  } else {
14512
14698
  for (const finding of report.findings) {
14513
- const location = finding.path ? ` ${path32.relative(ctx.cwd, finding.path)}${finding.line ? `:${finding.line}` : ""}` : "";
14699
+ const location = finding.path ? ` ${path33.relative(ctx.cwd, finding.path)}${finding.line ? `:${finding.line}` : ""}` : "";
14514
14700
  lines.push(` ${finding.severity.toUpperCase()} ${finding.code}${location} \u2014 ${finding.message}`);
14515
14701
  if (finding.suggestion) lines.push(` \u21B3 ${finding.suggestion}`);
14516
14702
  }
@@ -14584,7 +14770,7 @@ async function runHelp(ctx, commandName) {
14584
14770
  }
14585
14771
 
14586
14772
  // src/commands/init.ts
14587
- import path33 from "path";
14773
+ import path34 from "path";
14588
14774
  async function runInit(ctx) {
14589
14775
  const languageFlag = flagString(ctx.flags, "language");
14590
14776
  const language = languageFlag === "en" ? "en" : languageFlag === "es" ? "es" : void 0;
@@ -14631,11 +14817,11 @@ async function runInit(ctx) {
14631
14817
  const lines = [msg("init.title", ctx.language), ""];
14632
14818
  if (result.created.length > 0) {
14633
14819
  lines.push(msg("init.done", ctx.language));
14634
- for (const file of result.created) lines.push(` + ${path33.relative(ctx.cwd, file)}`);
14820
+ for (const file of result.created) lines.push(` + ${path34.relative(ctx.cwd, file)}`);
14635
14821
  if (compiled.length > 0) {
14636
14822
  lines.push("");
14637
14823
  lines.push("Adaptadores compilados:");
14638
- for (const file of compiled) lines.push(` + ${path33.relative(ctx.cwd, path33.join(ctx.cwd, file))}`);
14824
+ for (const file of compiled) lines.push(` + ${path34.relative(ctx.cwd, path34.join(ctx.cwd, file))}`);
14639
14825
  }
14640
14826
  lines.push("");
14641
14827
  lines.push("Siguiente: crea un cambio con `satlas new <slug>` y escribe la spec funcional.");
@@ -14645,8 +14831,8 @@ async function runInit(ctx) {
14645
14831
  exitCode: hasErrors2 ? 1 : 0,
14646
14832
  diagnostics: result.diagnostics,
14647
14833
  data: {
14648
- sddDir: path33.relative(ctx.cwd, result.sddDir),
14649
- created: result.created.map((f) => path33.relative(ctx.cwd, f)),
14834
+ sddDir: path34.relative(ctx.cwd, result.sddDir),
14835
+ created: result.created.map((f) => path34.relative(ctx.cwd, f)),
14650
14836
  detected: result.detected?.matches.map((m) => ({ name: m.name, score: m.score })),
14651
14837
  best: result.detected?.best?.name
14652
14838
  },
@@ -14687,7 +14873,7 @@ async function runMetrics(ctx) {
14687
14873
  }
14688
14874
 
14689
14875
  // src/commands/mockup.ts
14690
- import path34 from "path";
14876
+ import path35 from "path";
14691
14877
  async function runMockup(ctx) {
14692
14878
  const { root, workspace, config } = await requireWorkspace(ctx);
14693
14879
  const slug = ctx.positionals[0];
@@ -14736,17 +14922,17 @@ async function runMockup(ctx) {
14736
14922
  ` pantallas: ${plan.screens.length}`,
14737
14923
  ...plan.screens.map((s) => ` - ${s.id}: ${s.title} (${s.illustrates.length} escenario(s))`),
14738
14924
  "",
14739
- ` plan: ${path34.relative(ctx.cwd, planFile)}`,
14740
- ` manifiesto: ${path34.relative(ctx.cwd, manifestFile)}`,
14925
+ ` plan: ${path35.relative(ctx.cwd, planFile)}`,
14926
+ ` manifiesto: ${path35.relative(ctx.cwd, manifestFile)}`,
14741
14927
  "",
14742
14928
  "Siguiente: genera el HTML de cada pantalla (fase /satlas-mockup) y valida con `satlas mockup " + slug + " --check`."
14743
14929
  ];
14744
- return { exitCode: 0, diagnostics: [], data: { plan, planFile: path34.relative(ctx.cwd, planFile), manifestFile: path34.relative(ctx.cwd, manifestFile) }, text: lines };
14930
+ return { exitCode: 0, diagnostics: [], data: { plan, planFile: path35.relative(ctx.cwd, planFile), manifestFile: path35.relative(ctx.cwd, manifestFile) }, text: lines };
14745
14931
  }
14746
14932
 
14747
14933
  // src/mcp/server.ts
14748
14934
  import { promises as fs3 } from "fs";
14749
- import path37 from "path";
14935
+ import path38 from "path";
14750
14936
  import { createInterface } from "readline/promises";
14751
14937
 
14752
14938
  // src/mcp/protocol.ts
@@ -14807,13 +14993,40 @@ function stringArg(args, key) {
14807
14993
  return typeof value === "string" && value.trim() !== "" ? value.trim() : void 0;
14808
14994
  }
14809
14995
 
14996
+ // src/mcp/tools/fixes.ts
14997
+ async function runAtlasFixes(_args, host) {
14998
+ const resolved = await host.getWorkspace();
14999
+ if (!resolved) return noWorkspaceResult();
15000
+ const fixes = await loadLivingFixes(resolved.root);
15001
+ if (fixes.length === 0) {
15002
+ const activeFix = resolved.workspace.changes.find((change) => change.meta?.lane === "fix");
15003
+ return jsonResult({
15004
+ fixes: [],
15005
+ message: "No hay fixes vivos registrados.",
15006
+ action: activeFix ? `satlas archive ${activeFix.slug}` : "satlas new <slug> --lane fix"
15007
+ });
15008
+ }
15009
+ return jsonResult({
15010
+ fixes: fixes.map((fix) => ({
15011
+ slug: fix.slug,
15012
+ date: fix.date,
15013
+ result: fix.result,
15014
+ source: fix.source,
15015
+ domain: fix.domain,
15016
+ title: fix.title,
15017
+ covers: fix.covers,
15018
+ content: fix.content
15019
+ }))
15020
+ });
15021
+ }
15022
+
14810
15023
  // src/mcp/tools/glossary.ts
14811
- import path35 from "path";
15024
+ import path36 from "path";
14812
15025
  async function runAtlasGlossary(_args, host) {
14813
15026
  const resolved = await host.getWorkspace();
14814
15027
  if (!resolved) return noWorkspaceResult();
14815
15028
  const { root, config } = resolved;
14816
- const glossaryPath = path35.resolve(root, config.spec.glossary);
15029
+ const glossaryPath = path36.resolve(root, config.spec.glossary);
14817
15030
  const raw = await readTextIfExists(glossaryPath);
14818
15031
  if (raw === void 0) {
14819
15032
  return jsonResult({
@@ -14859,7 +15072,7 @@ async function runAtlasImpact(args, host) {
14859
15072
  }
14860
15073
 
14861
15074
  // src/evaluate.ts
14862
- import path36 from "path";
15075
+ import path37 from "path";
14863
15076
  function livingRequirementsMap2(specs) {
14864
15077
  const map = /* @__PURE__ */ new Map();
14865
15078
  for (const spec of specs) {
@@ -14868,7 +15081,7 @@ function livingRequirementsMap2(specs) {
14868
15081
  return map;
14869
15082
  }
14870
15083
  async function evaluateChange(workspace, config, change, approvals) {
14871
- const deltaPath = path36.join(change.dir, "spec.md");
15084
+ const deltaPath = path37.join(change.dir, "spec.md");
14872
15085
  const deltaContent = await readTextIfExists(deltaPath);
14873
15086
  const approval = verifyApproval(change, approvals, config, deltaContent ?? void 0);
14874
15087
  const living = livingRequirementsMap2(workspace.specs);
@@ -15044,7 +15257,8 @@ var HANDLERS = {
15044
15257
  atlas_validate: runAtlasValidate,
15045
15258
  atlas_trace: runAtlasTrace,
15046
15259
  atlas_impact: runAtlasImpact,
15047
- atlas_glossary: runAtlasGlossary
15260
+ atlas_glossary: runAtlasGlossary,
15261
+ atlas_fixes: runAtlasFixes
15048
15262
  };
15049
15263
  async function runToolByName(name, args, host) {
15050
15264
  const handler = HANDLERS[name];
@@ -15113,6 +15327,15 @@ function listTools() {
15113
15327
  properties: {},
15114
15328
  additionalProperties: false
15115
15329
  }
15330
+ },
15331
+ {
15332
+ name: "atlas_fixes",
15333
+ description: "Fixes vivos (carril express ya archivado): identidad, resultado de su evidencia, contenido y requisitos que declaran. Solo lectura.",
15334
+ inputSchema: {
15335
+ type: "object",
15336
+ properties: {},
15337
+ additionalProperties: false
15338
+ }
15116
15339
  }
15117
15340
  ];
15118
15341
  }
@@ -15130,8 +15353,8 @@ var WorkspaceCache = class {
15130
15353
  }
15131
15354
  };
15132
15355
  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")];
15356
+ const sddDir = path38.join(root, ".sdd");
15357
+ const markers = [sddDir, path38.join(sddDir, "config.yaml"), path38.join(sddDir, "changes"), path38.join(sddDir, "specs"), path38.join(sddDir, "approvals.yaml")];
15135
15358
  let stamp = 0;
15136
15359
  for (const marker of markers) {
15137
15360
  try {
@@ -15149,7 +15372,7 @@ async function resolveMcpWorkspace(cwd, cache) {
15149
15372
  const cached2 = cache.get(root);
15150
15373
  if (cached2 && cached2.mtimeMs === stamp) return cached2.value;
15151
15374
  const { workspace, config } = await loadWorkspace(root);
15152
- const approvals = await loadApprovals(path37.join(root, ".sdd"));
15375
+ const approvals = await loadApprovals(path38.join(root, ".sdd"));
15153
15376
  const value = { root, workspace, config, approvals: approvals.byArtifact };
15154
15377
  cache.set(root, stamp, value);
15155
15378
  return value;
@@ -15241,7 +15464,7 @@ async function runMcp(ctx) {
15241
15464
  }
15242
15465
 
15243
15466
  // src/commands/packs.ts
15244
- import path38 from "path";
15467
+ import path39 from "path";
15245
15468
  async function runPacks(ctx) {
15246
15469
  const { root, config, workspace } = await requireWorkspace(ctx);
15247
15470
  const slug = flagString(ctx.flags, "check");
@@ -15299,14 +15522,14 @@ async function runPacks(ctx) {
15299
15522
  data: {
15300
15523
  slug,
15301
15524
  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")
15525
+ analyzePath: path39.posix.join(".sdd", "changes", slug, "analyze.md")
15303
15526
  },
15304
15527
  text: lines
15305
15528
  };
15306
15529
  }
15307
15530
 
15308
15531
  // src/commands/new.ts
15309
- import path39 from "path";
15532
+ import path40 from "path";
15310
15533
  async function runNew(ctx) {
15311
15534
  const slug = ctx.positionals[0];
15312
15535
  if (!slug) {
@@ -15332,14 +15555,14 @@ async function runNew(ctx) {
15332
15555
  msg("new.title", ctx.language),
15333
15556
  "",
15334
15557
  msg("new.done", ctx.language),
15335
- ...result.files.map((f) => ` + ${path39.relative(ctx.cwd, f)}`),
15558
+ ...result.files.map((f) => ` + ${path40.relative(ctx.cwd, f)}`),
15336
15559
  "",
15337
15560
  `Siguiente: /satlas.specify ${result.slug} \u2014 escribe la especificaci\xF3n 100% funcional y de negocio.`
15338
15561
  ];
15339
15562
  return {
15340
15563
  exitCode: hasErrors2 ? 1 : 0,
15341
15564
  diagnostics: result.diagnostics,
15342
- data: { slug: result.slug, files: result.files.map((f) => path39.relative(ctx.cwd, f)) },
15565
+ data: { slug: result.slug, files: result.files.map((f) => path40.relative(ctx.cwd, f)) },
15343
15566
  text: lines
15344
15567
  };
15345
15568
  }
@@ -15374,7 +15597,7 @@ async function runNext(ctx) {
15374
15597
  }
15375
15598
 
15376
15599
  // src/commands/present.ts
15377
- import path40 from "path";
15600
+ import path41 from "path";
15378
15601
  async function runPresentCommand(ctx) {
15379
15602
  const { root } = await requireWorkspace(ctx);
15380
15603
  const slug = ctx.positionals[0];
@@ -15386,7 +15609,7 @@ async function runPresentCommand(ctx) {
15386
15609
  if (result.path) {
15387
15610
  lines.push("Propuesta generada");
15388
15611
  lines.push("");
15389
- lines.push(` archivo: ${path40.relative(ctx.cwd, result.path)}`);
15612
+ lines.push(` archivo: ${path41.relative(ctx.cwd, result.path)}`);
15390
15613
  if (result.hash) lines.push(` hash de la spec: ${shortHash(result.hash)}`);
15391
15614
  lines.push("");
15392
15615
  lines.push("\xC1brela en el navegador y comp\xE1rtela con el stakeholder.");
@@ -15396,7 +15619,7 @@ async function runPresentCommand(ctx) {
15396
15619
  return {
15397
15620
  exitCode: hasErrors2 ? 1 : 0,
15398
15621
  diagnostics: result.diagnostics,
15399
- data: result.path ? { path: path40.relative(ctx.cwd, result.path), hash: result.hash } : void 0,
15622
+ data: result.path ? { path: path41.relative(ctx.cwd, result.path), hash: result.hash } : void 0,
15400
15623
  text: lines
15401
15624
  };
15402
15625
  }
@@ -15505,6 +15728,17 @@ async function runStatus(ctx) {
15505
15728
  for (const spec of workspace.specs) {
15506
15729
  lines.push(` ${spec.domain} \u2014 ${spec.spec.requirements.length} requisitos`);
15507
15730
  }
15731
+ const fixes = await loadLivingFixes(root);
15732
+ lines.push("");
15733
+ lines.push(`${msg("status.fixes", ctx.language)}: ${fixes.length}`);
15734
+ if (fixes.length === 0) {
15735
+ lines.push(" (se llenan al archivar un fix)");
15736
+ } else {
15737
+ for (const fix of fixes) {
15738
+ const origin = fix.source === "archive" ? " (hist\xF3rico)" : "";
15739
+ lines.push(` ${fix.date || "\u2014"} ${(fix.domain ?? "\u2014").padEnd(10)} ${fix.slug} \u2014 ${fix.result}${origin}`);
15740
+ }
15741
+ }
15508
15742
  const diagnostics = [...workspace.diagnostics, ...advisory.diagnostics];
15509
15743
  const errors = diagnostics.filter((d) => d.severity === "error").length;
15510
15744
  return {
@@ -15513,6 +15747,14 @@ async function runStatus(ctx) {
15513
15747
  data: {
15514
15748
  changes,
15515
15749
  specs: workspace.specs.map((s) => ({ domain: s.domain, requirements: s.spec.requirements.length })),
15750
+ fixes: fixes.map((fix) => ({
15751
+ slug: fix.slug,
15752
+ date: fix.date,
15753
+ result: fix.result,
15754
+ source: fix.source,
15755
+ ...fix.domain !== void 0 ? { domain: fix.domain } : {},
15756
+ covers: fix.covers
15757
+ })),
15516
15758
  upgrade: {
15517
15759
  currentVersion: advisory.plan.currentVersion,
15518
15760
  pending: advisory.plan.pending.length,
@@ -15525,7 +15767,7 @@ async function runStatus(ctx) {
15525
15767
  }
15526
15768
 
15527
15769
  // src/commands/trace.ts
15528
- import path41 from "path";
15770
+ import path42 from "path";
15529
15771
  async function runTrace(ctx) {
15530
15772
  const { workspace, config } = await requireWorkspace(ctx);
15531
15773
  const slug = flagString(ctx.flags, "change");
@@ -15545,7 +15787,7 @@ async function runTrace(ctx) {
15545
15787
  data.push({ slug: change.slug, nodes: result.graph.nodes.length, edges: result.graph.edges.length, summary: result.summary, findings: result.findings });
15546
15788
  lines.push(` ${change.slug}: ${result.graph.nodes.length} nodos, ${result.graph.edges.length} aristas, ${errors} errores, ${result.summary.warnings} avisos`);
15547
15789
  for (const finding of result.findings) {
15548
- const location = finding.path ? ` (${path41.relative(ctx.cwd, finding.path)}${finding.line ? `:${finding.line}` : ""})` : "";
15790
+ const location = finding.path ? ` (${path42.relative(ctx.cwd, finding.path)}${finding.line ? `:${finding.line}` : ""})` : "";
15549
15791
  lines.push(` ${finding.severity.toUpperCase()} ${finding.code} \u2014 ${finding.message}${location}`);
15550
15792
  }
15551
15793
  }
@@ -15555,7 +15797,7 @@ async function runTrace(ctx) {
15555
15797
  }
15556
15798
 
15557
15799
  // src/commands/upgrade.ts
15558
- import path42 from "path";
15800
+ import path43 from "path";
15559
15801
  async function runUpgrade(ctx) {
15560
15802
  const { root } = await requireWorkspace(ctx);
15561
15803
  const apply = flagBool(ctx.flags, "apply");
@@ -15609,7 +15851,7 @@ function renderPreview(plan, root, ctx) {
15609
15851
  }
15610
15852
  for (const item of plan.unreadable) {
15611
15853
  lines.push(` Ilegible: "${item.artifact}" \u2014 ${item.reason}`);
15612
- lines.push(` \u21B3 ${path42.relative(ctx.cwd, item.path)}`);
15854
+ lines.push(` \u21B3 ${path43.relative(ctx.cwd, item.path)}`);
15613
15855
  }
15614
15856
  return { exitCode: 0, diagnostics: [], data: summarizable(plan), text: lines };
15615
15857
  }
@@ -15672,7 +15914,7 @@ function renderRollback(report, ctx) {
15672
15914
  }
15673
15915
 
15674
15916
  // src/commands/validate.ts
15675
- import path43 from "path";
15917
+ import path44 from "path";
15676
15918
  async function runValidate(ctx) {
15677
15919
  const { root, workspace, config, approvals } = await requireWorkspace(ctx);
15678
15920
  const slug = flagString(ctx.flags, "change");
@@ -15699,7 +15941,7 @@ async function runValidate(ctx) {
15699
15941
  for (const finding of evaluation.lintFindings) {
15700
15942
  lines.push(` ${finding.severity.toUpperCase()} ${finding.code}${finding.line ? `:${finding.line}` : ""} \u2014 ${finding.message}`);
15701
15943
  }
15702
- lines.push(` archivo: ${path43.relative(ctx.cwd, path43.join(change.dir, "spec.md"))}`);
15944
+ lines.push(` archivo: ${path44.relative(ctx.cwd, path44.join(change.dir, "spec.md"))}`);
15703
15945
  }
15704
15946
  if (targets.length === 0) lines.push("Sin cambios activos.");
15705
15947
  if (advisory && advisory.plan.pending.length > 0) {
@@ -15727,7 +15969,7 @@ async function runValidate(ctx) {
15727
15969
  }
15728
15970
 
15729
15971
  // src/commands/verify.ts
15730
- import path44 from "path";
15972
+ import path45 from "path";
15731
15973
  async function runVerify(ctx) {
15732
15974
  const { root, workspace } = await requireWorkspace(ctx);
15733
15975
  const slug = ctx.positionals[0];
@@ -15770,7 +16012,7 @@ async function runVerify(ctx) {
15770
16012
  return { exitCode: 2, diagnostics: [{ code: "ATLAS-VERIFY-001", severity: "error", message: "Falta --by <nombre> (o define meta.owner)" }] };
15771
16013
  }
15772
16014
  const profilesDir = await resolveProfilesDir();
15773
- const profile = await loadActiveProfile(path44.join(root, ".sdd"), [profilesDir ?? "", path44.join(root, ".sdd", "profiles", "custom")].filter(Boolean));
16015
+ const profile = await loadActiveProfile(path45.join(root, ".sdd"), [profilesDir ?? "", path45.join(root, ".sdd", "profiles", "custom")].filter(Boolean));
15774
16016
  const record = await recordEvidence({
15775
16017
  root,
15776
16018
  slug,
@@ -15792,7 +16034,7 @@ async function runVerify(ctx) {
15792
16034
  if (record.evidence.command) lines.push(` comando: ${record.evidence.command}`);
15793
16035
  lines.push(` resultado: ${record.evidence.result}`);
15794
16036
  if (record.evidence.outputHash) lines.push(` hash: ${record.evidence.outputHash}`);
15795
- lines.push(` archivo: ${path44.relative(ctx.cwd, record.path)}`);
16037
+ lines.push(` archivo: ${path45.relative(ctx.cwd, record.path)}`);
15796
16038
  if (record.stdout) {
15797
16039
  lines.push("");
15798
16040
  lines.push(" salida (\xFAltimas l\xEDneas):");
@@ -15807,7 +16049,7 @@ async function runVerify(ctx) {
15807
16049
  return {
15808
16050
  exitCode: record.exitCode === 0 ? 0 : 1,
15809
16051
  diagnostics: record.diagnostics,
15810
- data: { evidence: record.evidence, path: path44.relative(ctx.cwd, record.path), exitCode: record.exitCode },
16052
+ data: { evidence: record.evidence, path: path45.relative(ctx.cwd, record.path), exitCode: record.exitCode },
15811
16053
  text: lines
15812
16054
  };
15813
16055
  }
@@ -15907,7 +16149,7 @@ async function requireWorkspace(ctx) {
15907
16149
  throw new CliError("ATLAS-WS-001", msg("cli.noWorkspace", ctx.language), 2);
15908
16150
  }
15909
16151
  const { workspace, config } = await loadWorkspace(root);
15910
- const approvals = await loadApprovals(path45.join(root, ".sdd"));
16152
+ const approvals = await loadApprovals(path46.join(root, ".sdd"));
15911
16153
  return { root, workspace, config, approvals: approvals.byArtifact };
15912
16154
  }
15913
16155
  var CliError = class extends Error {
@@ -15974,7 +16216,7 @@ function printResult(command, result, json2, language) {
15974
16216
  }
15975
16217
  const lines = [...result.text ?? []];
15976
16218
  for (const d of result.diagnostics) {
15977
- const location = d.path ? ` ${path45.relative(process.cwd(), d.path)}${d.line ? `:${d.line}` : ""}` : "";
16219
+ const location = d.path ? ` ${path46.relative(process.cwd(), d.path)}${d.line ? `:${d.line}` : ""}` : "";
15978
16220
  lines.push(`${d.severity === "error" ? "ERROR" : d.severity === "warning" ? "AVISO" : "NOTA"} ${d.code}${location} \u2014 ${d.message}`);
15979
16221
  if (d.suggestion) lines.push(` \u21B3 ${d.suggestion}`);
15980
16222
  }