@specatlas/core 0.1.25 → 0.1.27
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 +85 -4
- package/dist/index.js +304 -26
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -195,6 +195,17 @@ interface MockupManifest {
|
|
|
195
195
|
tokens?: string;
|
|
196
196
|
screens: MockupScreen[];
|
|
197
197
|
}
|
|
198
|
+
interface ClarifyItem {
|
|
199
|
+
text: string;
|
|
200
|
+
line: number;
|
|
201
|
+
answer?: string;
|
|
202
|
+
}
|
|
203
|
+
interface ClarifyFile {
|
|
204
|
+
path: string;
|
|
205
|
+
open: ClarifyItem[];
|
|
206
|
+
resolved: ClarifyItem[];
|
|
207
|
+
diagnostics: Diagnostic[];
|
|
208
|
+
}
|
|
198
209
|
interface Change {
|
|
199
210
|
slug: string;
|
|
200
211
|
dir: string;
|
|
@@ -204,6 +215,9 @@ interface Change {
|
|
|
204
215
|
verify?: VerifyFile;
|
|
205
216
|
fix?: VerifyFile;
|
|
206
217
|
fixCovers?: string[];
|
|
218
|
+
clarify?: ClarifyFile;
|
|
219
|
+
clarifyPath?: string;
|
|
220
|
+
docsPaths?: string[];
|
|
207
221
|
planPath?: string;
|
|
208
222
|
reviewPath?: string;
|
|
209
223
|
presentationPath?: string;
|
|
@@ -295,6 +309,20 @@ declare const atlasConfigSchema: z.ZodObject<{
|
|
|
295
309
|
}, {
|
|
296
310
|
mode?: "off" | "advisory" | "blocking" | undefined;
|
|
297
311
|
}>>;
|
|
312
|
+
clarify: z.ZodDefault<z.ZodObject<{
|
|
313
|
+
mode: z.ZodDefault<z.ZodEnum<["off", "advisory", "blocking"]>>;
|
|
314
|
+
}, "strip", z.ZodTypeAny, {
|
|
315
|
+
mode: "off" | "advisory" | "blocking";
|
|
316
|
+
}, {
|
|
317
|
+
mode?: "off" | "advisory" | "blocking" | undefined;
|
|
318
|
+
}>>;
|
|
319
|
+
docs: z.ZodDefault<z.ZodObject<{
|
|
320
|
+
mode: z.ZodDefault<z.ZodEnum<["off", "advisory", "blocking"]>>;
|
|
321
|
+
}, "strip", z.ZodTypeAny, {
|
|
322
|
+
mode: "off" | "advisory" | "blocking";
|
|
323
|
+
}, {
|
|
324
|
+
mode?: "off" | "advisory" | "blocking" | undefined;
|
|
325
|
+
}>>;
|
|
298
326
|
mockup: z.ZodDefault<z.ZodObject<{
|
|
299
327
|
require_approval: z.ZodDefault<z.ZodBoolean>;
|
|
300
328
|
compare_in_verify: z.ZodDefault<z.ZodBoolean>;
|
|
@@ -319,6 +347,12 @@ declare const atlasConfigSchema: z.ZodObject<{
|
|
|
319
347
|
review: {
|
|
320
348
|
mode: "off" | "advisory" | "blocking";
|
|
321
349
|
};
|
|
350
|
+
clarify: {
|
|
351
|
+
mode: "off" | "advisory" | "blocking";
|
|
352
|
+
};
|
|
353
|
+
docs: {
|
|
354
|
+
mode: "off" | "advisory" | "blocking";
|
|
355
|
+
};
|
|
322
356
|
mockup: {
|
|
323
357
|
require_approval: boolean;
|
|
324
358
|
compare_in_verify: boolean;
|
|
@@ -337,6 +371,12 @@ declare const atlasConfigSchema: z.ZodObject<{
|
|
|
337
371
|
review?: {
|
|
338
372
|
mode?: "off" | "advisory" | "blocking" | undefined;
|
|
339
373
|
} | undefined;
|
|
374
|
+
clarify?: {
|
|
375
|
+
mode?: "off" | "advisory" | "blocking" | undefined;
|
|
376
|
+
} | undefined;
|
|
377
|
+
docs?: {
|
|
378
|
+
mode?: "off" | "advisory" | "blocking" | undefined;
|
|
379
|
+
} | undefined;
|
|
340
380
|
mockup?: {
|
|
341
381
|
require_approval?: boolean | undefined;
|
|
342
382
|
compare_in_verify?: boolean | undefined;
|
|
@@ -452,6 +492,12 @@ declare const atlasConfigSchema: z.ZodObject<{
|
|
|
452
492
|
review: {
|
|
453
493
|
mode: "off" | "advisory" | "blocking";
|
|
454
494
|
};
|
|
495
|
+
clarify: {
|
|
496
|
+
mode: "off" | "advisory" | "blocking";
|
|
497
|
+
};
|
|
498
|
+
docs: {
|
|
499
|
+
mode: "off" | "advisory" | "blocking";
|
|
500
|
+
};
|
|
455
501
|
mockup: {
|
|
456
502
|
require_approval: boolean;
|
|
457
503
|
compare_in_verify: boolean;
|
|
@@ -515,6 +561,12 @@ declare const atlasConfigSchema: z.ZodObject<{
|
|
|
515
561
|
review?: {
|
|
516
562
|
mode?: "off" | "advisory" | "blocking" | undefined;
|
|
517
563
|
} | undefined;
|
|
564
|
+
clarify?: {
|
|
565
|
+
mode?: "off" | "advisory" | "blocking" | undefined;
|
|
566
|
+
} | undefined;
|
|
567
|
+
docs?: {
|
|
568
|
+
mode?: "off" | "advisory" | "blocking" | undefined;
|
|
569
|
+
} | undefined;
|
|
518
570
|
mockup?: {
|
|
519
571
|
require_approval?: boolean | undefined;
|
|
520
572
|
compare_in_verify?: boolean | undefined;
|
|
@@ -803,6 +855,8 @@ declare function parseApprovals(raw: string, filePath: string): {
|
|
|
803
855
|
diagnostics: Diagnostic[];
|
|
804
856
|
};
|
|
805
857
|
|
|
858
|
+
declare function parseClarify(content: string, filePath: string): ClarifyFile;
|
|
859
|
+
|
|
806
860
|
interface GlossaryTerm {
|
|
807
861
|
term: string;
|
|
808
862
|
definition: string;
|
|
@@ -910,7 +964,7 @@ declare function planWaves(tasks: TasksFile, opts?: {
|
|
|
910
964
|
}): WavePlan;
|
|
911
965
|
declare function planBlock(block: TaskBlock, maxParallel: number): BlockWavePlan;
|
|
912
966
|
|
|
913
|
-
type ChangeState = 'draft' | 'spec_draft' | 'awaiting_mockups' | 'awaiting_approval' | 'approved' | 'planned' | 'building' | 'built' | 'verified' | 'ready' | 'archived';
|
|
967
|
+
type ChangeState = 'draft' | 'spec_draft' | 'awaiting_mockups' | 'awaiting_approval' | 'approved' | 'planned' | 'building' | 'built' | 'verified' | 'reviewed' | 'ready' | 'archived';
|
|
914
968
|
interface NextAction {
|
|
915
969
|
command: string;
|
|
916
970
|
description: string;
|
|
@@ -946,6 +1000,9 @@ interface DeriveInput {
|
|
|
946
1000
|
specContent?: string;
|
|
947
1001
|
}
|
|
948
1002
|
declare function requiresMockups(meta: ChangeMeta | undefined, cfg: AtlasConfig): boolean;
|
|
1003
|
+
declare function docsReady(change: Change): boolean;
|
|
1004
|
+
declare function clarifyAdvisory(change: Change, cfg: AtlasConfig): Diagnostic[];
|
|
1005
|
+
declare function docsAdvisory(change: Change, cfg: AtlasConfig): Diagnostic[];
|
|
949
1006
|
declare function deriveState(input: DeriveInput): DerivedState;
|
|
950
1007
|
declare function stateLabel(state: ChangeState): string;
|
|
951
1008
|
|
|
@@ -1125,6 +1182,8 @@ interface TemplateSet {
|
|
|
1125
1182
|
tasks: string;
|
|
1126
1183
|
verify: string;
|
|
1127
1184
|
fix: string;
|
|
1185
|
+
docTecnica: string;
|
|
1186
|
+
docManual: string;
|
|
1128
1187
|
}
|
|
1129
1188
|
declare function templatesFor(language: Language): TemplateSet;
|
|
1130
1189
|
declare function renderTemplate(template: string, vars: Record<string, string>): string;
|
|
@@ -1804,10 +1863,11 @@ interface LivingFix {
|
|
|
1804
1863
|
file: string;
|
|
1805
1864
|
date: string;
|
|
1806
1865
|
result: string;
|
|
1807
|
-
domain?: string;
|
|
1808
|
-
title?: string;
|
|
1809
1866
|
covers: string[];
|
|
1810
1867
|
content: string;
|
|
1868
|
+
source: 'living' | 'archive';
|
|
1869
|
+
domain?: string;
|
|
1870
|
+
title?: string;
|
|
1811
1871
|
}
|
|
1812
1872
|
declare function parseFixCovers(content: string): string[];
|
|
1813
1873
|
declare function parseLivingFix(content: string, file: string): LivingFix;
|
|
@@ -1828,6 +1888,27 @@ interface WriteLivingFixResult {
|
|
|
1828
1888
|
}
|
|
1829
1889
|
declare function writeLivingFix(root: string, input: WriteLivingFixInput): Promise<WriteLivingFixResult>;
|
|
1830
1890
|
|
|
1891
|
+
type DocsTipo = 'tecnica' | 'manual' | 'all';
|
|
1892
|
+
declare const DOCS_MARKER_START = "<!-- specatlas:generado:inicio -->";
|
|
1893
|
+
declare const DOCS_MARKER_END = "<!-- specatlas:generado:fin -->";
|
|
1894
|
+
interface GenerateDocsOptions {
|
|
1895
|
+
root: string;
|
|
1896
|
+
slug: string;
|
|
1897
|
+
tipo?: DocsTipo;
|
|
1898
|
+
now?: Date;
|
|
1899
|
+
}
|
|
1900
|
+
interface GeneratedDoc {
|
|
1901
|
+
tipo: 'tecnica' | 'manual';
|
|
1902
|
+
path: string;
|
|
1903
|
+
created: boolean;
|
|
1904
|
+
}
|
|
1905
|
+
interface GenerateDocsResult {
|
|
1906
|
+
slug: string;
|
|
1907
|
+
files: GeneratedDoc[];
|
|
1908
|
+
diagnostics: Diagnostic[];
|
|
1909
|
+
}
|
|
1910
|
+
declare function generateDocs(opts: GenerateDocsOptions): Promise<GenerateDocsResult>;
|
|
1911
|
+
|
|
1831
1912
|
declare const SARIF_SCHEMA = "https://json.schemastore.org/sarif-2.1.0.json";
|
|
1832
1913
|
declare const SARIF_VERSION = "2.1.0";
|
|
1833
1914
|
declare const TOOL_NAME = "SpecAtlas";
|
|
@@ -1927,4 +2008,4 @@ declare function livingRequirementsMap(specs: Array<{
|
|
|
1927
2008
|
}>): Map<string, Requirement>;
|
|
1928
2009
|
declare function runCiGate(opts: CiOptions): Promise<CiResult>;
|
|
1929
2010
|
|
|
1930
|
-
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, LIVING_FIXES_DIR, type Lane, type Language, type LinkedIssue, type LintOptions, type LivingFix, 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, type WriteLivingFixInput, type WriteLivingFixResult, 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, loadLivingFixes, loadProfileFile, loadProfilesFromDir, loadProjectPacks, loadSpecs, loadWorkspace, localCompact, localDate, localMonth, localOffset, localStamp, matchGlob, mockupManifestSchema, mockupsDir, mockupsReady, moveDirectory, packFindings, parseApprovals, parseChangeMeta, parseConfig, parseDelta, parseFixCovers, parseFrontmatter, parseGitHubRemote, parseGlossary, parseIssueUrl, parseLivingFix, 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, writeLivingFix, writeMockupManifest, writeMockupPlan, writeText };
|
|
2011
|
+
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 ClarifyFile, type ClarifyItem, DOCS_MARKER_END, DOCS_MARKER_START, type Delta, type DeltaOp, type DeriveInput, type DerivedState, type DetectionResult, type Diagnostic, type DocsTipo, type DoctorReport, type Domain, type Evidence, type EvidenceMethod, type EvidenceResult, type ExecOptions, type ExecResult, type FoldOutcome, type FrontmatterResult, type GenerateDocsOptions, type GenerateDocsResult, type GeneratedDoc, type GhRunner, type GitHubRepo, type GlossaryTerm, type ImpactEvidence, type ImpactReport, type ImpactTask, type InitOptions, type InitResult, type IssueBodyInput, LIVING_FIXES_DIR, type Lane, type Language, type LinkedIssue, type LintOptions, type LivingFix, 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, type WriteLivingFixInput, type WriteLivingFixResult, adoptWorkspace, appendRunEvent, applyUpgrade, approvalsSchema, approveFromGithub, archiveChange, artifactHash, atlasConfigSchema, buildTraceGraph, canonicalizeMarkdown, captureMockups, changeMarker, changeMetaSchema, changeMetaYaml, checkMockups, checkTrace, clarifyAdvisory, collectMetrics, collectVersionedArtifacts, commentIssue, compareTaskIds, computeInputsHash, configToYaml, copyFile, countBySeverity, createChange, createIssue, createRun, defaultConfig, deriveState, detectProfiles, detectRepo, detectionToYaml, diag, docsAdvisory, docsReady, editIssue, emitFrontmatter, ensureDir, ensureSddDirs, esc, evaluatePacks, evidenceSummary, exists, findLinkedIssue, findWorkspaceRoot, firstToken, foldDelta, generateDocs, 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, loadLivingFixes, loadProfileFile, loadProfilesFromDir, loadProjectPacks, loadSpecs, loadWorkspace, localCompact, localDate, localMonth, localOffset, localStamp, matchGlob, mockupManifestSchema, mockupsDir, mockupsReady, moveDirectory, packFindings, parseApprovals, parseChangeMeta, parseClarify, parseConfig, parseDelta, parseFixCovers, parseFrontmatter, parseGitHubRemote, parseGlossary, parseIssueUrl, parseLivingFix, 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, writeLivingFix, writeMockupManifest, writeMockupPlan, writeText };
|
package/dist/index.js
CHANGED
|
@@ -61,6 +61,8 @@ var atlasConfigSchema = z.object({
|
|
|
61
61
|
analyze: z.object({ mode: z.enum(["off", "advisory", "blocking"]).default("blocking"), min_severity: z.enum(["low", "medium", "high"]).default("medium") }).default({}),
|
|
62
62
|
verify: z.object({ mode: z.enum(["off", "advisory", "blocking"]).default("blocking"), require_evidence: z.boolean().default(true) }).default({}),
|
|
63
63
|
review: z.object({ mode: z.enum(["off", "advisory", "blocking"]).default("advisory") }).default({}),
|
|
64
|
+
clarify: z.object({ mode: z.enum(["off", "advisory", "blocking"]).default("advisory") }).default({}),
|
|
65
|
+
docs: z.object({ mode: z.enum(["off", "advisory", "blocking"]).default("blocking") }).default({}),
|
|
64
66
|
mockup: z.object({ require_approval: z.boolean().default(false), compare_in_verify: z.boolean().default(false) }).default({})
|
|
65
67
|
}).default({}),
|
|
66
68
|
trace: z.object({ mode: z.enum(["off", "advisory", "blocking"]).default("blocking"), prefix: z.string().default("REQ") }).default({}),
|
|
@@ -640,6 +642,35 @@ function findScenarioHeading(lines, blockLineIndex) {
|
|
|
640
642
|
return void 0;
|
|
641
643
|
}
|
|
642
644
|
|
|
645
|
+
// src/parse/clarify.ts
|
|
646
|
+
var OPEN_RE = /^\s*-\s*\[\s\]\s+(.+?)\s*$/;
|
|
647
|
+
var DONE_RE = /^\s*-\s*\[[xX]\]\s+(.+?)\s*$/;
|
|
648
|
+
var ANSWER_SEPARATOR = " \u2014 ";
|
|
649
|
+
function splitAnswer(raw) {
|
|
650
|
+
const index = raw.indexOf(ANSWER_SEPARATOR);
|
|
651
|
+
if (index === -1) return { text: raw.trim() };
|
|
652
|
+
return { text: raw.slice(0, index).trim(), answer: raw.slice(index + ANSWER_SEPARATOR.length).trim() };
|
|
653
|
+
}
|
|
654
|
+
function parseClarify(content, filePath) {
|
|
655
|
+
const fm = parseFrontmatter(content, filePath);
|
|
656
|
+
const lines = fm.body.replace(/\r\n?/g, "\n").split("\n");
|
|
657
|
+
const open = [];
|
|
658
|
+
const resolved = [];
|
|
659
|
+
for (let i = 0; i < lines.length; i += 1) {
|
|
660
|
+
const line = lines[i] ?? "";
|
|
661
|
+
const lineNo = fm.bodyStartLine + i;
|
|
662
|
+
const done = DONE_RE.exec(line);
|
|
663
|
+
if (done) {
|
|
664
|
+
const item = splitAnswer(done[1] ?? "");
|
|
665
|
+
resolved.push({ text: item.text, line: lineNo, ...item.answer !== void 0 ? { answer: item.answer } : {} });
|
|
666
|
+
continue;
|
|
667
|
+
}
|
|
668
|
+
const pending = OPEN_RE.exec(line);
|
|
669
|
+
if (pending) open.push({ text: pending[1] ?? "", line: lineNo });
|
|
670
|
+
}
|
|
671
|
+
return { path: filePath, open, resolved, diagnostics: [] };
|
|
672
|
+
}
|
|
673
|
+
|
|
643
674
|
// src/parse/glossary.ts
|
|
644
675
|
var HEADER_KEYS = /* @__PURE__ */ new Set(["t\xE9rmino", "termino", "term", "definici\xF3n", "definicion", "definition", "sin\xF3nimos", "sinonimos", "synonyms"]);
|
|
645
676
|
var SEPARATOR_RE = /^:?-{2,}:?$/;
|
|
@@ -794,14 +825,14 @@ function wordBoundary(text, term) {
|
|
|
794
825
|
const re = new RegExp(`(?<![\\p{L}\\p{N}])${escaped}(?![\\p{L}\\p{N}])`, "iu");
|
|
795
826
|
return re.test(text);
|
|
796
827
|
}
|
|
797
|
-
function lintText(text, code, terms, label,
|
|
828
|
+
function lintText(text, code, terms, label, path23, line) {
|
|
798
829
|
const out = [];
|
|
799
830
|
const lower = text.toLowerCase();
|
|
800
831
|
for (const term of terms) {
|
|
801
832
|
if (wordBoundary(lower, term)) {
|
|
802
833
|
out.push(
|
|
803
834
|
diag(code, "error", `${label}: "${term}"`, {
|
|
804
|
-
path:
|
|
835
|
+
path: path23,
|
|
805
836
|
line,
|
|
806
837
|
suggestion: "La especificaci\xF3n es funcional y de negocio: describe comportamiento, no tecnolog\xEDa ni adjetivos vagos"
|
|
807
838
|
})
|
|
@@ -810,7 +841,7 @@ function lintText(text, code, terms, label, path22, line) {
|
|
|
810
841
|
}
|
|
811
842
|
return out;
|
|
812
843
|
}
|
|
813
|
-
function lintRequirement(req,
|
|
844
|
+
function lintRequirement(req, path23, opts = {}) {
|
|
814
845
|
const out = [];
|
|
815
846
|
const vague = opts.language === "en" ? VAGUE_EN : VAGUE_ES;
|
|
816
847
|
const tech = opts.language === "en" ? TECH_EN : TECH_ES;
|
|
@@ -821,32 +852,32 @@ function lintRequirement(req, path22, opts = {}) {
|
|
|
821
852
|
...req.scenarios.flatMap((s) => [...s.when.map((w) => ({ text: w, line: s.line })), ...s.then.map((t) => ({ text: t, line: s.line }))])
|
|
822
853
|
];
|
|
823
854
|
for (const part of parts) {
|
|
824
|
-
out.push(...lintText(part.text, "LINT-BIZ-002", vague, "Palabra vaga en la especificaci\xF3n",
|
|
855
|
+
out.push(...lintText(part.text, "LINT-BIZ-002", vague, "Palabra vaga en la especificaci\xF3n", path23, part.line));
|
|
825
856
|
if (opts.businessOnly !== false) {
|
|
826
|
-
out.push(...lintText(part.text, "LINT-BIZ-001", tech, "Jerga t\xE9cnica en la especificaci\xF3n de negocio",
|
|
857
|
+
out.push(...lintText(part.text, "LINT-BIZ-001", tech, "Jerga t\xE9cnica en la especificaci\xF3n de negocio", path23, part.line));
|
|
827
858
|
}
|
|
828
859
|
}
|
|
829
860
|
if (req.scenarios.length === 0) {
|
|
830
|
-
out.push(diag("TRACE-001", "error", `El requisito ${req.id} no tiene ning\xFAn escenario`, { path:
|
|
861
|
+
out.push(diag("TRACE-001", "error", `El requisito ${req.id} no tiene ning\xFAn escenario`, { path: path23, line: req.line, suggestion: "A\xF1ade al menos un escenario CUANDO/ENTONCES" }));
|
|
831
862
|
}
|
|
832
863
|
return out;
|
|
833
864
|
}
|
|
834
|
-
function lintDelta(delta, livingRequirements,
|
|
865
|
+
function lintDelta(delta, livingRequirements, path23, opts = {}) {
|
|
835
866
|
const out = [...delta.diagnostics];
|
|
836
867
|
for (const req of [...delta.added, ...delta.modified]) {
|
|
837
|
-
out.push(...lintRequirement(req,
|
|
868
|
+
out.push(...lintRequirement(req, path23, opts));
|
|
838
869
|
}
|
|
839
870
|
for (const req of delta.modified) {
|
|
840
871
|
const living = livingRequirements.get(req.id);
|
|
841
872
|
if (!living) {
|
|
842
|
-
out.push(diag("TRACE-007", "error", `MODIFIED ${req.id} no existe en la spec viva; usa ADDED`, { path:
|
|
873
|
+
out.push(diag("TRACE-007", "error", `MODIFIED ${req.id} no existe en la spec viva; usa ADDED`, { path: path23, line: req.line }));
|
|
843
874
|
continue;
|
|
844
875
|
}
|
|
845
876
|
for (const existing of living.scenarios) {
|
|
846
877
|
if (!req.scenarios.some((s) => s.id === existing.id)) {
|
|
847
878
|
out.push(
|
|
848
879
|
diag("TRACE-007", "error", `MODIFIED ${req.id} pierde el escenario ${existing.id}: copia el bloque completo`, {
|
|
849
|
-
path:
|
|
880
|
+
path: path23,
|
|
850
881
|
line: req.line,
|
|
851
882
|
suggestion: "Copia el bloque completo de la spec viva y ed\xEDtalo; para quitarlo, decl\xE1ralo en REMOVED"
|
|
852
883
|
})
|
|
@@ -857,15 +888,15 @@ function lintDelta(delta, livingRequirements, path22, opts = {}) {
|
|
|
857
888
|
for (const req of delta.removed) {
|
|
858
889
|
const living = livingRequirements.get(req.id);
|
|
859
890
|
if (!living) {
|
|
860
|
-
out.push(diag("TRACE-007", "error", `REMOVED ${req.id} no existe en la spec viva`, { path:
|
|
891
|
+
out.push(diag("TRACE-007", "error", `REMOVED ${req.id} no existe en la spec viva`, { path: path23, line: req.line }));
|
|
861
892
|
}
|
|
862
893
|
}
|
|
863
894
|
for (const rename2 of delta.renamed) {
|
|
864
895
|
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:
|
|
896
|
+
out.push(diag("LINT-DLT-003", "error", `RENAMED cambia el id (${rename2.from.id} \u2192 ${rename2.to.id}); los ids son inmutables`, { path: path23, line: rename2.line }));
|
|
866
897
|
}
|
|
867
898
|
if (!livingRequirements.has(rename2.from.id)) {
|
|
868
|
-
out.push(diag("TRACE-007", "error", `RENAMED ${rename2.from.id} no existe en la spec viva`, { path:
|
|
899
|
+
out.push(diag("TRACE-007", "error", `RENAMED ${rename2.from.id} no existe en la spec viva`, { path: path23, line: rename2.line }));
|
|
869
900
|
}
|
|
870
901
|
}
|
|
871
902
|
return out;
|
|
@@ -895,7 +926,7 @@ var MERMAID_KEYWORDS = [
|
|
|
895
926
|
"architecture-beta"
|
|
896
927
|
];
|
|
897
928
|
var MERMAID_BLOCKS = /^\s*(alt|loop|opt|par|rect|critical|break|subgraph)\b/;
|
|
898
|
-
function lintPlan(planText,
|
|
929
|
+
function lintPlan(planText, path23) {
|
|
899
930
|
const out = [];
|
|
900
931
|
const blocks = [...planText.matchAll(/```mermaid\r?\n([\s\S]*?)```/g)];
|
|
901
932
|
for (const [index, block] of blocks.entries()) {
|
|
@@ -905,7 +936,7 @@ function lintPlan(planText, path22) {
|
|
|
905
936
|
if (!MERMAID_KEYWORDS.some((keyword) => first.startsWith(keyword))) {
|
|
906
937
|
out.push(
|
|
907
938
|
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:
|
|
939
|
+
path: path23,
|
|
909
940
|
suggestion: "Corrige el tipo del diagrama o elimina el bloque"
|
|
910
941
|
})
|
|
911
942
|
);
|
|
@@ -919,7 +950,7 @@ function lintPlan(planText, path22) {
|
|
|
919
950
|
if (open !== 0) {
|
|
920
951
|
out.push(
|
|
921
952
|
diag("LINT-PLN-002", "error", `Diagrama mermaid ${index + 1}: faltan ${Math.abs(open)} \`end\` (bloques alt/loop/subgraph sin cerrar)`, {
|
|
922
|
-
path:
|
|
953
|
+
path: path23,
|
|
923
954
|
suggestion: "Cierra cada bloque alt/loop/opt/par/rect/subgraph con `end`"
|
|
924
955
|
})
|
|
925
956
|
);
|
|
@@ -931,7 +962,7 @@ function lintPlan(planText, path22) {
|
|
|
931
962
|
if (message.includes(";")) {
|
|
932
963
|
out.push(
|
|
933
964
|
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:
|
|
965
|
+
path: path23,
|
|
935
966
|
suggestion: "Sustituye `;` por `\xB7` o `,` en los mensajes de sequenceDiagram"
|
|
936
967
|
})
|
|
937
968
|
);
|
|
@@ -1240,6 +1271,29 @@ function requiresMockups(meta, cfg) {
|
|
|
1240
1271
|
function mockupOverride(change) {
|
|
1241
1272
|
return (change.meta?.overrides ?? []).some((override) => override.gate === "mockup");
|
|
1242
1273
|
}
|
|
1274
|
+
function docsReady(change) {
|
|
1275
|
+
const paths = change.docsPaths ?? [];
|
|
1276
|
+
return paths.some((p) => p.endsWith("tecnica.md")) && paths.some((p) => p.endsWith("manual.md"));
|
|
1277
|
+
}
|
|
1278
|
+
function clarifyAdvisory(change, cfg) {
|
|
1279
|
+
const open = change.clarify?.open.length ?? 0;
|
|
1280
|
+
if (cfg.gates.clarify.mode !== "advisory" || open === 0) return [];
|
|
1281
|
+
return [
|
|
1282
|
+
diag("ATLAS-CLARIFY-001", "warning", `El cambio "${change.slug}" tiene ${open} pregunta(s) sin aclarar`, {
|
|
1283
|
+
...change.clarifyPath !== void 0 ? { path: change.clarifyPath } : {},
|
|
1284
|
+
suggestion: `Aclara antes de planificar: /satlas.clarify ${change.slug} (o satlas clarify ${change.slug})`
|
|
1285
|
+
})
|
|
1286
|
+
];
|
|
1287
|
+
}
|
|
1288
|
+
function docsAdvisory(change, cfg) {
|
|
1289
|
+
const lane = change.meta?.lane ?? cfg.lanes.default;
|
|
1290
|
+
if (lane !== "full" || cfg.gates.docs.mode !== "advisory" || docsReady(change)) return [];
|
|
1291
|
+
return [
|
|
1292
|
+
diag("ATLAS-DOCS-001", "warning", `El cambio "${change.slug}" (carril completo) no tiene su documentaci\xF3n t\xE9cnica y manual`, {
|
|
1293
|
+
suggestion: `Genera la documentaci\xF3n: satlas docs ${change.slug} (o /satlas.docs ${change.slug})`
|
|
1294
|
+
})
|
|
1295
|
+
];
|
|
1296
|
+
}
|
|
1243
1297
|
function deriveState(input) {
|
|
1244
1298
|
const { change, cfg, approval, blockingFindings } = input;
|
|
1245
1299
|
const lane = change.meta?.lane ?? cfg.lanes.default;
|
|
@@ -1302,6 +1356,16 @@ function deriveState(input) {
|
|
|
1302
1356
|
};
|
|
1303
1357
|
}
|
|
1304
1358
|
if (!change.planPath && !change.tasks) {
|
|
1359
|
+
const openQuestions = change.clarify?.open.length ?? 0;
|
|
1360
|
+
if (openQuestions > 0 && cfg.gates.clarify.mode === "blocking") {
|
|
1361
|
+
blockedBy.push(`aclaraci\xF3n pendiente (${openQuestions})`);
|
|
1362
|
+
return {
|
|
1363
|
+
state: "approved",
|
|
1364
|
+
blockedBy,
|
|
1365
|
+
nextAction: next(`/satlas.clarify ${change.slug}`, `Aclarar ${openQuestions} pregunta(s) antes de planificar`, true),
|
|
1366
|
+
progress
|
|
1367
|
+
};
|
|
1368
|
+
}
|
|
1305
1369
|
return { state: "approved", blockedBy, nextAction: next(`/satlas.plan ${change.slug}`, "Crear el plan t\xE9cnico y las tareas", true), progress };
|
|
1306
1370
|
}
|
|
1307
1371
|
if (tasksTotal > 0 && tasksDone < tasksTotal) {
|
|
@@ -1315,6 +1379,10 @@ function deriveState(input) {
|
|
|
1315
1379
|
blockedBy.push("review pendiente");
|
|
1316
1380
|
return { state: "verified", blockedBy, nextAction: next(`/satlas.review ${change.slug}`, "Revisi\xF3n de c\xF3digo", true), progress };
|
|
1317
1381
|
}
|
|
1382
|
+
if (lane === "full" && cfg.gates.docs.mode === "blocking" && !docsReady(change)) {
|
|
1383
|
+
blockedBy.push("documentaci\xF3n pendiente");
|
|
1384
|
+
return { state: "reviewed", blockedBy, nextAction: next(`/satlas.docs ${change.slug}`, "Generar la documentaci\xF3n t\xE9cnica y manual del cambio", true), progress };
|
|
1385
|
+
}
|
|
1318
1386
|
return { state: "ready", blockedBy, nextAction: next(`satlas archive ${change.slug}`, "Archivar el cambio y plegar los deltas"), progress };
|
|
1319
1387
|
}
|
|
1320
1388
|
function stateLabel(state) {
|
|
@@ -1328,6 +1396,7 @@ function stateLabel(state) {
|
|
|
1328
1396
|
building: "construyendo",
|
|
1329
1397
|
built: "construido",
|
|
1330
1398
|
verified: "verificado",
|
|
1399
|
+
reviewed: "revisado",
|
|
1331
1400
|
ready: "listo para archivar",
|
|
1332
1401
|
archived: "archivado"
|
|
1333
1402
|
};
|
|
@@ -1513,12 +1582,43 @@ function parseLivingFix(content, file) {
|
|
|
1513
1582
|
date: typeof data["date"] === "string" ? data["date"] : "",
|
|
1514
1583
|
result: typeof data["result"] === "string" ? data["result"] : "pass",
|
|
1515
1584
|
covers: coversOf(data["covers"]),
|
|
1516
|
-
content: fm.body.trimStart()
|
|
1585
|
+
content: fm.body.trimStart(),
|
|
1586
|
+
source: "living"
|
|
1517
1587
|
};
|
|
1518
1588
|
if (typeof data["domain"] === "string") fix.domain = data["domain"];
|
|
1519
1589
|
if (typeof data["title"] === "string") fix.title = data["title"];
|
|
1520
1590
|
return fix;
|
|
1521
1591
|
}
|
|
1592
|
+
async function archivedFixes(root, known) {
|
|
1593
|
+
const archiveDir = path4.join(root, ".sdd", "changes", "archive");
|
|
1594
|
+
const fixes = [];
|
|
1595
|
+
for (const entry of await listDirs(archiveDir)) {
|
|
1596
|
+
const dir = path4.join(archiveDir, entry);
|
|
1597
|
+
const fixFile = path4.join(dir, "fix.md");
|
|
1598
|
+
const fixRaw = await readTextIfExists(fixFile);
|
|
1599
|
+
if (fixRaw === void 0) continue;
|
|
1600
|
+
const metaFile = path4.join(dir, "meta.yaml");
|
|
1601
|
+
const metaRaw = await readTextIfExists(metaFile);
|
|
1602
|
+
const meta = metaRaw !== void 0 ? parseChangeMeta(metaRaw, metaFile).meta : void 0;
|
|
1603
|
+
if (meta?.lane !== "fix") continue;
|
|
1604
|
+
const slug = entry.replace(/^\d{4}-\d{2}-/, "");
|
|
1605
|
+
if (known.has(slug)) continue;
|
|
1606
|
+
const evidence = parseVerifyFile(fixRaw, fixFile).evidence;
|
|
1607
|
+
const fix = {
|
|
1608
|
+
slug,
|
|
1609
|
+
file: fixFile,
|
|
1610
|
+
date: /^(\d{4}-\d{2})/.exec(entry)?.[1] ?? "",
|
|
1611
|
+
result: evidence.some((e) => e.result === "pass") ? "pass" : evidence.length > 0 ? "fail" : "pending",
|
|
1612
|
+
covers: parseFixCovers(fixRaw),
|
|
1613
|
+
content: parseFrontmatter(fixRaw, fixFile).body.trimStart(),
|
|
1614
|
+
source: "archive"
|
|
1615
|
+
};
|
|
1616
|
+
if (meta.domain !== void 0) fix.domain = meta.domain;
|
|
1617
|
+
if (meta.title !== void 0) fix.title = meta.title;
|
|
1618
|
+
fixes.push(fix);
|
|
1619
|
+
}
|
|
1620
|
+
return fixes;
|
|
1621
|
+
}
|
|
1522
1622
|
async function loadLivingFixes(root) {
|
|
1523
1623
|
const dir = path4.join(root, LIVING_FIXES_DIR);
|
|
1524
1624
|
const entries = (await listDir(dir)).filter((entry) => entry.toLowerCase().endsWith(".md")).sort();
|
|
@@ -1529,6 +1629,7 @@ async function loadLivingFixes(root) {
|
|
|
1529
1629
|
if (content === void 0) continue;
|
|
1530
1630
|
fixes.push(parseLivingFix(content, file));
|
|
1531
1631
|
}
|
|
1632
|
+
fixes.push(...await archivedFixes(root, new Set(fixes.map((fix) => fix.slug))));
|
|
1532
1633
|
return fixes.sort((a, b) => b.date.localeCompare(a.date) || b.slug.localeCompare(a.slug));
|
|
1533
1634
|
}
|
|
1534
1635
|
async function writeLivingFix(root, input) {
|
|
@@ -1636,6 +1737,19 @@ async function loadChange(root, slug, relDir) {
|
|
|
1636
1737
|
diagnostics.push(...change.fix.diagnostics);
|
|
1637
1738
|
change.fixCovers = parseFixCovers(fixRaw);
|
|
1638
1739
|
}
|
|
1740
|
+
const clarifyFile = path5.join(dir, "clarify.md");
|
|
1741
|
+
const clarifyRaw = await readTextIfExists(clarifyFile);
|
|
1742
|
+
if (clarifyRaw !== void 0) {
|
|
1743
|
+
change.clarify = parseClarify(clarifyRaw, clarifyFile);
|
|
1744
|
+
change.clarifyPath = clarifyFile;
|
|
1745
|
+
diagnostics.push(...change.clarify.diagnostics);
|
|
1746
|
+
}
|
|
1747
|
+
const docsPaths = [];
|
|
1748
|
+
for (const name of ["tecnica.md", "manual.md"]) {
|
|
1749
|
+
const docFile = path5.join(dir, "docs", name);
|
|
1750
|
+
if (await exists(docFile)) docsPaths.push(docFile);
|
|
1751
|
+
}
|
|
1752
|
+
if (docsPaths.length > 0) change.docsPaths = docsPaths;
|
|
1639
1753
|
const mockupManifest = path5.join(dir, "mockups", "manifest.yaml");
|
|
1640
1754
|
if (await exists(mockupManifest)) change.mockupManifestPath = mockupManifest;
|
|
1641
1755
|
return change;
|
|
@@ -1852,6 +1966,44 @@ satlas verify <slug> --file fix --scenario REQ-DOMINIO-001-S1 --command "<comand
|
|
|
1852
1966
|
o, si es manual:
|
|
1853
1967
|
satlas verify <slug> --file fix --scenario REQ-DOMINIO-001-S1 --method manual --result pass --by "<nombre>" --notes "<c\xF3mo se comprob\xF3>"
|
|
1854
1968
|
-->
|
|
1969
|
+
`,
|
|
1970
|
+
docTecnica: `# Documentaci\xF3n t\xE9cnica \u2014 {{TITLE}}
|
|
1971
|
+
|
|
1972
|
+
## Resumen del cambio
|
|
1973
|
+
|
|
1974
|
+
- **Cambio**: \`{{SLUG}}\` \xB7 dominio \`{{DOMAIN}}\` \xB7 carril \`{{LANE}}\`
|
|
1975
|
+
- **Actualizado**: {{DATE}}
|
|
1976
|
+
- **Tareas**: {{TASKS}}
|
|
1977
|
+
|
|
1978
|
+
## Requisitos y escenarios
|
|
1979
|
+
|
|
1980
|
+
{{REQUIREMENTS}}
|
|
1981
|
+
|
|
1982
|
+
## Evidencia registrada
|
|
1983
|
+
|
|
1984
|
+
{{EVIDENCE}}
|
|
1985
|
+
|
|
1986
|
+
## Pendiente de evidencia
|
|
1987
|
+
|
|
1988
|
+
{{PENDING}}
|
|
1989
|
+
`,
|
|
1990
|
+
docManual: `# Manual \u2014 {{TITLE}}
|
|
1991
|
+
|
|
1992
|
+
## Qu\xE9 hace este cambio
|
|
1993
|
+
|
|
1994
|
+
{{TITLE}} \u2014 dominio \`{{DOMAIN}}\` (cambio \`{{SLUG}}\`, carril \`{{LANE}}\`).
|
|
1995
|
+
|
|
1996
|
+
## C\xF3mo se usa
|
|
1997
|
+
|
|
1998
|
+
{{SCENARIOS}}
|
|
1999
|
+
|
|
2000
|
+
## C\xF3mo se comprob\xF3
|
|
2001
|
+
|
|
2002
|
+
{{EVIDENCE}}
|
|
2003
|
+
|
|
2004
|
+
## Pendiente de comprobar
|
|
2005
|
+
|
|
2006
|
+
{{PENDING}}
|
|
1855
2007
|
`
|
|
1856
2008
|
};
|
|
1857
2009
|
var EN = {
|
|
@@ -1977,6 +2129,44 @@ Cubre: REQ-DOMAIN-001
|
|
|
1977
2129
|
<!-- Register real evidence with:
|
|
1978
2130
|
satlas verify <slug> --file fix --scenario REQ-DOMAIN-001-S1 --command "<command>" --by "<name>"
|
|
1979
2131
|
-->
|
|
2132
|
+
`,
|
|
2133
|
+
docTecnica: `# Technical documentation \u2014 {{TITLE}}
|
|
2134
|
+
|
|
2135
|
+
## Change summary
|
|
2136
|
+
|
|
2137
|
+
- **Change**: \`{{SLUG}}\` \xB7 domain \`{{DOMAIN}}\` \xB7 lane \`{{LANE}}\`
|
|
2138
|
+
- **Updated**: {{DATE}}
|
|
2139
|
+
- **Tasks**: {{TASKS}}
|
|
2140
|
+
|
|
2141
|
+
## Requirements and scenarios
|
|
2142
|
+
|
|
2143
|
+
{{REQUIREMENTS}}
|
|
2144
|
+
|
|
2145
|
+
## Recorded evidence
|
|
2146
|
+
|
|
2147
|
+
{{EVIDENCE}}
|
|
2148
|
+
|
|
2149
|
+
## Pending evidence
|
|
2150
|
+
|
|
2151
|
+
{{PENDING}}
|
|
2152
|
+
`,
|
|
2153
|
+
docManual: `# Manual \u2014 {{TITLE}}
|
|
2154
|
+
|
|
2155
|
+
## What this change does
|
|
2156
|
+
|
|
2157
|
+
{{TITLE}} \u2014 domain \`{{DOMAIN}}\` (change \`{{SLUG}}\`, lane \`{{LANE}}\`).
|
|
2158
|
+
|
|
2159
|
+
## How to use it
|
|
2160
|
+
|
|
2161
|
+
{{SCENARIOS}}
|
|
2162
|
+
|
|
2163
|
+
## How it was verified
|
|
2164
|
+
|
|
2165
|
+
{{EVIDENCE}}
|
|
2166
|
+
|
|
2167
|
+
## Pending verification
|
|
2168
|
+
|
|
2169
|
+
{{PENDING}}
|
|
1980
2170
|
`
|
|
1981
2171
|
};
|
|
1982
2172
|
function templatesFor(language) {
|
|
@@ -4568,8 +4758,89 @@ async function upgradeAdvisory(root) {
|
|
|
4568
4758
|
return { plan, diagnostics };
|
|
4569
4759
|
}
|
|
4570
4760
|
|
|
4571
|
-
// src/
|
|
4761
|
+
// src/docs.ts
|
|
4572
4762
|
import path19 from "path";
|
|
4763
|
+
var DOCS_MARKER_START = "<!-- specatlas:generado:inicio -->";
|
|
4764
|
+
var DOCS_MARKER_END = "<!-- specatlas:generado:fin -->";
|
|
4765
|
+
function docData(change, language, now) {
|
|
4766
|
+
const es = language !== "en";
|
|
4767
|
+
const requirements = [...change.delta?.added ?? [], ...change.delta?.modified ?? []];
|
|
4768
|
+
const evidence = change.verify?.evidence ?? [];
|
|
4769
|
+
const passed = new Set(evidence.filter((entry) => entry.result === "pass").map((entry) => entry.scenario));
|
|
4770
|
+
const requirementsText = requirements.length === 0 ? es ? "_Sin requisitos en el delta._" : "_No requirements in the delta._" : requirements.map((requirement) => {
|
|
4771
|
+
const lines = [`### ${requirement.id} \u2014 ${requirement.title}`, ""];
|
|
4772
|
+
for (const scenario of requirement.scenarios) lines.push(`- \`${scenario.id}\` \u2014 ${scenario.title}`);
|
|
4773
|
+
return lines.join("\n");
|
|
4774
|
+
}).join("\n\n");
|
|
4775
|
+
const scenarioList = requirements.flatMap((requirement) => requirement.scenarios);
|
|
4776
|
+
const scenariosText = scenarioList.length === 0 ? es ? "_Sin escenarios._" : "_No scenarios._" : scenarioList.map((scenario) => `- \`${scenario.id}\` \u2014 ${scenario.title}`).join("\n");
|
|
4777
|
+
const evidenceText = evidence.length === 0 ? es ? "_Sin evidencia registrada._" : "_No evidence recorded._" : evidence.map((entry) => `- \`${entry.scenario}\` \u2014 ${entry.method} \xB7 ${entry.result}${entry.date ? ` \xB7 ${entry.date}` : ""}`).join("\n");
|
|
4778
|
+
const pending = scenarioList.filter((scenario) => !passed.has(scenario.id));
|
|
4779
|
+
const pendingText = pending.length === 0 ? es ? "_Nada pendiente: todos los escenarios tienen evidencia en pass._" : "_Nothing pending: every scenario has passing evidence._" : pending.map((scenario) => `- \`${scenario.id}\` \u2014 ${scenario.title}`).join("\n");
|
|
4780
|
+
return {
|
|
4781
|
+
TITLE: change.meta?.title ?? change.slug,
|
|
4782
|
+
SLUG: change.slug,
|
|
4783
|
+
DOMAIN: change.meta?.domain ?? "\u2014",
|
|
4784
|
+
LANE: change.meta?.lane ?? "standard",
|
|
4785
|
+
DATE: localDate(now),
|
|
4786
|
+
TASKS: change.tasks ? `${change.tasks.counts.done}/${change.tasks.counts.total}` : es ? "sin tareas" : "no tasks",
|
|
4787
|
+
REQUIREMENTS: requirementsText,
|
|
4788
|
+
SCENARIOS: scenariosText,
|
|
4789
|
+
EVIDENCE: evidenceText,
|
|
4790
|
+
PENDING: pendingText
|
|
4791
|
+
};
|
|
4792
|
+
}
|
|
4793
|
+
function mergeManaged(existing, block, language) {
|
|
4794
|
+
const managed = `${DOCS_MARKER_START}
|
|
4795
|
+
${block.trimEnd()}
|
|
4796
|
+
${DOCS_MARKER_END}`;
|
|
4797
|
+
if (existing === void 0) {
|
|
4798
|
+
const notes = language === "en" ? "## Notes\n\n(Write here whatever you want to keep across regenerations.)" : "## Notas\n\n(Escribe aqu\xED lo que quieras conservar entre regeneraciones.)";
|
|
4799
|
+
return `${managed}
|
|
4800
|
+
|
|
4801
|
+
${notes}
|
|
4802
|
+
`;
|
|
4803
|
+
}
|
|
4804
|
+
const start = existing.indexOf(DOCS_MARKER_START);
|
|
4805
|
+
const end = existing.indexOf(DOCS_MARKER_END);
|
|
4806
|
+
if (start >= 0 && end > start) {
|
|
4807
|
+
const before = existing.slice(0, start);
|
|
4808
|
+
const after = existing.slice(end + DOCS_MARKER_END.length);
|
|
4809
|
+
return `${before}${managed}${after}`;
|
|
4810
|
+
}
|
|
4811
|
+
return `${managed}
|
|
4812
|
+
|
|
4813
|
+
${existing}`;
|
|
4814
|
+
}
|
|
4815
|
+
async function generateDocs(opts) {
|
|
4816
|
+
const root = path19.resolve(opts.root);
|
|
4817
|
+
const { config } = await loadWorkspace(root);
|
|
4818
|
+
const change = await loadChange(root, opts.slug);
|
|
4819
|
+
if (!change.meta) {
|
|
4820
|
+
return {
|
|
4821
|
+
slug: opts.slug,
|
|
4822
|
+
files: [],
|
|
4823
|
+
diagnostics: [diag("ATLAS-DOCS-002", "error", `No existe el cambio "${opts.slug}"`, { suggestion: "Comprueba el nombre del cambio" })]
|
|
4824
|
+
};
|
|
4825
|
+
}
|
|
4826
|
+
const language = config.project.language;
|
|
4827
|
+
const tipo = opts.tipo ?? "all";
|
|
4828
|
+
const tipos = tipo === "all" ? ["tecnica", "manual"] : [tipo];
|
|
4829
|
+
const templates = templatesFor(language);
|
|
4830
|
+
const data = docData(change, language, opts.now ?? /* @__PURE__ */ new Date());
|
|
4831
|
+
const files = [];
|
|
4832
|
+
for (const current of tipos) {
|
|
4833
|
+
const file = path19.join(change.dir, "docs", `${current}.md`);
|
|
4834
|
+
const block = renderTemplate(current === "tecnica" ? templates.docTecnica : templates.docManual, data);
|
|
4835
|
+
const existing = await readTextIfExists(file);
|
|
4836
|
+
await writeText(file, mergeManaged(existing, block, language));
|
|
4837
|
+
files.push({ tipo: current, path: file, created: existing === void 0 });
|
|
4838
|
+
}
|
|
4839
|
+
return { slug: opts.slug, files, diagnostics: [] };
|
|
4840
|
+
}
|
|
4841
|
+
|
|
4842
|
+
// src/sarif.ts
|
|
4843
|
+
import path20 from "path";
|
|
4573
4844
|
var SARIF_SCHEMA = "https://json.schemastore.org/sarif-2.1.0.json";
|
|
4574
4845
|
var SARIF_VERSION = "2.1.0";
|
|
4575
4846
|
var TOOL_NAME = "SpecAtlas";
|
|
@@ -4607,7 +4878,7 @@ function resultOf(diagnostic, root) {
|
|
|
4607
4878
|
message: { text: diagnostic.message }
|
|
4608
4879
|
};
|
|
4609
4880
|
if (diagnostic.path) {
|
|
4610
|
-
const rel =
|
|
4881
|
+
const rel = path20.relative(root, diagnostic.path);
|
|
4611
4882
|
if (rel !== "") {
|
|
4612
4883
|
const physicalLocation = {
|
|
4613
4884
|
artifactLocation: { uri: toPosix(rel) },
|
|
@@ -4657,7 +4928,7 @@ function toSarifText(opts) {
|
|
|
4657
4928
|
}
|
|
4658
4929
|
|
|
4659
4930
|
// src/doctor.ts
|
|
4660
|
-
import
|
|
4931
|
+
import path21 from "path";
|
|
4661
4932
|
async function runDoctor(root) {
|
|
4662
4933
|
const findings = [];
|
|
4663
4934
|
const { workspace, config } = await loadWorkspace(root);
|
|
@@ -4665,7 +4936,7 @@ async function runDoctor(root) {
|
|
|
4665
4936
|
const approvals = await loadApprovals(workspace.sddDir);
|
|
4666
4937
|
findings.push(...approvals.diagnostics);
|
|
4667
4938
|
for (const change of workspace.changes) {
|
|
4668
|
-
const deltaPath =
|
|
4939
|
+
const deltaPath = path21.join(change.dir, "spec.md");
|
|
4669
4940
|
const deltaContent = await readTextIfExists(deltaPath);
|
|
4670
4941
|
const approval = verifyApproval(change, approvals.byArtifact, config, deltaContent ?? void 0);
|
|
4671
4942
|
if ((change.planPath || change.tasks) && (approval.status === "missing" || approval.status === "stale")) {
|
|
@@ -4687,7 +4958,7 @@ async function runDoctor(root) {
|
|
|
4687
4958
|
}
|
|
4688
4959
|
for (const override of change.meta?.overrides ?? []) {
|
|
4689
4960
|
if (!override.reason.trim() || !override.by.trim()) {
|
|
4690
|
-
findings.push(diag("ATLAS-LIFECYCLE-004", "error", `Override del gate "${override.gate}" sin motivo o autor`, { path:
|
|
4961
|
+
findings.push(diag("ATLAS-LIFECYCLE-004", "error", `Override del gate "${override.gate}" sin motivo o autor`, { path: path21.join(change.dir, "meta.yaml") }));
|
|
4691
4962
|
}
|
|
4692
4963
|
}
|
|
4693
4964
|
}
|
|
@@ -4710,7 +4981,7 @@ async function specHashOf(filePath) {
|
|
|
4710
4981
|
}
|
|
4711
4982
|
|
|
4712
4983
|
// src/gate.ts
|
|
4713
|
-
import
|
|
4984
|
+
import path22 from "path";
|
|
4714
4985
|
var UI_DOMAINS2 = /* @__PURE__ */ new Set(["frontend", "mobile", "fullstack"]);
|
|
4715
4986
|
function count(name, diagnostics) {
|
|
4716
4987
|
return {
|
|
@@ -4727,7 +4998,7 @@ function livingRequirementsMap(specs) {
|
|
|
4727
4998
|
return map;
|
|
4728
4999
|
}
|
|
4729
5000
|
async function runCiGate(opts) {
|
|
4730
|
-
const root =
|
|
5001
|
+
const root = path22.resolve(opts.root);
|
|
4731
5002
|
const { workspace, config } = await loadWorkspace(root);
|
|
4732
5003
|
const diagnostics = [];
|
|
4733
5004
|
const checks = [];
|
|
@@ -4738,7 +5009,7 @@ async function runCiGate(opts) {
|
|
|
4738
5009
|
let changesErrors = 0;
|
|
4739
5010
|
let changesWarnings = 0;
|
|
4740
5011
|
for (const change of workspace.changes) {
|
|
4741
|
-
const lintFindings = change.delta ? lintDelta(change.delta, living,
|
|
5012
|
+
const lintFindings = change.delta ? lintDelta(change.delta, living, path22.join(change.dir, "spec.md"), { language: config.spec.language }) : [];
|
|
4742
5013
|
const trace = checkTrace({
|
|
4743
5014
|
specs: workspace.specs,
|
|
4744
5015
|
change,
|
|
@@ -4788,6 +5059,8 @@ export {
|
|
|
4788
5059
|
BUILTIN_PACKS,
|
|
4789
5060
|
CONFIG_FILE,
|
|
4790
5061
|
CORE_VERSION,
|
|
5062
|
+
DOCS_MARKER_END,
|
|
5063
|
+
DOCS_MARKER_START,
|
|
4791
5064
|
LIVING_FIXES_DIR,
|
|
4792
5065
|
REQ_HEAD_RE,
|
|
4793
5066
|
REQ_ID_RE,
|
|
@@ -4822,6 +5095,7 @@ export {
|
|
|
4822
5095
|
changeMetaYaml,
|
|
4823
5096
|
checkMockups,
|
|
4824
5097
|
checkTrace,
|
|
5098
|
+
clarifyAdvisory,
|
|
4825
5099
|
collectMetrics,
|
|
4826
5100
|
collectVersionedArtifacts,
|
|
4827
5101
|
commentIssue,
|
|
@@ -4840,6 +5114,8 @@ export {
|
|
|
4840
5114
|
detectRepo,
|
|
4841
5115
|
detectionToYaml,
|
|
4842
5116
|
diag,
|
|
5117
|
+
docsAdvisory,
|
|
5118
|
+
docsReady,
|
|
4843
5119
|
editIssue,
|
|
4844
5120
|
emitFrontmatter,
|
|
4845
5121
|
ensureDir,
|
|
@@ -4852,6 +5128,7 @@ export {
|
|
|
4852
5128
|
findWorkspaceRoot,
|
|
4853
5129
|
firstToken,
|
|
4854
5130
|
foldDelta,
|
|
5131
|
+
generateDocs,
|
|
4855
5132
|
generatePresentation,
|
|
4856
5133
|
generateRunId,
|
|
4857
5134
|
getNumber,
|
|
@@ -4907,6 +5184,7 @@ export {
|
|
|
4907
5184
|
packFindings,
|
|
4908
5185
|
parseApprovals,
|
|
4909
5186
|
parseChangeMeta,
|
|
5187
|
+
parseClarify,
|
|
4910
5188
|
parseConfig,
|
|
4911
5189
|
parseDelta,
|
|
4912
5190
|
parseFixCovers,
|