@specatlas/core 0.1.22 → 0.1.24
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 +143 -1
- package/dist/index.js +395 -23
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1708,6 +1708,148 @@ interface ArchiveResult {
|
|
|
1708
1708
|
declare function archiveChange(opts: ArchiveOptions): Promise<ArchiveResult>;
|
|
1709
1709
|
declare function regenerateIndex(root: string, cfg?: AtlasConfig, now?: Date): Promise<void>;
|
|
1710
1710
|
|
|
1711
|
+
declare const SCHEMA_VERSION = 1;
|
|
1712
|
+
declare const BACKUP_DIRNAME = ".backup";
|
|
1713
|
+
declare const BACKUP_POINTER = ".latest";
|
|
1714
|
+
interface MigrationSpec {
|
|
1715
|
+
id: string;
|
|
1716
|
+
description: string;
|
|
1717
|
+
from: number;
|
|
1718
|
+
to: number;
|
|
1719
|
+
}
|
|
1720
|
+
declare const SCHEMA_MIGRATIONS: MigrationSpec[];
|
|
1721
|
+
interface UpgradeItem {
|
|
1722
|
+
artifact: string;
|
|
1723
|
+
path: string;
|
|
1724
|
+
from: number;
|
|
1725
|
+
to: number;
|
|
1726
|
+
migration: string;
|
|
1727
|
+
summary: string;
|
|
1728
|
+
}
|
|
1729
|
+
interface UpgradeIssue {
|
|
1730
|
+
artifact: string;
|
|
1731
|
+
path: string;
|
|
1732
|
+
reason: string;
|
|
1733
|
+
}
|
|
1734
|
+
interface UpgradePlan {
|
|
1735
|
+
root: string;
|
|
1736
|
+
currentVersion: number;
|
|
1737
|
+
pending: UpgradeItem[];
|
|
1738
|
+
newer: UpgradeIssue[];
|
|
1739
|
+
unreadable: UpgradeIssue[];
|
|
1740
|
+
upToDate: boolean;
|
|
1741
|
+
}
|
|
1742
|
+
interface FoundArtifact {
|
|
1743
|
+
artifact: string;
|
|
1744
|
+
path: string;
|
|
1745
|
+
}
|
|
1746
|
+
declare function collectVersionedArtifacts(root: string): Promise<FoundArtifact[]>;
|
|
1747
|
+
type ArtifactVersionState = {
|
|
1748
|
+
state: 'ok';
|
|
1749
|
+
version: number;
|
|
1750
|
+
} | {
|
|
1751
|
+
state: 'absent';
|
|
1752
|
+
} | {
|
|
1753
|
+
state: 'unreadable';
|
|
1754
|
+
reason: string;
|
|
1755
|
+
};
|
|
1756
|
+
declare function readArtifactVersion(content: string): ArtifactVersionState;
|
|
1757
|
+
declare function stampSchemaVersion(content: string, version: number): string | undefined;
|
|
1758
|
+
declare function planUpgrade(root: string): Promise<UpgradePlan>;
|
|
1759
|
+
interface UpgradeBackupInfo {
|
|
1760
|
+
name: string;
|
|
1761
|
+
dir: string;
|
|
1762
|
+
relativeDir: string;
|
|
1763
|
+
createdAt: string;
|
|
1764
|
+
files: string[];
|
|
1765
|
+
}
|
|
1766
|
+
interface UpgradeApplyReport {
|
|
1767
|
+
status: 'applied' | 'up-to-date' | 'failed';
|
|
1768
|
+
plan: UpgradePlan;
|
|
1769
|
+
applied: UpgradeItem[];
|
|
1770
|
+
backup?: UpgradeBackupInfo;
|
|
1771
|
+
failure?: {
|
|
1772
|
+
artifact: string;
|
|
1773
|
+
message: string;
|
|
1774
|
+
};
|
|
1775
|
+
}
|
|
1776
|
+
declare function applyUpgrade(root: string, now?: Date): Promise<UpgradeApplyReport>;
|
|
1777
|
+
interface UpgradeRollbackReport {
|
|
1778
|
+
status: 'restored' | 'no-backup' | 'failed';
|
|
1779
|
+
restored: string[];
|
|
1780
|
+
backup?: string;
|
|
1781
|
+
failure?: {
|
|
1782
|
+
artifact: string;
|
|
1783
|
+
message: string;
|
|
1784
|
+
};
|
|
1785
|
+
}
|
|
1786
|
+
declare function rollbackUpgrade(root: string): Promise<UpgradeRollbackReport>;
|
|
1787
|
+
interface UpgradeAdvisory {
|
|
1788
|
+
plan: UpgradePlan;
|
|
1789
|
+
diagnostics: Diagnostic[];
|
|
1790
|
+
}
|
|
1791
|
+
declare function upgradeAdvisory(root: string): Promise<UpgradeAdvisory>;
|
|
1792
|
+
|
|
1793
|
+
declare const SARIF_SCHEMA = "https://json.schemastore.org/sarif-2.1.0.json";
|
|
1794
|
+
declare const SARIF_VERSION = "2.1.0";
|
|
1795
|
+
declare const TOOL_NAME = "SpecAtlas";
|
|
1796
|
+
declare const TOOL_URL = "https://github.com/AlonsoAM/specatlas";
|
|
1797
|
+
interface SarifOptions {
|
|
1798
|
+
diagnostics: readonly Diagnostic[];
|
|
1799
|
+
root: string;
|
|
1800
|
+
version: string;
|
|
1801
|
+
failed?: boolean;
|
|
1802
|
+
}
|
|
1803
|
+
interface SarifRule {
|
|
1804
|
+
id: string;
|
|
1805
|
+
shortDescription: {
|
|
1806
|
+
text: string;
|
|
1807
|
+
};
|
|
1808
|
+
help?: {
|
|
1809
|
+
text: string;
|
|
1810
|
+
};
|
|
1811
|
+
}
|
|
1812
|
+
interface SarifLocation {
|
|
1813
|
+
physicalLocation: {
|
|
1814
|
+
artifactLocation: {
|
|
1815
|
+
uri: string;
|
|
1816
|
+
};
|
|
1817
|
+
region?: {
|
|
1818
|
+
startLine: number;
|
|
1819
|
+
};
|
|
1820
|
+
};
|
|
1821
|
+
}
|
|
1822
|
+
interface SarifResult {
|
|
1823
|
+
ruleId: string;
|
|
1824
|
+
level: 'error' | 'warning' | 'note';
|
|
1825
|
+
message: {
|
|
1826
|
+
text: string;
|
|
1827
|
+
};
|
|
1828
|
+
locations?: SarifLocation[];
|
|
1829
|
+
}
|
|
1830
|
+
interface SarifReport {
|
|
1831
|
+
$schema: string;
|
|
1832
|
+
version: string;
|
|
1833
|
+
runs: Array<{
|
|
1834
|
+
tool: {
|
|
1835
|
+
driver: {
|
|
1836
|
+
name: string;
|
|
1837
|
+
informationUri: string;
|
|
1838
|
+
version: string;
|
|
1839
|
+
rules: SarifRule[];
|
|
1840
|
+
};
|
|
1841
|
+
};
|
|
1842
|
+
results: SarifResult[];
|
|
1843
|
+
invocations: Array<{
|
|
1844
|
+
executionSuccessful: boolean;
|
|
1845
|
+
}>;
|
|
1846
|
+
}>;
|
|
1847
|
+
}
|
|
1848
|
+
declare function ruleDescription(code: string): string;
|
|
1849
|
+
declare function sarifLevel(severity: Diagnostic['severity']): SarifResult['level'];
|
|
1850
|
+
declare function toSarifReport(opts: SarifOptions): SarifReport;
|
|
1851
|
+
declare function toSarifText(opts: SarifOptions): string;
|
|
1852
|
+
|
|
1711
1853
|
interface DoctorReport {
|
|
1712
1854
|
findings: Diagnostic[];
|
|
1713
1855
|
summary: {
|
|
@@ -1747,4 +1889,4 @@ declare function livingRequirementsMap(specs: Array<{
|
|
|
1747
1889
|
}>): Map<string, Requirement>;
|
|
1748
1890
|
declare function runCiGate(opts: CiOptions): Promise<CiResult>;
|
|
1749
1891
|
|
|
1750
|
-
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 GlossaryTerm, type ImpactEvidence, type ImpactReport, type ImpactTask, 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 MoveOps, type NewChangeOptions, type NewChangeResult, type NextAction, type Override, type Pack, type PackCheck, type PackCheckResult, type PackCheckType, type PackEvaluation, type ParsedGlossary, 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, impactOfFile, impactOfRequirement, indexMarkdown, initWorkspace, inline, isCommandAllowed, isDirectory, issueBody, issueLabels, laneOrDefault, lintDelta, lintMockupHtml, lintMockupManifest, lintPlan, lintRequirement, lintSpec, lintTasks, lintVerify, listChangeSlugs, listDir, listDirs, listRuns, livingRequirementsMap, loadActiveProfile, loadApprovals, loadChange, loadConfig, loadDetectedBest, loadProfileFile, loadProfilesFromDir, loadProjectPacks, loadSpecs, loadWorkspace, localCompact, localDate, localMonth, localOffset, localStamp, matchGlob, mockupManifestSchema, mockupsDir, mockupsReady, moveDirectory, packFindings, parseApprovals, parseChangeMeta, parseConfig, parseDelta, parseFrontmatter, parseGitHubRemote, parseGlossary, parseIssueUrl, parseRequirementBlocks, parseSpecFile, parseTasksFile, parseVerifyFile, patchChangeMeta, planBlock, planMockups, planWaves, profileSchema, readMockupManifest, readRun, readText, readTextIfExists, recordEvidence, regenerateIndex, renderMarkdown, renderRequirement, renderTemplate, requiresMockups, resolvePacks, runAnalyze, runCiGate, runDoctor, runProcess, setMockupRequirement, sha256, shortHash, signApproval, specHashOf, splitCommand, stateLabel, syncGithubIssue, templatesFor, toPosix, tokensFile, updateMockupScreenshots, updateRunStatus, verifyApproval, walkFiles, writeConfig, writeMockupManifest, writeMockupPlan, writeText };
|
|
1892
|
+
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, BACKUP_DIRNAME, BACKUP_POINTER, 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 GlossaryTerm, type ImpactEvidence, type ImpactReport, type ImpactTask, type InitOptions, type InitResult, type IssueBodyInput, type Lane, type Language, type LinkedIssue, type LintOptions, type LoadedConfig, type MigrationSpec, type MockupCheckResult, type MockupManifest, type MockupPlan, type MockupPlanScreen, type MockupScreen, type MoveOps, type NewChangeOptions, type NewChangeResult, type NextAction, type Override, type Pack, type PackCheck, type PackCheckResult, type PackCheckType, type PackEvaluation, type ParsedGlossary, 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, SARIF_SCHEMA, SARIF_VERSION, SCENARIO_HEAD_RE, SCENARIO_ID_RE, SCHEMA_MIGRATIONS, SCHEMA_VERSION, SDD_DIR, SLUG_RE, type SarifLocation, type SarifOptions, type SarifReport, type SarifResult, type SarifRule, type Scenario, type Severity, type SignApprovalOptions, type SignApprovalResult, type SpecFile, type SpecRef, type StackProfile, type SyncGithubOptions, type SyncGithubResult, TASK_ID_RE, TASK_LINE_RE, TOOL_NAME, TOOL_URL, type Task, type TaskBlock, type TasksFile, type TemplateSet, type TraceEdge, type TraceFinding, type TraceGraph, type TraceInput, type TraceNode, type TraceResult, type UpgradeAdvisory, type UpgradeApplyReport, type UpgradeBackupInfo, type UpgradeIssue, type UpgradeItem, type UpgradePlan, type UpgradeRollbackReport, type VerifyFile, type WalkEntry, type WavePlan, type Workspace, type WorkspaceMetrics, adoptWorkspace, appendRunEvent, applyUpgrade, approvalsSchema, approveFromGithub, archiveChange, artifactHash, atlasConfigSchema, buildTraceGraph, canonicalizeMarkdown, captureMockups, changeMarker, changeMetaSchema, changeMetaYaml, checkMockups, checkTrace, collectMetrics, collectVersionedArtifacts, 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, impactOfFile, impactOfRequirement, indexMarkdown, initWorkspace, inline, isCommandAllowed, isDirectory, issueBody, issueLabels, laneOrDefault, lintDelta, lintMockupHtml, lintMockupManifest, lintPlan, lintRequirement, lintSpec, lintTasks, lintVerify, listChangeSlugs, listDir, listDirs, listRuns, livingRequirementsMap, loadActiveProfile, loadApprovals, loadChange, loadConfig, loadDetectedBest, loadProfileFile, loadProfilesFromDir, loadProjectPacks, loadSpecs, loadWorkspace, localCompact, localDate, localMonth, localOffset, localStamp, matchGlob, mockupManifestSchema, mockupsDir, mockupsReady, moveDirectory, packFindings, parseApprovals, parseChangeMeta, parseConfig, parseDelta, parseFrontmatter, parseGitHubRemote, parseGlossary, parseIssueUrl, parseRequirementBlocks, parseSpecFile, parseTasksFile, parseVerifyFile, patchChangeMeta, planBlock, planMockups, planUpgrade, planWaves, profileSchema, readArtifactVersion, readMockupManifest, readRun, readText, readTextIfExists, recordEvidence, regenerateIndex, renderMarkdown, renderRequirement, renderTemplate, requiresMockups, resolvePacks, rollbackUpgrade, ruleDescription, runAnalyze, runCiGate, runDoctor, runProcess, sarifLevel, setMockupRequirement, sha256, shortHash, signApproval, specHashOf, splitCommand, stampSchemaVersion, stateLabel, syncGithubIssue, templatesFor, toPosix, toSarifReport, toSarifText, tokensFile, updateMockupScreenshots, updateRunStatus, upgradeAdvisory, verifyApproval, walkFiles, writeConfig, writeMockupManifest, writeMockupPlan, writeText };
|
package/dist/index.js
CHANGED
|
@@ -794,14 +794,14 @@ function wordBoundary(text, term) {
|
|
|
794
794
|
const re = new RegExp(`(?<![\\p{L}\\p{N}])${escaped}(?![\\p{L}\\p{N}])`, "iu");
|
|
795
795
|
return re.test(text);
|
|
796
796
|
}
|
|
797
|
-
function lintText(text, code, terms, label,
|
|
797
|
+
function lintText(text, code, terms, label, path21, line) {
|
|
798
798
|
const out = [];
|
|
799
799
|
const lower = text.toLowerCase();
|
|
800
800
|
for (const term of terms) {
|
|
801
801
|
if (wordBoundary(lower, term)) {
|
|
802
802
|
out.push(
|
|
803
803
|
diag(code, "error", `${label}: "${term}"`, {
|
|
804
|
-
path:
|
|
804
|
+
path: path21,
|
|
805
805
|
line,
|
|
806
806
|
suggestion: "La especificaci\xF3n es funcional y de negocio: describe comportamiento, no tecnolog\xEDa ni adjetivos vagos"
|
|
807
807
|
})
|
|
@@ -810,7 +810,7 @@ function lintText(text, code, terms, label, path19, line) {
|
|
|
810
810
|
}
|
|
811
811
|
return out;
|
|
812
812
|
}
|
|
813
|
-
function lintRequirement(req,
|
|
813
|
+
function lintRequirement(req, path21, opts = {}) {
|
|
814
814
|
const out = [];
|
|
815
815
|
const vague = opts.language === "en" ? VAGUE_EN : VAGUE_ES;
|
|
816
816
|
const tech = opts.language === "en" ? TECH_EN : TECH_ES;
|
|
@@ -821,32 +821,32 @@ function lintRequirement(req, path19, opts = {}) {
|
|
|
821
821
|
...req.scenarios.flatMap((s) => [...s.when.map((w) => ({ text: w, line: s.line })), ...s.then.map((t) => ({ text: t, line: s.line }))])
|
|
822
822
|
];
|
|
823
823
|
for (const part of parts) {
|
|
824
|
-
out.push(...lintText(part.text, "LINT-BIZ-002", vague, "Palabra vaga en la especificaci\xF3n",
|
|
824
|
+
out.push(...lintText(part.text, "LINT-BIZ-002", vague, "Palabra vaga en la especificaci\xF3n", path21, part.line));
|
|
825
825
|
if (opts.businessOnly !== false) {
|
|
826
|
-
out.push(...lintText(part.text, "LINT-BIZ-001", tech, "Jerga t\xE9cnica en la especificaci\xF3n de negocio",
|
|
826
|
+
out.push(...lintText(part.text, "LINT-BIZ-001", tech, "Jerga t\xE9cnica en la especificaci\xF3n de negocio", path21, part.line));
|
|
827
827
|
}
|
|
828
828
|
}
|
|
829
829
|
if (req.scenarios.length === 0) {
|
|
830
|
-
out.push(diag("TRACE-001", "error", `El requisito ${req.id} no tiene ning\xFAn escenario`, { path:
|
|
830
|
+
out.push(diag("TRACE-001", "error", `El requisito ${req.id} no tiene ning\xFAn escenario`, { path: path21, line: req.line, suggestion: "A\xF1ade al menos un escenario CUANDO/ENTONCES" }));
|
|
831
831
|
}
|
|
832
832
|
return out;
|
|
833
833
|
}
|
|
834
|
-
function lintDelta(delta, livingRequirements,
|
|
834
|
+
function lintDelta(delta, livingRequirements, path21, opts = {}) {
|
|
835
835
|
const out = [...delta.diagnostics];
|
|
836
836
|
for (const req of [...delta.added, ...delta.modified]) {
|
|
837
|
-
out.push(...lintRequirement(req,
|
|
837
|
+
out.push(...lintRequirement(req, path21, opts));
|
|
838
838
|
}
|
|
839
839
|
for (const req of delta.modified) {
|
|
840
840
|
const living = livingRequirements.get(req.id);
|
|
841
841
|
if (!living) {
|
|
842
|
-
out.push(diag("TRACE-007", "error", `MODIFIED ${req.id} no existe en la spec viva; usa ADDED`, { path:
|
|
842
|
+
out.push(diag("TRACE-007", "error", `MODIFIED ${req.id} no existe en la spec viva; usa ADDED`, { path: path21, line: req.line }));
|
|
843
843
|
continue;
|
|
844
844
|
}
|
|
845
845
|
for (const existing of living.scenarios) {
|
|
846
846
|
if (!req.scenarios.some((s) => s.id === existing.id)) {
|
|
847
847
|
out.push(
|
|
848
848
|
diag("TRACE-007", "error", `MODIFIED ${req.id} pierde el escenario ${existing.id}: copia el bloque completo`, {
|
|
849
|
-
path:
|
|
849
|
+
path: path21,
|
|
850
850
|
line: req.line,
|
|
851
851
|
suggestion: "Copia el bloque completo de la spec viva y ed\xEDtalo; para quitarlo, decl\xE1ralo en REMOVED"
|
|
852
852
|
})
|
|
@@ -857,15 +857,15 @@ function lintDelta(delta, livingRequirements, path19, opts = {}) {
|
|
|
857
857
|
for (const req of delta.removed) {
|
|
858
858
|
const living = livingRequirements.get(req.id);
|
|
859
859
|
if (!living) {
|
|
860
|
-
out.push(diag("TRACE-007", "error", `REMOVED ${req.id} no existe en la spec viva`, { path:
|
|
860
|
+
out.push(diag("TRACE-007", "error", `REMOVED ${req.id} no existe en la spec viva`, { path: path21, line: req.line }));
|
|
861
861
|
}
|
|
862
862
|
}
|
|
863
863
|
for (const rename2 of delta.renamed) {
|
|
864
864
|
if (rename2.from.id !== rename2.to.id) {
|
|
865
|
-
out.push(diag("LINT-DLT-003", "error", `RENAMED cambia el id (${rename2.from.id} \u2192 ${rename2.to.id}); los ids son inmutables`, { path:
|
|
865
|
+
out.push(diag("LINT-DLT-003", "error", `RENAMED cambia el id (${rename2.from.id} \u2192 ${rename2.to.id}); los ids son inmutables`, { path: path21, line: rename2.line }));
|
|
866
866
|
}
|
|
867
867
|
if (!livingRequirements.has(rename2.from.id)) {
|
|
868
|
-
out.push(diag("TRACE-007", "error", `RENAMED ${rename2.from.id} no existe en la spec viva`, { path:
|
|
868
|
+
out.push(diag("TRACE-007", "error", `RENAMED ${rename2.from.id} no existe en la spec viva`, { path: path21, line: rename2.line }));
|
|
869
869
|
}
|
|
870
870
|
}
|
|
871
871
|
return out;
|
|
@@ -895,7 +895,7 @@ var MERMAID_KEYWORDS = [
|
|
|
895
895
|
"architecture-beta"
|
|
896
896
|
];
|
|
897
897
|
var MERMAID_BLOCKS = /^\s*(alt|loop|opt|par|rect|critical|break|subgraph)\b/;
|
|
898
|
-
function lintPlan(planText,
|
|
898
|
+
function lintPlan(planText, path21) {
|
|
899
899
|
const out = [];
|
|
900
900
|
const blocks = [...planText.matchAll(/```mermaid\r?\n([\s\S]*?)```/g)];
|
|
901
901
|
for (const [index, block] of blocks.entries()) {
|
|
@@ -905,7 +905,7 @@ function lintPlan(planText, path19) {
|
|
|
905
905
|
if (!MERMAID_KEYWORDS.some((keyword) => first.startsWith(keyword))) {
|
|
906
906
|
out.push(
|
|
907
907
|
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)}"`, {
|
|
908
|
-
path:
|
|
908
|
+
path: path21,
|
|
909
909
|
suggestion: "Corrige el tipo del diagrama o elimina el bloque"
|
|
910
910
|
})
|
|
911
911
|
);
|
|
@@ -919,7 +919,7 @@ function lintPlan(planText, path19) {
|
|
|
919
919
|
if (open !== 0) {
|
|
920
920
|
out.push(
|
|
921
921
|
diag("LINT-PLN-002", "error", `Diagrama mermaid ${index + 1}: faltan ${Math.abs(open)} \`end\` (bloques alt/loop/subgraph sin cerrar)`, {
|
|
922
|
-
path:
|
|
922
|
+
path: path21,
|
|
923
923
|
suggestion: "Cierra cada bloque alt/loop/opt/par/rect/subgraph con `end`"
|
|
924
924
|
})
|
|
925
925
|
);
|
|
@@ -931,7 +931,7 @@ function lintPlan(planText, path19) {
|
|
|
931
931
|
if (message.includes(";")) {
|
|
932
932
|
out.push(
|
|
933
933
|
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`, {
|
|
934
|
-
path:
|
|
934
|
+
path: path21,
|
|
935
935
|
suggestion: "Sustituye `;` por `\xB7` o `,` en los mensajes de sequenceDiagram"
|
|
936
936
|
})
|
|
937
937
|
);
|
|
@@ -4151,8 +4151,361 @@ async function regenerateIndex(root, cfg, now = /* @__PURE__ */ new Date()) {
|
|
|
4151
4151
|
await writeText(path16.join(root, ".sdd", "INDEX.md"), markdown);
|
|
4152
4152
|
}
|
|
4153
4153
|
|
|
4154
|
-
// src/
|
|
4154
|
+
// src/migrations.ts
|
|
4155
|
+
import { promises as fs2 } from "fs";
|
|
4155
4156
|
import path17 from "path";
|
|
4157
|
+
import { parse as parseYaml8, stringify as stringifyYaml5 } from "yaml";
|
|
4158
|
+
var SCHEMA_VERSION = 1;
|
|
4159
|
+
var BACKUP_DIRNAME = ".backup";
|
|
4160
|
+
var BACKUP_POINTER = ".latest";
|
|
4161
|
+
var SCHEMA_MIGRATIONS = [
|
|
4162
|
+
{
|
|
4163
|
+
id: "0001-sellar-version-de-esquema",
|
|
4164
|
+
description: "Sella la versi\xF3n de esquema vigente en los elementos que no la declaran",
|
|
4165
|
+
from: 0,
|
|
4166
|
+
to: SCHEMA_VERSION
|
|
4167
|
+
}
|
|
4168
|
+
];
|
|
4169
|
+
var FIXED_ARTIFACTS = ["config.yaml", "approvals.yaml", path17.join("profiles", "detected.yaml")];
|
|
4170
|
+
var CHANGE_ARTIFACTS = ["meta.yaml", path17.join("mockups", "manifest.yaml")];
|
|
4171
|
+
async function pushChangeArtifacts(out, sddDir, relDir) {
|
|
4172
|
+
for (const rel of CHANGE_ARTIFACTS) {
|
|
4173
|
+
const abs = path17.join(sddDir, relDir, rel);
|
|
4174
|
+
if (await exists(abs)) out.push({ artifact: toPosix(path17.join(relDir, rel)), path: abs });
|
|
4175
|
+
}
|
|
4176
|
+
}
|
|
4177
|
+
async function collectVersionedArtifacts(root) {
|
|
4178
|
+
const sddDir = path17.join(root, ".sdd");
|
|
4179
|
+
const out = [];
|
|
4180
|
+
for (const rel of FIXED_ARTIFACTS) {
|
|
4181
|
+
const abs = path17.join(sddDir, rel);
|
|
4182
|
+
if (await exists(abs)) out.push({ artifact: toPosix(rel), path: abs });
|
|
4183
|
+
}
|
|
4184
|
+
const changesDir = path17.join(sddDir, "changes");
|
|
4185
|
+
for (const slug of await listDirs(changesDir)) {
|
|
4186
|
+
if (slug === "archive") continue;
|
|
4187
|
+
await pushChangeArtifacts(out, sddDir, path17.join("changes", slug));
|
|
4188
|
+
}
|
|
4189
|
+
for (const entry of await listDirs(path17.join(changesDir, "archive"))) {
|
|
4190
|
+
await pushChangeArtifacts(out, sddDir, path17.join("changes", "archive", entry));
|
|
4191
|
+
}
|
|
4192
|
+
return out;
|
|
4193
|
+
}
|
|
4194
|
+
function readArtifactVersion(content) {
|
|
4195
|
+
let data;
|
|
4196
|
+
try {
|
|
4197
|
+
data = parseYaml8(content);
|
|
4198
|
+
} catch (err) {
|
|
4199
|
+
return { state: "unreadable", reason: `YAML inv\xE1lido (${err.message})` };
|
|
4200
|
+
}
|
|
4201
|
+
if (data === null || data === void 0) return { state: "absent" };
|
|
4202
|
+
if (typeof data !== "object" || Array.isArray(data)) return { state: "unreadable", reason: "la ra\xEDz no es un mapa de claves y valores" };
|
|
4203
|
+
const raw = data["schema_version"];
|
|
4204
|
+
if (raw === void 0) return { state: "absent" };
|
|
4205
|
+
if (typeof raw !== "number" || !Number.isInteger(raw) || raw < 1) return { state: "unreadable", reason: `schema_version con valor no v\xE1lido: ${String(raw)}` };
|
|
4206
|
+
return { state: "ok", version: raw };
|
|
4207
|
+
}
|
|
4208
|
+
function stampSchemaVersion(content, version) {
|
|
4209
|
+
const line = `schema_version: ${version}`;
|
|
4210
|
+
const bom = content.startsWith("\uFEFF") ? "\uFEFF" : "";
|
|
4211
|
+
const body = bom ? content.slice(1) : content;
|
|
4212
|
+
if (body.trim() === "") return `${bom}${line}
|
|
4213
|
+
`;
|
|
4214
|
+
const eol = body.includes("\r\n") ? "\r\n" : "\n";
|
|
4215
|
+
const lines = body.split(/\r?\n/);
|
|
4216
|
+
const trailing = lines.length > 1 && lines[lines.length - 1] === "";
|
|
4217
|
+
let index = 0;
|
|
4218
|
+
while (index < lines.length) {
|
|
4219
|
+
const trimmed = (lines[index] ?? "").trim();
|
|
4220
|
+
if (trimmed === "" || trimmed.startsWith("#")) {
|
|
4221
|
+
index += 1;
|
|
4222
|
+
continue;
|
|
4223
|
+
}
|
|
4224
|
+
break;
|
|
4225
|
+
}
|
|
4226
|
+
if ((lines[index] ?? "").trim() === "---") index += 1;
|
|
4227
|
+
const insertAt = trailing ? Math.min(index, lines.length - 1) : index;
|
|
4228
|
+
lines.splice(insertAt, 0, line);
|
|
4229
|
+
const next = bom + lines.join(eol);
|
|
4230
|
+
const check = readArtifactVersion(next);
|
|
4231
|
+
if (check.state !== "ok" || check.version !== version) return void 0;
|
|
4232
|
+
return next;
|
|
4233
|
+
}
|
|
4234
|
+
async function planUpgrade(root) {
|
|
4235
|
+
const artifacts = await collectVersionedArtifacts(root);
|
|
4236
|
+
const pending = [];
|
|
4237
|
+
const newer = [];
|
|
4238
|
+
const unreadable = [];
|
|
4239
|
+
for (const found of artifacts) {
|
|
4240
|
+
const content = await readTextIfExists(found.path);
|
|
4241
|
+
if (content === void 0) continue;
|
|
4242
|
+
const state = readArtifactVersion(content);
|
|
4243
|
+
if (state.state === "unreadable") {
|
|
4244
|
+
unreadable.push({ ...found, reason: state.reason });
|
|
4245
|
+
continue;
|
|
4246
|
+
}
|
|
4247
|
+
if (state.state === "ok") {
|
|
4248
|
+
if (state.version > SCHEMA_VERSION) {
|
|
4249
|
+
newer.push({ ...found, reason: `produce la versi\xF3n ${state.version}, m\xE1s nueva que la vigente (${SCHEMA_VERSION})` });
|
|
4250
|
+
continue;
|
|
4251
|
+
}
|
|
4252
|
+
if (state.version === SCHEMA_VERSION) continue;
|
|
4253
|
+
}
|
|
4254
|
+
const from = state.state === "ok" ? state.version : 0;
|
|
4255
|
+
const migration = SCHEMA_MIGRATIONS.find((m) => m.from === from);
|
|
4256
|
+
if (!migration) {
|
|
4257
|
+
unreadable.push({ ...found, reason: `no hay migraci\xF3n registrada desde la versi\xF3n ${from}` });
|
|
4258
|
+
continue;
|
|
4259
|
+
}
|
|
4260
|
+
const stamped = stampSchemaVersion(content, migration.to);
|
|
4261
|
+
if (stamped === void 0) {
|
|
4262
|
+
unreadable.push({ ...found, reason: "no se pudo sellar con seguridad sin alterar el resto del contenido" });
|
|
4263
|
+
continue;
|
|
4264
|
+
}
|
|
4265
|
+
pending.push({ ...found, from, to: migration.to, migration: migration.id, summary: migration.description });
|
|
4266
|
+
}
|
|
4267
|
+
return { root, currentVersion: SCHEMA_VERSION, pending, newer, unreadable, upToDate: pending.length === 0 };
|
|
4268
|
+
}
|
|
4269
|
+
async function removeDir(dir) {
|
|
4270
|
+
try {
|
|
4271
|
+
await fs2.rm(dir, { recursive: true, force: true });
|
|
4272
|
+
} catch {
|
|
4273
|
+
}
|
|
4274
|
+
}
|
|
4275
|
+
async function removeFile(file) {
|
|
4276
|
+
try {
|
|
4277
|
+
await fs2.rm(file, { force: true });
|
|
4278
|
+
} catch {
|
|
4279
|
+
}
|
|
4280
|
+
}
|
|
4281
|
+
function backupName(now) {
|
|
4282
|
+
return `${localCompact(now)}${localOffset(now).replace(":", "")}`;
|
|
4283
|
+
}
|
|
4284
|
+
async function applyUpgrade(root, now = /* @__PURE__ */ new Date()) {
|
|
4285
|
+
const plan = await planUpgrade(root);
|
|
4286
|
+
if (plan.pending.length === 0) return { status: "up-to-date", plan, applied: [] };
|
|
4287
|
+
const sddDir = path17.join(root, ".sdd");
|
|
4288
|
+
const backupRoot = path17.join(sddDir, BACKUP_DIRNAME);
|
|
4289
|
+
const name = backupName(now);
|
|
4290
|
+
const backupDir = path17.join(backupRoot, name);
|
|
4291
|
+
const pointerFile = path17.join(backupRoot, BACKUP_POINTER);
|
|
4292
|
+
const previous = [];
|
|
4293
|
+
try {
|
|
4294
|
+
for (const item of plan.pending) {
|
|
4295
|
+
const content = await readTextIfExists(item.path);
|
|
4296
|
+
if (content === void 0) throw new Error("el elemento desapareci\xF3 mientras se respaldaba");
|
|
4297
|
+
previous.push({ item, contents: content });
|
|
4298
|
+
await writeText(path17.join(backupDir, "files", ...item.artifact.split("/")), content);
|
|
4299
|
+
}
|
|
4300
|
+
await writeText(
|
|
4301
|
+
path17.join(backupDir, "backup.yaml"),
|
|
4302
|
+
stringifyYaml5(
|
|
4303
|
+
{
|
|
4304
|
+
schema_version: SCHEMA_VERSION,
|
|
4305
|
+
created_at: localStamp(now),
|
|
4306
|
+
to_version: SCHEMA_VERSION,
|
|
4307
|
+
files: plan.pending.map((i) => ({ artifact: i.artifact, from: i.from, to: i.to, migration: i.migration }))
|
|
4308
|
+
},
|
|
4309
|
+
{ lineWidth: 120 }
|
|
4310
|
+
)
|
|
4311
|
+
);
|
|
4312
|
+
await writeText(pointerFile, `${name}
|
|
4313
|
+
`);
|
|
4314
|
+
} catch (err) {
|
|
4315
|
+
await removeDir(backupDir);
|
|
4316
|
+
await removeFile(pointerFile);
|
|
4317
|
+
return { status: "failed", plan, applied: [], failure: { artifact: "(respaldo)", message: err.message } };
|
|
4318
|
+
}
|
|
4319
|
+
const written = [];
|
|
4320
|
+
for (const { item, contents } of previous) {
|
|
4321
|
+
try {
|
|
4322
|
+
const next = stampSchemaVersion(contents, item.to);
|
|
4323
|
+
if (next === void 0) throw new Error("no se pudo sellar con seguridad");
|
|
4324
|
+
await writeText(item.path, next);
|
|
4325
|
+
written.push(item);
|
|
4326
|
+
} catch (err) {
|
|
4327
|
+
for (const w of written) {
|
|
4328
|
+
const prev = previous.find((p) => p.item.artifact === w.artifact);
|
|
4329
|
+
if (prev) {
|
|
4330
|
+
try {
|
|
4331
|
+
await writeText(prev.item.path, prev.contents);
|
|
4332
|
+
} catch {
|
|
4333
|
+
}
|
|
4334
|
+
}
|
|
4335
|
+
}
|
|
4336
|
+
await removeFile(pointerFile);
|
|
4337
|
+
await removeDir(backupDir);
|
|
4338
|
+
return { status: "failed", plan, applied: [], failure: { artifact: item.artifact, message: err.message } };
|
|
4339
|
+
}
|
|
4340
|
+
}
|
|
4341
|
+
for (const dir of await listDirs(backupRoot)) {
|
|
4342
|
+
if (dir !== name) await removeDir(path17.join(backupRoot, dir));
|
|
4343
|
+
}
|
|
4344
|
+
return {
|
|
4345
|
+
status: "applied",
|
|
4346
|
+
plan,
|
|
4347
|
+
applied: written,
|
|
4348
|
+
backup: {
|
|
4349
|
+
name,
|
|
4350
|
+
dir: backupDir,
|
|
4351
|
+
relativeDir: toPosix(path17.relative(root, backupDir)),
|
|
4352
|
+
createdAt: localStamp(now),
|
|
4353
|
+
files: plan.pending.map((i) => i.artifact)
|
|
4354
|
+
}
|
|
4355
|
+
};
|
|
4356
|
+
}
|
|
4357
|
+
async function rollbackUpgrade(root) {
|
|
4358
|
+
const sddDir = path17.join(root, ".sdd");
|
|
4359
|
+
const backupRoot = path17.join(sddDir, BACKUP_DIRNAME);
|
|
4360
|
+
const pointerFile = path17.join(backupRoot, BACKUP_POINTER);
|
|
4361
|
+
const name = (await readTextIfExists(pointerFile))?.trim();
|
|
4362
|
+
if (!name) return { status: "no-backup", restored: [] };
|
|
4363
|
+
const backupDir = path17.join(backupRoot, name);
|
|
4364
|
+
const manifestRaw = await readTextIfExists(path17.join(backupDir, "backup.yaml"));
|
|
4365
|
+
if (manifestRaw === void 0) return { status: "no-backup", restored: [] };
|
|
4366
|
+
let files = [];
|
|
4367
|
+
try {
|
|
4368
|
+
const parsed = parseYaml8(manifestRaw);
|
|
4369
|
+
files = (parsed?.files ?? []).filter((f) => typeof f?.artifact === "string");
|
|
4370
|
+
} catch {
|
|
4371
|
+
return { status: "no-backup", restored: [] };
|
|
4372
|
+
}
|
|
4373
|
+
const restored = [];
|
|
4374
|
+
for (const file of files) {
|
|
4375
|
+
const from = path17.join(backupDir, "files", ...file.artifact.split("/"));
|
|
4376
|
+
const to = path17.join(sddDir, ...file.artifact.split("/"));
|
|
4377
|
+
const content = await readTextIfExists(from);
|
|
4378
|
+
if (content === void 0) continue;
|
|
4379
|
+
try {
|
|
4380
|
+
await writeText(to, content);
|
|
4381
|
+
restored.push(file.artifact);
|
|
4382
|
+
} catch (err) {
|
|
4383
|
+
return { status: "failed", restored, backup: name, failure: { artifact: file.artifact, message: err.message } };
|
|
4384
|
+
}
|
|
4385
|
+
}
|
|
4386
|
+
await removeDir(backupDir);
|
|
4387
|
+
await removeFile(pointerFile);
|
|
4388
|
+
return { status: "restored", restored, backup: name };
|
|
4389
|
+
}
|
|
4390
|
+
async function upgradeAdvisory(root) {
|
|
4391
|
+
const plan = await planUpgrade(root);
|
|
4392
|
+
const diagnostics = [];
|
|
4393
|
+
if (plan.pending.length > 0) {
|
|
4394
|
+
diagnostics.push(
|
|
4395
|
+
diag("ATLAS-UPGRADE-001", "warning", `El estado del proyecto no corresponde a la versi\xF3n vigente: ${plan.pending.length} elemento(s) por actualizar`, {
|
|
4396
|
+
suggestion: "Vista previa: `satlas upgrade` \xB7 Aplicar: `satlas upgrade --apply`"
|
|
4397
|
+
})
|
|
4398
|
+
);
|
|
4399
|
+
}
|
|
4400
|
+
for (const item of plan.newer) {
|
|
4401
|
+
diagnostics.push(
|
|
4402
|
+
diag("ATLAS-UPGRADE-002", "warning", `El proyecto fue producido por una versi\xF3n m\xE1s nueva: "${item.artifact}" (${item.reason})`, {
|
|
4403
|
+
path: item.path,
|
|
4404
|
+
suggestion: "Actualiza la herramienta; no se actualiza hacia atr\xE1s"
|
|
4405
|
+
})
|
|
4406
|
+
);
|
|
4407
|
+
}
|
|
4408
|
+
for (const item of plan.unreadable) {
|
|
4409
|
+
diagnostics.push(
|
|
4410
|
+
diag("ATLAS-UPGRADE-003", "warning", `No se pudo interpretar "${item.artifact}": ${item.reason}`, {
|
|
4411
|
+
path: item.path,
|
|
4412
|
+
suggestion: "Revisa el elemento; no se modificar\xE1"
|
|
4413
|
+
})
|
|
4414
|
+
);
|
|
4415
|
+
}
|
|
4416
|
+
return { plan, diagnostics };
|
|
4417
|
+
}
|
|
4418
|
+
|
|
4419
|
+
// src/sarif.ts
|
|
4420
|
+
import path18 from "path";
|
|
4421
|
+
var SARIF_SCHEMA = "https://json.schemastore.org/sarif-2.1.0.json";
|
|
4422
|
+
var SARIF_VERSION = "2.1.0";
|
|
4423
|
+
var TOOL_NAME = "SpecAtlas";
|
|
4424
|
+
var TOOL_URL = "https://github.com/AlonsoAM/specatlas";
|
|
4425
|
+
var RULE_FAMILIES = [
|
|
4426
|
+
[/^LINT-STR-/, "Estructura de los artefactos"],
|
|
4427
|
+
[/^LINT-BIZ-/, "Lenguaje de negocio de la especificaci\xF3n"],
|
|
4428
|
+
[/^LINT-DLT-/, "Delta de especificaci\xF3n"],
|
|
4429
|
+
[/^LINT-EVD-/, "Evidencia registrada"],
|
|
4430
|
+
[/^LINT-TSK-/, "Tareas declaradas"],
|
|
4431
|
+
[/^LINT-PLN-/, "Plan t\xE9cnico"],
|
|
4432
|
+
[/^(LINT-MKP|MKP)-/, "Mockups"],
|
|
4433
|
+
[/^TRACE-/, "Trazabilidad requisito, escenario, tarea y evidencia"],
|
|
4434
|
+
[/^PACK-/, "Pack de cumplimiento"],
|
|
4435
|
+
[/^ATLAS-UPGRADE-/, "Actualizaci\xF3n del estado del proyecto"],
|
|
4436
|
+
[/^ATLAS-ADAPTERS-/, "Adaptadores de agente"],
|
|
4437
|
+
[/^ATLAS-CI-/, "Comprobaci\xF3n continua"],
|
|
4438
|
+
[/^ATLAS-/, "Estado del proyecto"]
|
|
4439
|
+
];
|
|
4440
|
+
function ruleDescription(code) {
|
|
4441
|
+
for (const [pattern, description] of RULE_FAMILIES) {
|
|
4442
|
+
if (pattern.test(code)) return description;
|
|
4443
|
+
}
|
|
4444
|
+
return code;
|
|
4445
|
+
}
|
|
4446
|
+
function sarifLevel(severity) {
|
|
4447
|
+
if (severity === "error") return "error";
|
|
4448
|
+
if (severity === "warning") return "warning";
|
|
4449
|
+
return "note";
|
|
4450
|
+
}
|
|
4451
|
+
function resultOf(diagnostic, root) {
|
|
4452
|
+
const result = {
|
|
4453
|
+
ruleId: diagnostic.code,
|
|
4454
|
+
level: sarifLevel(diagnostic.severity),
|
|
4455
|
+
message: { text: diagnostic.message }
|
|
4456
|
+
};
|
|
4457
|
+
if (diagnostic.path) {
|
|
4458
|
+
const rel = path18.relative(root, diagnostic.path);
|
|
4459
|
+
if (rel !== "") {
|
|
4460
|
+
const physicalLocation = {
|
|
4461
|
+
artifactLocation: { uri: toPosix(rel) },
|
|
4462
|
+
...diagnostic.line ? { region: { startLine: diagnostic.line } } : {}
|
|
4463
|
+
};
|
|
4464
|
+
result.locations = [{ physicalLocation }];
|
|
4465
|
+
}
|
|
4466
|
+
}
|
|
4467
|
+
return result;
|
|
4468
|
+
}
|
|
4469
|
+
function toSarifReport(opts) {
|
|
4470
|
+
const rules = /* @__PURE__ */ new Map();
|
|
4471
|
+
const results = [];
|
|
4472
|
+
for (const diagnostic of opts.diagnostics) {
|
|
4473
|
+
let rule = rules.get(diagnostic.code);
|
|
4474
|
+
if (!rule) {
|
|
4475
|
+
rule = { id: diagnostic.code, shortDescription: { text: ruleDescription(diagnostic.code) } };
|
|
4476
|
+
if (diagnostic.suggestion) rule.help = { text: diagnostic.suggestion };
|
|
4477
|
+
rules.set(diagnostic.code, rule);
|
|
4478
|
+
} else if (!rule.help && diagnostic.suggestion) {
|
|
4479
|
+
rule.help = { text: diagnostic.suggestion };
|
|
4480
|
+
}
|
|
4481
|
+
results.push(resultOf(diagnostic, opts.root));
|
|
4482
|
+
}
|
|
4483
|
+
return {
|
|
4484
|
+
$schema: SARIF_SCHEMA,
|
|
4485
|
+
version: SARIF_VERSION,
|
|
4486
|
+
runs: [
|
|
4487
|
+
{
|
|
4488
|
+
tool: {
|
|
4489
|
+
driver: {
|
|
4490
|
+
name: TOOL_NAME,
|
|
4491
|
+
informationUri: TOOL_URL,
|
|
4492
|
+
version: opts.version,
|
|
4493
|
+
rules: [...rules.values()]
|
|
4494
|
+
}
|
|
4495
|
+
},
|
|
4496
|
+
results,
|
|
4497
|
+
invocations: [{ executionSuccessful: !(opts.failed ?? false) }]
|
|
4498
|
+
}
|
|
4499
|
+
]
|
|
4500
|
+
};
|
|
4501
|
+
}
|
|
4502
|
+
function toSarifText(opts) {
|
|
4503
|
+
return `${JSON.stringify(toSarifReport(opts), null, 2)}
|
|
4504
|
+
`;
|
|
4505
|
+
}
|
|
4506
|
+
|
|
4507
|
+
// src/doctor.ts
|
|
4508
|
+
import path19 from "path";
|
|
4156
4509
|
async function runDoctor(root) {
|
|
4157
4510
|
const findings = [];
|
|
4158
4511
|
const { workspace, config } = await loadWorkspace(root);
|
|
@@ -4160,7 +4513,7 @@ async function runDoctor(root) {
|
|
|
4160
4513
|
const approvals = await loadApprovals(workspace.sddDir);
|
|
4161
4514
|
findings.push(...approvals.diagnostics);
|
|
4162
4515
|
for (const change of workspace.changes) {
|
|
4163
|
-
const deltaPath =
|
|
4516
|
+
const deltaPath = path19.join(change.dir, "spec.md");
|
|
4164
4517
|
const deltaContent = await readTextIfExists(deltaPath);
|
|
4165
4518
|
const approval = verifyApproval(change, approvals.byArtifact, config, deltaContent ?? void 0);
|
|
4166
4519
|
if ((change.planPath || change.tasks) && (approval.status === "missing" || approval.status === "stale")) {
|
|
@@ -4182,7 +4535,7 @@ async function runDoctor(root) {
|
|
|
4182
4535
|
}
|
|
4183
4536
|
for (const override of change.meta?.overrides ?? []) {
|
|
4184
4537
|
if (!override.reason.trim() || !override.by.trim()) {
|
|
4185
|
-
findings.push(diag("ATLAS-LIFECYCLE-004", "error", `Override del gate "${override.gate}" sin motivo o autor`, { path:
|
|
4538
|
+
findings.push(diag("ATLAS-LIFECYCLE-004", "error", `Override del gate "${override.gate}" sin motivo o autor`, { path: path19.join(change.dir, "meta.yaml") }));
|
|
4186
4539
|
}
|
|
4187
4540
|
}
|
|
4188
4541
|
}
|
|
@@ -4205,7 +4558,7 @@ async function specHashOf(filePath) {
|
|
|
4205
4558
|
}
|
|
4206
4559
|
|
|
4207
4560
|
// src/gate.ts
|
|
4208
|
-
import
|
|
4561
|
+
import path20 from "path";
|
|
4209
4562
|
var UI_DOMAINS2 = /* @__PURE__ */ new Set(["frontend", "mobile", "fullstack"]);
|
|
4210
4563
|
function count(name, diagnostics) {
|
|
4211
4564
|
return {
|
|
@@ -4222,7 +4575,7 @@ function livingRequirementsMap(specs) {
|
|
|
4222
4575
|
return map;
|
|
4223
4576
|
}
|
|
4224
4577
|
async function runCiGate(opts) {
|
|
4225
|
-
const root =
|
|
4578
|
+
const root = path20.resolve(opts.root);
|
|
4226
4579
|
const { workspace, config } = await loadWorkspace(root);
|
|
4227
4580
|
const diagnostics = [];
|
|
4228
4581
|
const checks = [];
|
|
@@ -4233,7 +4586,7 @@ async function runCiGate(opts) {
|
|
|
4233
4586
|
let changesErrors = 0;
|
|
4234
4587
|
let changesWarnings = 0;
|
|
4235
4588
|
for (const change of workspace.changes) {
|
|
4236
|
-
const lintFindings = change.delta ? lintDelta(change.delta, living,
|
|
4589
|
+
const lintFindings = change.delta ? lintDelta(change.delta, living, path20.join(change.dir, "spec.md"), { language: config.spec.language }) : [];
|
|
4237
4590
|
const trace = checkTrace({
|
|
4238
4591
|
specs: workspace.specs,
|
|
4239
4592
|
change,
|
|
@@ -4277,6 +4630,8 @@ async function runCiGate(opts) {
|
|
|
4277
4630
|
return { checks, diagnostics, errors, warnings, failed };
|
|
4278
4631
|
}
|
|
4279
4632
|
export {
|
|
4633
|
+
BACKUP_DIRNAME,
|
|
4634
|
+
BACKUP_POINTER,
|
|
4280
4635
|
BLOCK_HEAD_RE,
|
|
4281
4636
|
BUILTIN_PACKS,
|
|
4282
4637
|
CONFIG_FILE,
|
|
@@ -4286,14 +4641,21 @@ export {
|
|
|
4286
4641
|
RULE_ID_RE,
|
|
4287
4642
|
RULE_RE,
|
|
4288
4643
|
RUN_EVENT_TYPES,
|
|
4644
|
+
SARIF_SCHEMA,
|
|
4645
|
+
SARIF_VERSION,
|
|
4289
4646
|
SCENARIO_HEAD_RE,
|
|
4290
4647
|
SCENARIO_ID_RE,
|
|
4648
|
+
SCHEMA_MIGRATIONS,
|
|
4649
|
+
SCHEMA_VERSION,
|
|
4291
4650
|
SDD_DIR,
|
|
4292
4651
|
SLUG_RE,
|
|
4293
4652
|
TASK_ID_RE,
|
|
4294
4653
|
TASK_LINE_RE,
|
|
4654
|
+
TOOL_NAME,
|
|
4655
|
+
TOOL_URL,
|
|
4295
4656
|
adoptWorkspace,
|
|
4296
4657
|
appendRunEvent,
|
|
4658
|
+
applyUpgrade,
|
|
4297
4659
|
approvalsSchema,
|
|
4298
4660
|
approveFromGithub,
|
|
4299
4661
|
archiveChange,
|
|
@@ -4308,6 +4670,7 @@ export {
|
|
|
4308
4670
|
checkMockups,
|
|
4309
4671
|
checkTrace,
|
|
4310
4672
|
collectMetrics,
|
|
4673
|
+
collectVersionedArtifacts,
|
|
4311
4674
|
commentIssue,
|
|
4312
4675
|
compareTaskIds,
|
|
4313
4676
|
computeInputsHash,
|
|
@@ -4403,8 +4766,10 @@ export {
|
|
|
4403
4766
|
patchChangeMeta,
|
|
4404
4767
|
planBlock,
|
|
4405
4768
|
planMockups,
|
|
4769
|
+
planUpgrade,
|
|
4406
4770
|
planWaves,
|
|
4407
4771
|
profileSchema,
|
|
4772
|
+
readArtifactVersion,
|
|
4408
4773
|
readMockupManifest,
|
|
4409
4774
|
readRun,
|
|
4410
4775
|
readText,
|
|
@@ -4418,23 +4783,30 @@ export {
|
|
|
4418
4783
|
renderTemplate,
|
|
4419
4784
|
requiresMockups,
|
|
4420
4785
|
resolvePacks,
|
|
4786
|
+
rollbackUpgrade,
|
|
4787
|
+
ruleDescription,
|
|
4421
4788
|
runAnalyze,
|
|
4422
4789
|
runCiGate,
|
|
4423
4790
|
runDoctor,
|
|
4424
4791
|
runProcess,
|
|
4792
|
+
sarifLevel,
|
|
4425
4793
|
setMockupRequirement,
|
|
4426
4794
|
sha256,
|
|
4427
4795
|
shortHash,
|
|
4428
4796
|
signApproval,
|
|
4429
4797
|
specHashOf,
|
|
4430
4798
|
splitCommand,
|
|
4799
|
+
stampSchemaVersion,
|
|
4431
4800
|
stateLabel,
|
|
4432
4801
|
syncGithubIssue,
|
|
4433
4802
|
templatesFor,
|
|
4434
4803
|
toPosix,
|
|
4804
|
+
toSarifReport,
|
|
4805
|
+
toSarifText,
|
|
4435
4806
|
tokensFile,
|
|
4436
4807
|
updateMockupScreenshots,
|
|
4437
4808
|
updateRunStatus,
|
|
4809
|
+
upgradeAdvisory,
|
|
4438
4810
|
verifyApproval,
|
|
4439
4811
|
walkFiles,
|
|
4440
4812
|
writeConfig,
|