@specatlas/core 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1655,4 +1655,32 @@ interface DoctorReport {
1655
1655
  declare function runDoctor(root: string): Promise<DoctorReport>;
1656
1656
  declare function specHashOf(filePath: string): Promise<string | undefined>;
1657
1657
 
1658
- export { type AdoptDomain, type AdoptOptions, type AdoptResult, type AgingBucket, type AnalyzeOptions, type AnalyzePackSummary, type AnalyzeResult, type Approval, type ApprovalStatus, type ApprovalsFile, type ApprovalsIndex, type ApproveFromGithubOptions, type ApproveFromGithubResult, type ArchiveOptions, type ArchiveResult, type AtlasConfig, type AttentionItem, BLOCK_HEAD_RE, BUILTIN_PACKS, type BlockWavePlan, CONFIG_FILE, CORE_VERSION, type CaptureResult, type Change, type ChangeMeta, type ChangeMetrics, type ChangeState, type Delta, type DeltaOp, type DeriveInput, type DerivedState, type DetectionResult, type Diagnostic, type DoctorReport, type Domain, type Evidence, type EvidenceMethod, type EvidenceResult, type ExecOptions, type ExecResult, type FoldOutcome, type FrontmatterResult, type GhRunner, type GitHubRepo, type InitOptions, type InitResult, type IssueBodyInput, type Lane, type Language, type LinkedIssue, type LintOptions, type LoadedConfig, type MockupCheckResult, type MockupManifest, type MockupPlan, type MockupPlanScreen, type MockupScreen, type NewChangeOptions, type NewChangeResult, type NextAction, type Override, type Pack, type PackCheck, type PackCheckResult, type PackCheckType, type PackEvaluation, type PresentOptions, type PresentResult, type ProfileMatch, REQ_HEAD_RE, REQ_ID_RE, RULE_ID_RE, RULE_RE, RUN_EVENT_TYPES, type RecordEvidenceOptions, type RecordEvidenceResult, type Rename, type Requirement, type RiskLevel, type Rule, type RunEvent, type RunEventType, type RunRecord, type RunState, type RunStatus, SCENARIO_HEAD_RE, SCENARIO_ID_RE, SDD_DIR, SLUG_RE, type Scenario, type Severity, type SignApprovalOptions, type SignApprovalResult, type SpecFile, type SpecRef, type StackProfile, type SyncGithubOptions, type SyncGithubResult, TASK_ID_RE, TASK_LINE_RE, type Task, type TaskBlock, type TasksFile, type TemplateSet, type TraceEdge, type TraceFinding, type TraceGraph, type TraceInput, type TraceNode, type TraceResult, type VerifyFile, type WalkEntry, type WavePlan, type Workspace, type WorkspaceMetrics, adoptWorkspace, appendRunEvent, approvalsSchema, approveFromGithub, archiveChange, artifactHash, atlasConfigSchema, buildTraceGraph, canonicalizeMarkdown, captureMockups, changeMarker, changeMetaSchema, changeMetaYaml, checkMockups, checkTrace, collectMetrics, commentIssue, compareTaskIds, computeInputsHash, configToYaml, copyFile, countBySeverity, createChange, createIssue, createRun, defaultConfig, deriveState, detectProfiles, detectRepo, detectionToYaml, diag, editIssue, emitFrontmatter, ensureDir, ensureSddDirs, esc, evaluatePacks, evidenceSummary, exists, findLinkedIssue, findWorkspaceRoot, firstToken, foldDelta, generatePresentation, generateRunId, getNumber, getString, ghAuthStatus, ghAvailable, hasErrors, hasShellMetacharacters, indexMarkdown, initWorkspace, inline, isCommandAllowed, isDirectory, issueBody, issueLabels, laneOrDefault, lintDelta, lintMockupHtml, lintMockupManifest, lintRequirement, lintSpec, lintTasks, lintVerify, listChangeSlugs, listDir, listDirs, listRuns, loadActiveProfile, loadApprovals, loadChange, loadConfig, loadDetectedBest, loadProfileFile, loadProfilesFromDir, loadProjectPacks, loadSpecs, loadWorkspace, matchGlob, mockupManifestSchema, mockupsDir, packFindings, parseApprovals, parseChangeMeta, parseConfig, parseDelta, parseFrontmatter, parseGitHubRemote, parseIssueUrl, parseRequirementBlocks, parseSpecFile, parseTasksFile, parseVerifyFile, patchChangeMeta, planBlock, planMockups, planWaves, profileSchema, readMockupManifest, readRun, readText, readTextIfExists, recordEvidence, regenerateIndex, renderMarkdown, renderRequirement, renderTemplate, resolvePacks, runAnalyze, runDoctor, runProcess, sha256, shortHash, signApproval, specHashOf, splitCommand, stateLabel, syncGithubIssue, templatesFor, toPosix, tokensFile, updateMockupScreenshots, updateRunStatus, verifyApproval, walkFiles, writeConfig, writeMockupManifest, writeMockupPlan, writeText };
1658
+ interface CiCheck {
1659
+ name: string;
1660
+ errors: number;
1661
+ warnings: number;
1662
+ }
1663
+ interface CiExtraCheck {
1664
+ name: string;
1665
+ diagnostics: Diagnostic[];
1666
+ }
1667
+ interface CiResult {
1668
+ checks: CiCheck[];
1669
+ diagnostics: Diagnostic[];
1670
+ errors: number;
1671
+ warnings: number;
1672
+ failed: boolean;
1673
+ }
1674
+ interface CiOptions {
1675
+ root: string;
1676
+ strict?: boolean;
1677
+ extra?: CiExtraCheck[];
1678
+ }
1679
+ declare function livingRequirementsMap(specs: Array<{
1680
+ spec: {
1681
+ requirements: Requirement[];
1682
+ };
1683
+ }>): Map<string, Requirement>;
1684
+ declare function runCiGate(opts: CiOptions): Promise<CiResult>;
1685
+
1686
+ export { type AdoptDomain, type AdoptOptions, type AdoptResult, type AgingBucket, type AnalyzeOptions, type AnalyzePackSummary, type AnalyzeResult, type Approval, type ApprovalStatus, type ApprovalsFile, type ApprovalsIndex, type ApproveFromGithubOptions, type ApproveFromGithubResult, type ArchiveOptions, type ArchiveResult, type AtlasConfig, type AttentionItem, BLOCK_HEAD_RE, BUILTIN_PACKS, type BlockWavePlan, CONFIG_FILE, CORE_VERSION, type CaptureResult, type Change, type ChangeMeta, type ChangeMetrics, type ChangeState, type CiCheck, type CiExtraCheck, type CiOptions, type CiResult, type Delta, type DeltaOp, type DeriveInput, type DerivedState, type DetectionResult, type Diagnostic, type DoctorReport, type Domain, type Evidence, type EvidenceMethod, type EvidenceResult, type ExecOptions, type ExecResult, type FoldOutcome, type FrontmatterResult, type GhRunner, type GitHubRepo, type InitOptions, type InitResult, type IssueBodyInput, type Lane, type Language, type LinkedIssue, type LintOptions, type LoadedConfig, type MockupCheckResult, type MockupManifest, type MockupPlan, type MockupPlanScreen, type MockupScreen, type NewChangeOptions, type NewChangeResult, type NextAction, type Override, type Pack, type PackCheck, type PackCheckResult, type PackCheckType, type PackEvaluation, type PresentOptions, type PresentResult, type ProfileMatch, REQ_HEAD_RE, REQ_ID_RE, RULE_ID_RE, RULE_RE, RUN_EVENT_TYPES, type RecordEvidenceOptions, type RecordEvidenceResult, type Rename, type Requirement, type RiskLevel, type Rule, type RunEvent, type RunEventType, type RunRecord, type RunState, type RunStatus, SCENARIO_HEAD_RE, SCENARIO_ID_RE, SDD_DIR, SLUG_RE, type Scenario, type Severity, type SignApprovalOptions, type SignApprovalResult, type SpecFile, type SpecRef, type StackProfile, type SyncGithubOptions, type SyncGithubResult, TASK_ID_RE, TASK_LINE_RE, type Task, type TaskBlock, type TasksFile, type TemplateSet, type TraceEdge, type TraceFinding, type TraceGraph, type TraceInput, type TraceNode, type TraceResult, type VerifyFile, type WalkEntry, type WavePlan, type Workspace, type WorkspaceMetrics, adoptWorkspace, appendRunEvent, approvalsSchema, approveFromGithub, archiveChange, artifactHash, atlasConfigSchema, buildTraceGraph, canonicalizeMarkdown, captureMockups, changeMarker, changeMetaSchema, changeMetaYaml, checkMockups, checkTrace, collectMetrics, commentIssue, compareTaskIds, computeInputsHash, configToYaml, copyFile, countBySeverity, createChange, createIssue, createRun, defaultConfig, deriveState, detectProfiles, detectRepo, detectionToYaml, diag, editIssue, emitFrontmatter, ensureDir, ensureSddDirs, esc, evaluatePacks, evidenceSummary, exists, findLinkedIssue, findWorkspaceRoot, firstToken, foldDelta, generatePresentation, generateRunId, getNumber, getString, ghAuthStatus, ghAvailable, hasErrors, hasShellMetacharacters, indexMarkdown, initWorkspace, inline, isCommandAllowed, isDirectory, issueBody, issueLabels, laneOrDefault, lintDelta, lintMockupHtml, lintMockupManifest, lintRequirement, lintSpec, lintTasks, lintVerify, listChangeSlugs, listDir, listDirs, listRuns, livingRequirementsMap, loadActiveProfile, loadApprovals, loadChange, loadConfig, loadDetectedBest, loadProfileFile, loadProfilesFromDir, loadProjectPacks, loadSpecs, loadWorkspace, matchGlob, mockupManifestSchema, mockupsDir, packFindings, parseApprovals, parseChangeMeta, parseConfig, parseDelta, parseFrontmatter, parseGitHubRemote, parseIssueUrl, parseRequirementBlocks, parseSpecFile, parseTasksFile, parseVerifyFile, patchChangeMeta, planBlock, planMockups, planWaves, profileSchema, readMockupManifest, readRun, readText, readTextIfExists, recordEvidence, regenerateIndex, renderMarkdown, renderRequirement, renderTemplate, resolvePacks, runAnalyze, runCiGate, runDoctor, runProcess, sha256, shortHash, signApproval, specHashOf, splitCommand, stateLabel, syncGithubIssue, templatesFor, toPosix, tokensFile, updateMockupScreenshots, updateRunStatus, verifyApproval, walkFiles, writeConfig, writeMockupManifest, writeMockupPlan, writeText };
package/dist/index.js CHANGED
@@ -645,14 +645,14 @@ function wordBoundary(text, term) {
645
645
  const re = new RegExp(`(?<![\\p{L}\\p{N}])${escaped}(?![\\p{L}\\p{N}])`, "iu");
646
646
  return re.test(text);
647
647
  }
648
- function lintText(text, code, terms, label, path18, line) {
648
+ function lintText(text, code, terms, label, path19, line) {
649
649
  const out = [];
650
650
  const lower = text.toLowerCase();
651
651
  for (const term of terms) {
652
652
  if (wordBoundary(lower, term)) {
653
653
  out.push(
654
654
  diag(code, "error", `${label}: "${term}"`, {
655
- path: path18,
655
+ path: path19,
656
656
  line,
657
657
  suggestion: "La especificaci\xF3n es funcional y de negocio: describe comportamiento, no tecnolog\xEDa ni adjetivos vagos"
658
658
  })
@@ -661,7 +661,7 @@ function lintText(text, code, terms, label, path18, line) {
661
661
  }
662
662
  return out;
663
663
  }
664
- function lintRequirement(req, path18, opts = {}) {
664
+ function lintRequirement(req, path19, opts = {}) {
665
665
  const out = [];
666
666
  const vague = opts.language === "en" ? VAGUE_EN : VAGUE_ES;
667
667
  const tech = opts.language === "en" ? TECH_EN : TECH_ES;
@@ -672,32 +672,32 @@ function lintRequirement(req, path18, opts = {}) {
672
672
  ...req.scenarios.flatMap((s) => [...s.when.map((w) => ({ text: w, line: s.line })), ...s.then.map((t) => ({ text: t, line: s.line }))])
673
673
  ];
674
674
  for (const part of parts) {
675
- out.push(...lintText(part.text, "LINT-BIZ-002", vague, "Palabra vaga en la especificaci\xF3n", path18, part.line));
675
+ out.push(...lintText(part.text, "LINT-BIZ-002", vague, "Palabra vaga en la especificaci\xF3n", path19, part.line));
676
676
  if (opts.businessOnly !== false) {
677
- out.push(...lintText(part.text, "LINT-BIZ-001", tech, "Jerga t\xE9cnica en la especificaci\xF3n de negocio", path18, part.line));
677
+ out.push(...lintText(part.text, "LINT-BIZ-001", tech, "Jerga t\xE9cnica en la especificaci\xF3n de negocio", path19, part.line));
678
678
  }
679
679
  }
680
680
  if (req.scenarios.length === 0) {
681
- out.push(diag("TRACE-001", "error", `El requisito ${req.id} no tiene ning\xFAn escenario`, { path: path18, line: req.line, suggestion: "A\xF1ade al menos un escenario CUANDO/ENTONCES" }));
681
+ out.push(diag("TRACE-001", "error", `El requisito ${req.id} no tiene ning\xFAn escenario`, { path: path19, line: req.line, suggestion: "A\xF1ade al menos un escenario CUANDO/ENTONCES" }));
682
682
  }
683
683
  return out;
684
684
  }
685
- function lintDelta(delta, livingRequirements, path18, opts = {}) {
685
+ function lintDelta(delta, livingRequirements, path19, opts = {}) {
686
686
  const out = [...delta.diagnostics];
687
687
  for (const req of [...delta.added, ...delta.modified]) {
688
- out.push(...lintRequirement(req, path18, opts));
688
+ out.push(...lintRequirement(req, path19, opts));
689
689
  }
690
690
  for (const req of delta.modified) {
691
691
  const living = livingRequirements.get(req.id);
692
692
  if (!living) {
693
- out.push(diag("TRACE-007", "error", `MODIFIED ${req.id} no existe en la spec viva; usa ADDED`, { path: path18, line: req.line }));
693
+ out.push(diag("TRACE-007", "error", `MODIFIED ${req.id} no existe en la spec viva; usa ADDED`, { path: path19, line: req.line }));
694
694
  continue;
695
695
  }
696
696
  for (const existing of living.scenarios) {
697
697
  if (!req.scenarios.some((s) => s.id === existing.id)) {
698
698
  out.push(
699
699
  diag("TRACE-007", "error", `MODIFIED ${req.id} pierde el escenario ${existing.id}: copia el bloque completo`, {
700
- path: path18,
700
+ path: path19,
701
701
  line: req.line,
702
702
  suggestion: "Copia el bloque completo de la spec viva y ed\xEDtalo; para quitarlo, decl\xE1ralo en REMOVED"
703
703
  })
@@ -708,15 +708,15 @@ function lintDelta(delta, livingRequirements, path18, opts = {}) {
708
708
  for (const req of delta.removed) {
709
709
  const living = livingRequirements.get(req.id);
710
710
  if (!living) {
711
- out.push(diag("TRACE-007", "error", `REMOVED ${req.id} no existe en la spec viva`, { path: path18, line: req.line }));
711
+ out.push(diag("TRACE-007", "error", `REMOVED ${req.id} no existe en la spec viva`, { path: path19, line: req.line }));
712
712
  }
713
713
  }
714
714
  for (const rename of delta.renamed) {
715
715
  if (rename.from.id !== rename.to.id) {
716
- out.push(diag("LINT-DLT-003", "error", `RENAMED cambia el id (${rename.from.id} \u2192 ${rename.to.id}); los ids son inmutables`, { path: path18, line: rename.line }));
716
+ out.push(diag("LINT-DLT-003", "error", `RENAMED cambia el id (${rename.from.id} \u2192 ${rename.to.id}); los ids son inmutables`, { path: path19, line: rename.line }));
717
717
  }
718
718
  if (!livingRequirements.has(rename.from.id)) {
719
- out.push(diag("TRACE-007", "error", `RENAMED ${rename.from.id} no existe en la spec viva`, { path: path18, line: rename.line }));
719
+ out.push(diag("TRACE-007", "error", `RENAMED ${rename.from.id} no existe en la spec viva`, { path: path19, line: rename.line }));
720
720
  }
721
721
  }
722
722
  return out;
@@ -3864,6 +3864,75 @@ async function specHashOf(filePath) {
3864
3864
  if (content === void 0) return void 0;
3865
3865
  return artifactHash(content);
3866
3866
  }
3867
+
3868
+ // src/gate.ts
3869
+ import path18 from "path";
3870
+ var UI_DOMAINS2 = /* @__PURE__ */ new Set(["frontend", "mobile", "fullstack"]);
3871
+ function count(name, diagnostics) {
3872
+ return {
3873
+ name,
3874
+ errors: diagnostics.filter((d) => d.severity === "error").length,
3875
+ warnings: diagnostics.filter((d) => d.severity === "warning").length
3876
+ };
3877
+ }
3878
+ function livingRequirementsMap(specs) {
3879
+ const map = /* @__PURE__ */ new Map();
3880
+ for (const spec of specs) {
3881
+ for (const requirement of spec.spec.requirements) map.set(requirement.id, requirement);
3882
+ }
3883
+ return map;
3884
+ }
3885
+ async function runCiGate(opts) {
3886
+ const root = path18.resolve(opts.root);
3887
+ const { workspace, config } = await loadWorkspace(root);
3888
+ const diagnostics = [];
3889
+ const checks = [];
3890
+ const specFindings = workspace.specs.flatMap((spec) => lintSpec(spec.spec, { language: config.spec.language, businessOnly: config.spec.business_only }));
3891
+ diagnostics.push(...specFindings);
3892
+ checks.push(count("specs vivas", specFindings));
3893
+ const living = livingRequirementsMap(workspace.specs);
3894
+ let changesErrors = 0;
3895
+ let changesWarnings = 0;
3896
+ for (const change of workspace.changes) {
3897
+ const lintFindings = change.delta ? lintDelta(change.delta, living, path18.join(change.dir, "spec.md"), { language: config.spec.language }) : [];
3898
+ const trace = checkTrace({
3899
+ specs: workspace.specs,
3900
+ change,
3901
+ requireEvidence: config.gates.verify.mode !== "off" && config.gates.verify.require_evidence
3902
+ });
3903
+ const changeDiags = [...lintFindings, ...trace.findings];
3904
+ if (change.tasks && change.tasks.counts.total > 0) {
3905
+ const plan = planWaves(change.tasks, { maxParallel: config.waves.max_parallel });
3906
+ changeDiags.push(...plan.blocks.flatMap((block) => block.diagnostics));
3907
+ }
3908
+ if (UI_DOMAINS2.has(change.meta?.domain ?? "")) {
3909
+ const mockups = await checkMockups(root, change.slug, change);
3910
+ changeDiags.push(...mockups.findings);
3911
+ }
3912
+ if (config.packs.length > 0) {
3913
+ const resolved = await resolvePacks(workspace.sddDir, config);
3914
+ const evaluations = evaluatePacks(resolved.packs, change, config);
3915
+ changeDiags.push(...packFindings(evaluations, change));
3916
+ }
3917
+ diagnostics.push(...changeDiags);
3918
+ const entry = count(change.slug, changeDiags);
3919
+ changesErrors += entry.errors;
3920
+ changesWarnings += entry.warnings;
3921
+ }
3922
+ checks.push({ name: "cambios (lint + trace + waves + mockups)", errors: changesErrors, warnings: changesWarnings });
3923
+ const doctor = await runDoctor(root);
3924
+ const doctorErrors = doctor.findings.filter((d) => d.severity === "error");
3925
+ diagnostics.push(...doctorErrors);
3926
+ checks.push({ name: "doctor", errors: doctorErrors.length, warnings: 0 });
3927
+ for (const extra of opts.extra ?? []) {
3928
+ diagnostics.push(...extra.diagnostics);
3929
+ checks.push(count(extra.name, extra.diagnostics));
3930
+ }
3931
+ const errors = diagnostics.filter((d) => d.severity === "error").length;
3932
+ const warnings = diagnostics.filter((d) => d.severity === "warning").length;
3933
+ const failed = errors > 0 || (opts.strict ?? false) && warnings > 0;
3934
+ return { checks, diagnostics, errors, warnings, failed };
3935
+ }
3867
3936
  export {
3868
3937
  BLOCK_HEAD_RE,
3869
3938
  BUILTIN_PACKS,
@@ -3951,6 +4020,7 @@ export {
3951
4020
  listDir,
3952
4021
  listDirs,
3953
4022
  listRuns,
4023
+ livingRequirementsMap,
3954
4024
  loadActiveProfile,
3955
4025
  loadApprovals,
3956
4026
  loadChange,
@@ -3994,6 +4064,7 @@ export {
3994
4064
  renderTemplate,
3995
4065
  resolvePacks,
3996
4066
  runAnalyze,
4067
+ runCiGate,
3997
4068
  runDoctor,
3998
4069
  runProcess,
3999
4070
  sha256,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@specatlas/core",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Kernel determinista de SpecAtlas: parsing, validación, trazabilidad, olas y ciclo de vida",
5
5
  "license": "MIT",
6
6
  "type": "module",