specatlas 0.1.2 → 0.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/bin.js CHANGED
@@ -9681,7 +9681,7 @@ function checkTrace(input) {
9681
9681
  }
9682
9682
  }
9683
9683
  for (const sc of deltaScenarios) {
9684
- if (!coversExpanded.has(sc)) {
9684
+ if (taskById.size > 0 && !coversExpanded.has(sc)) {
9685
9685
  findings.push(
9686
9686
  diag("TRACE-002", "error", `El escenario ${sc} no est\xE1 cubierto por ninguna tarea`, {
9687
9687
  path: change.tasks?.path ?? change.delta?.path,
@@ -11845,7 +11845,7 @@ async function generatePresentation(opts) {
11845
11845
  language,
11846
11846
  labels,
11847
11847
  title: change.meta?.title ?? change.slug,
11848
- domain: change.meta?.domain ?? "\xE2\u20AC\u201D",
11848
+ domain: change.meta?.domain ?? "\u2014",
11849
11849
  lane: change.meta?.lane ?? config.lanes.default,
11850
11850
  project: config.project.name,
11851
11851
  version: change.delta.sectionsFound.length,
@@ -11872,7 +11872,7 @@ async function copyMockups(root, change, destDir) {
11872
11872
  for (const screen of manifest.screens) {
11873
11873
  const src = path12.join(dir, screen.file);
11874
11874
  if (!await exists(src)) {
11875
- diagnostics.push(diag("ATLAS-PRESENT-002", "warning", `El mockup ${screen.file} no existe y no se incluir\xC3\xA1`, { path: src }));
11875
+ diagnostics.push(diag("ATLAS-PRESENT-002", "warning", `El mockup ${screen.file} no existe y no se incluir\xE1`, { path: src }));
11876
11876
  continue;
11877
11877
  }
11878
11878
  await copyFile(src, path12.join(destDir, screen.file));
@@ -11923,15 +11923,15 @@ function labelsFor(language) {
11923
11923
  return {
11924
11924
  kicker: "Propuesta",
11925
11925
  summary: "Resumen de negocio",
11926
- criteria: "Criterios de aceptaci\xC3\xB3n",
11926
+ criteria: "Criterios de aceptaci\xF3n",
11927
11927
  requirements: "Requisitos y escenarios",
11928
11928
  mockups: "Mockups",
11929
- approve: "Aprobaci\xC3\xB3n",
11929
+ approve: "Aprobaci\xF3n",
11930
11930
  approveText: "Esta propuesta se aprueba firmando la spec (hash + autor). Cualquier cambio posterior invalida la firma.",
11931
11931
  command: "Comando",
11932
11932
  hash: "Hash",
11933
11933
  generated: "Generado",
11934
- noProposal: "La propuesta est\xC3\xA1 vac\xC3\xADa.",
11934
+ noProposal: "La propuesta est\xE1 vac\xEDa.",
11935
11935
  added: "agregado",
11936
11936
  modified: "modificado",
11937
11937
  scenario: "Escenario",
@@ -11986,13 +11986,13 @@ footer { color: var(--muted); font-size: 13px; text-align:center; }
11986
11986
  function page(input) {
11987
11987
  const l = input.labels;
11988
11988
  const reqHtml = input.requirements.map(({ req, kind }) => {
11989
- const rules = req.rules.map((r) => `<li><code>${esc(r.id)}</code> \xE2\u20AC\u201D ${inline(r.text)}</li>`).join("");
11989
+ const rules = req.rules.map((r) => `<li><code>${esc(r.id)}</code> \u2014 ${inline(r.text)}</li>`).join("");
11990
11990
  const scenarios = req.scenarios.map((s) => {
11991
11991
  const when = s.when.map((w) => `<div class="when">CUANDO ${inline(w)}</div>`).join("");
11992
11992
  const then = s.then.map((t) => `<div class="then">ENTONCES ${inline(t)}</div>`).join("");
11993
- return `<div class="scenario"><h4>${esc(s.id)} \xE2\u20AC\u201D ${esc(s.title)}</h4>${when}${then}</div>`;
11993
+ return `<div class="scenario"><h4>${esc(s.id)} \u2014 ${esc(s.title)}</h4>${when}${then}</div>`;
11994
11994
  }).join("");
11995
- return `<div class="req"><h3>${esc(req.id)} \xE2\u20AC\u201D ${esc(req.title)}<span class="badge">${kind === "added" ? l.added : l.modified}</span></h3><p>${inline(req.prose)}</p>${rules ? `<ul class="rules">${rules}</ul>` : ""}${scenarios}</div>`;
11995
+ return `<div class="req"><h3>${esc(req.id)} \u2014 ${esc(req.title)}<span class="badge">${kind === "added" ? l.added : l.modified}</span></h3><p>${inline(req.prose)}</p>${rules ? `<ul class="rules">${rules}</ul>` : ""}${scenarios}</div>`;
11996
11996
  }).join("");
11997
11997
  const criteriaRows = input.requirements.flatMap(({ req }) => req.scenarios).map((s) => {
11998
11998
  const status = input.evidence.get(s.id);
@@ -12001,7 +12001,7 @@ function page(input) {
12001
12001
  return `<tr><td><code>${esc(s.id)}</code></td><td>${esc(s.title)}</td><td><span class="pill ${cls}">${esc(label)}</span></td></tr>`;
12002
12002
  }).join("");
12003
12003
  const mockupHtml = input.mockups.length === 0 ? `<p><em>${l.noMockups}</em></p>` : `<div class="mockups">${input.mockups.map(
12004
- (m) => `<div class="mockup ${m.platform === "mobile" ? "phone" : ""}"><header>${esc(m.title)} \xE2\u20AC\u201D ${esc(m.file)}</header><iframe src="${esc(m.file)}" loading="lazy" title="${esc(m.title)}"></iframe></div>`
12004
+ (m) => `<div class="mockup ${m.platform === "mobile" ? "phone" : ""}"><header>${esc(m.title)} \u2014 ${esc(m.file)}</header><iframe src="${esc(m.file)}" loading="lazy" title="${esc(m.title)}"></iframe></div>`
12005
12005
  ).join("")}</div>`;
12006
12006
  const shotsHtml = input.screenshots.length === 0 ? "" : `<h3>${l.screenshots}</h3><div class="grid-shots">${input.screenshots.map((s) => `<img src="${esc(s)}" alt="${esc(s)}" loading="lazy">`).join("")}</div>`;
12007
12007
  return `<!doctype html>
@@ -12009,15 +12009,15 @@ function page(input) {
12009
12009
  <head>
12010
12010
  <meta charset="utf-8">
12011
12011
  <meta name="viewport" content="width=device-width, initial-scale=1">
12012
- <title>${esc(l.kicker)} \xE2\u20AC\u201D ${esc(input.title)}</title>
12012
+ <title>${esc(l.kicker)} \u2014 ${esc(input.title)}</title>
12013
12013
  <style>${CSS}</style>
12014
12014
  </head>
12015
12015
  <body>
12016
12016
  <div class="wrap">
12017
12017
  <header class="hero">
12018
- <p class="kicker">${esc(l.kicker)} \xC2\xB7 ${esc(input.domain)} \xC2\xB7 ${esc(input.lane)}</p>
12018
+ <p class="kicker">${esc(l.kicker)} \xB7 ${esc(input.domain)} \xB7 ${esc(input.lane)}</p>
12019
12019
  <h1>${esc(input.title)}</h1>
12020
- <p class="meta">${esc(input.project)} \xC2\xB7 ${esc(l.hash)} ${esc(shortHash(input.hash))} \xC2\xB7 ${esc(l.generated)} ${esc(input.generatedAt)}</p>
12020
+ <p class="meta">${esc(input.project)} \xB7 ${esc(l.hash)} ${esc(shortHash(input.hash))} \xB7 ${esc(l.generated)} ${esc(input.generatedAt)}</p>
12021
12021
  </header>
12022
12022
 
12023
12023
  <section>
@@ -12047,7 +12047,7 @@ function page(input) {
12047
12047
  <p>${esc(l.command)}: <code>${esc(input.approveHint)}</code></p>
12048
12048
  </section>
12049
12049
 
12050
- <footer>SpecAtlas \xC2\xB7 ${esc(input.project)} \xC2\xB7 ${esc(input.generatedAt)}</footer>
12050
+ <footer>SpecAtlas \xB7 ${esc(input.project)} \xB7 ${esc(input.generatedAt)}</footer>
12051
12051
  </div>
12052
12052
  </body>
12053
12053
  </html>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specatlas",
3
- "version": "0.1.2",
3
+ "version": "0.1.4",
4
4
  "description": "SpecAtlas: kernel determinista de Spec-Driven Development (CLI)",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -40,7 +40,7 @@
40
40
  },
41
41
  "devDependencies": {
42
42
  "@specatlas/adapters": "0.1.1",
43
- "@specatlas/core": "0.1.0"
43
+ "@specatlas/core": "0.1.2"
44
44
  },
45
45
  "scripts": {
46
46
  "typecheck": "tsc --noEmit",
@@ -7,6 +7,7 @@ description: >-
7
7
  requires: []
8
8
  produces:
9
9
  - changes/<slug>/spec.md
10
+ - changes/<slug>/proposal.md
10
11
  arguments: true
11
12
  agent:
12
13
  mode: primary
@@ -32,20 +33,22 @@ El contenido se escribe en **{{LANGUAGE_NAME}}**.
32
33
  - Incluye siempre escenarios de error, vacío, sin permiso y límites.
33
34
  - `MODIFIED` copia el bloque **completo** del requisito tal como está en la spec viva y lo edita.
34
35
  - `REMOVED` declara `- Motivo:` y `- Migración:`.
35
- 5. Verifica con el CLI y corrige hasta que no haya errores:
36
+ 5. Completa `.sdd/changes/{{SLUG}}/proposal.md` en lenguaje de negocio (sin tecnología y sin dejar los textos entre paréntesis de la plantilla): **Por qué** (problema u oportunidad), **Qué cambia** (alcance funcional), **Fuera de alcance** (lo que no se hará) y **Cómo se mide el éxito** (indicadores observables).
37
+ 6. Verifica con el CLI y corrige hasta que no haya errores:
36
38
 
37
39
  ```
38
40
  satlas validate --change {{SLUG}}
39
41
  ```
40
42
 
41
- 6. Reporta: número de requisitos y escenarios, supuestos, y las preguntas que quedaron abiertas.
43
+ 7. Reporta: número de requisitos y escenarios, supuestos, y las preguntas que quedaron abiertas.
42
44
 
43
45
  ## Prohibido
44
46
 
45
47
  - Nombres de tablas, campos, endpoints, clases, archivos, frameworks o librerías.
46
48
  - Adjetivos vagos ("rápido", "fácil", "varios", "robusto") sin un valor medible.
47
49
  - Renumerar ids existentes o reescribir un requisito aprobado sin pasar por `MODIFIED`/`REMOVED`.
50
+ - Dejar `proposal.md` con la plantilla sin completar.
48
51
 
49
52
  ## Salida
50
53
 
51
- `changes/{{SLUG}}/spec.md` validado, más un resumen para el usuario y la indicación de que la spec debe aprobarse (`satlas approve`) antes de planificar.
54
+ `changes/{{SLUG}}/spec.md` validado y `changes/{{SLUG}}/proposal.md` completo, más un resumen para el usuario y la indicación de que la spec debe aprobarse (`satlas approve`) antes de planificar.