@ipation/specbridge 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -7,6 +7,171 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [1.0.4] - 2026-01-30
11
+
12
+ ### Fixed
13
+
14
+ #### Critical Fixes
15
+ - **Verification Timeout Bug** (src/verification/engine.ts)
16
+ - Fixed event loop hanging issue where verification would wait for full 60-second timeout even after completing
17
+ - Added proper timeout handle cleanup with try-finally block
18
+ - Used `unref()` to prevent blocking process exit
19
+ - **Impact**: Integration tests now complete in ~97 seconds instead of hanging for 10+ minutes
20
+ - CLI verify command exits immediately (~600ms) instead of waiting 60 seconds
21
+
22
+ #### Inference Issues
23
+ - **Non-Existent Verifier References** (src/inference/analyzers/errors.ts)
24
+ - Fixed `error-hierarchy` verifier reference → `errors` (line 90)
25
+ - Fixed `custom-errors-only` verifier reference → `errors` (line 205)
26
+ - Updated corresponding unit tests to match
27
+ - **Impact**: Inferred constraints now reference valid verifiers that exist in the registry
28
+
29
+ #### CLI Command Issues
30
+ - **`infer --output` Bug** (src/cli/commands/infer.ts)
31
+ - Fixed early return preventing file save when no patterns detected
32
+ - Moved file saving logic before pattern check
33
+ - **Impact**: Output file is always created when `--output` flag is used, even with empty results
34
+
35
+ #### Visual Formatting
36
+ - **Empty Placeholder Characters** (src/reporting/formats/markdown.ts, src/agent/context.generator.ts)
37
+ - Added compliance emojis: ✅ (≥90%), ⚠️ (70-89%), ❌ (<70%)
38
+ - Added progress bar characters: █ (filled), ░ (empty)
39
+ - Added constraint type emojis: 🔒 (invariant), 📋 (convention), 💡 (guideline)
40
+ - **Impact**: Reports and agent context now display with proper visual indicators
41
+
42
+ ### Changed
43
+
44
+ #### Documentation
45
+ - **Version Consistency**
46
+ - Archived outdated `IMPLEMENTATION_STATUS.md` → `IMPLEMENTATION_STATUS_v0.2.2_ARCHIVED.md`
47
+ - Added archive notice indicating current version is 1.0.4
48
+ - **Impact**: No misleading version information
49
+
50
+ - **GitHub URL Standardization**
51
+ - Standardized all repository references to `nouatzi/specbridge`
52
+ - Fixed docs/README.md, docs/troubleshooting.md, src/reporting/formats/markdown.ts
53
+ - Corrected wrong `anthropics/claude-code` reference in archived file
54
+ - **Impact**: Consistent repository URLs throughout documentation and code
55
+
56
+ ### Improved
57
+
58
+ #### Test Suite
59
+ - **Integration Tests** (tests/integration/cli.test.ts)
60
+ - Fixed `decision create` test syntax (positional argument instead of `--id` flag)
61
+ - Added valid constraints to manually created decision files (schema compliance)
62
+ - Fixed 3 previously failing CLI integration tests
63
+ - **Result**: All 30 integration tests now pass (100% success rate)
64
+ - **Duration**: Tests complete in ~97 seconds (down from 10+ minutes)
65
+
66
+ ### Test Results Summary
67
+ - ✅ Type Checking: Passed
68
+ - ✅ Unit Tests: 762/762 passed (24 test files)
69
+ - ✅ Integration Tests: 30/30 passed (2 test files)
70
+ - ✅ Total Duration: ~97 seconds
71
+
72
+ ### Files Modified
73
+ - 9 source/test files updated
74
+ - 2 documentation files updated
75
+ - 1 file archived
76
+ - All changes backward compatible (no breaking changes)
77
+
78
+ ## [1.0.3] - 2026-01-30
79
+
80
+ ### Improved
81
+
82
+ #### Test Coverage - Major Achievement 🎉
83
+ - **Overall coverage**: 67.37% → **94.07%** (+26.7% improvement, +39.6% total increase from v1.0.1)
84
+ - **Functions coverage**: 78.21% → **96.99%** (+18.78%, now at 97%)
85
+ - **Branches coverage**: 81.95% → **93.93%** (+11.98%, exceeds 87% target)
86
+ - **Statements coverage**: 67.37% → **94.07%** (+26.7%, exceeds 83% target)
87
+ - **Total tests**: 312 → **762** (+450 tests, +144% growth)
88
+
89
+ **All coverage targets exceeded by 7-14 percentage points!**
90
+
91
+ #### Sprint 1: Foundation Components (New Tests)
92
+ - **Scanner Tests** - Added `scanner.test.ts`: 580 lines, 48 tests
93
+ - Coverage: 0% → **99.38%** (was completely untested)
94
+ - Tests all 8 public methods with comprehensive edge cases
95
+ - Handles malformed files, nested structures, large file sets
96
+
97
+ - **Propagation Graph Tests** - Added `graph.test.ts`: 480 lines, 34 tests
98
+ - Coverage: 0% → **100%** (was completely untested)
99
+ - Tests dependency graph building, transitive dependencies
100
+ - Edge cases: cycles, empty graphs, overlapping scopes
101
+
102
+ - **Core Error Classes Tests** - Added `errors.test.ts`: 400 lines
103
+ - All 11 error classes comprehensively tested
104
+ - formatError() function validated with edge cases
105
+ - Error inheritance chain and special character handling
106
+
107
+ - **Verification Engine** - Extended `engine.test.ts`: +280 lines
108
+ - Added timeout handling, severity filtering, exception lifecycle
109
+ - Edge cases: empty sourceRoots, concurrent calls, malformed files
110
+ - Coverage improved with comprehensive scenario testing
111
+
112
+ - **Registry** - Extended `registry.test.ts`: +290 lines
113
+ - Filter combinations (status+tags, constraintType+severity)
114
+ - Additional methods: has(), getIds(), getByOwner(), getByTag()
115
+ - Coverage: **100%** (maintained and enhanced)
116
+
117
+ - **Reporter** - Extended `reporter.test.ts`: +320 lines
118
+ - formatAsTableGrouped(), formatAsMarkdown() comprehensive tests
119
+ - checkDegradation() function fully tested
120
+ - generateComplianceReport() edge cases covered
121
+
122
+ #### Sprint 2: Partial Coverage Improvements
123
+ - **Agent Context Generator** - Extended `context.generator.test.ts`: +400 lines, 42 total tests
124
+ - Coverage: 43.52% → **98.96%** (+55.44%, highest improvement)
125
+ - Standalone functions fully tested: generateContext, formatContextAsMarkdown, formatContextAsJson, formatContextAsMcp
126
+ - Format conversion validation (markdown/JSON/MCP)
127
+ - Rationale inclusion/exclusion scenarios
128
+
129
+ - **Propagation Engine** - Extended `engine.test.ts`: +200 lines, 29 total tests
130
+ - Coverage: 62.5% → **76.92%** (+14.42%)
131
+ - Migration step generation and prioritization tested
132
+ - Effort estimation with various scenarios
133
+ - Auto-fix detection and affected files handling
134
+
135
+ - **Registry Loader** - Extended `loader.test.ts`: +200 lines, 11 new tests
136
+ - Coverage: 67.21% → **100%** (+32.79%)
137
+ - validateDecisionFile() function completely tested
138
+ - Schema validation, corrupted file handling
139
+ - All error scenarios validated
140
+
141
+ #### Components at Perfect Coverage (100%)
142
+ - ✅ Registry (100%)
143
+ - ✅ Registry Loader (100%)
144
+ - ✅ Propagation Graph (100%)
145
+ - ✅ Analyzers (100%)
146
+ - ✅ Config Schemas (100%)
147
+ - ✅ Utils (100%)
148
+
149
+ #### Components at Excellent Coverage (95-99%)
150
+ - ✅ Agent Context Generator (98.96%)
151
+ - ✅ Scanner (99.38%)
152
+ - ✅ Verifiers (99.72%)
153
+ - ✅ Functions (96.99%)
154
+
155
+ #### Quality Metrics
156
+ - ✅ **762 tests passing** (100% pass rate, +450 tests)
157
+ - ✅ **94.07% test coverage** (exceeds all thresholds)
158
+ - ✅ **No type errors**
159
+ - ✅ **Build succeeds**
160
+ - ✅ **Test-to-source ratio**: ~1.7:1
161
+ - ✅ **Production-ready quality**
162
+
163
+ ### Changed
164
+ - Updated coverage thresholds in `vitest.config.ts`:
165
+ - Lines: 67% → **83%**
166
+ - Functions: 78% → **83%**
167
+ - Branches: 78% → **87%**
168
+ - Statements: 67% → **83%**
169
+
170
+ ### Added
171
+ - Comprehensive test helpers and fixtures for easier test creation
172
+ - Integration test scenarios for multi-command workflows
173
+ - Edge case testing for all critical components
174
+
10
175
  ## [1.0.2] - 2026-01-29
11
176
 
12
177
  ### Improved
package/dist/cli.js CHANGED
@@ -1005,9 +1005,7 @@ var ErrorsAnalyzer = class {
1005
1005
  );
1006
1006
  if (errorClasses.length < 2) return null;
1007
1007
  const files = scanner.getFiles();
1008
- let extendsError = 0;
1009
- let extendsCustomBase = 0;
1010
- let customBaseName = null;
1008
+ const baseCount = /* @__PURE__ */ new Map();
1011
1009
  for (const errorClass of errorClasses) {
1012
1010
  const file = files.find((f) => f.path === errorClass.file);
1013
1011
  if (!file) continue;
@@ -1016,22 +1014,27 @@ var ErrorsAnalyzer = class {
1016
1014
  const extendClause = classDecl.getExtends();
1017
1015
  if (extendClause) {
1018
1016
  const baseName = extendClause.getText();
1019
- if (baseName === "Error") {
1020
- extendsError++;
1021
- } else if (baseName.endsWith("Error")) {
1022
- extendsCustomBase++;
1023
- customBaseName = customBaseName || baseName;
1017
+ if (baseName !== "Error" && baseName.endsWith("Error")) {
1018
+ baseCount.set(baseName, (baseCount.get(baseName) || 0) + 1);
1024
1019
  }
1025
1020
  }
1026
1021
  }
1027
- if (extendsCustomBase >= 2 && customBaseName) {
1028
- const confidence = calculateConfidence(extendsCustomBase, errorClasses.length);
1022
+ let customBaseName = null;
1023
+ let maxCount = 0;
1024
+ for (const [baseName, count] of baseCount.entries()) {
1025
+ if (count > maxCount) {
1026
+ maxCount = count;
1027
+ customBaseName = baseName;
1028
+ }
1029
+ }
1030
+ if (maxCount >= 3 && customBaseName) {
1031
+ const confidence = calculateConfidence(maxCount, errorClasses.length);
1029
1032
  return createPattern(this.id, {
1030
1033
  id: "errors-custom-base",
1031
1034
  name: "Custom Error Base Class",
1032
1035
  description: `Custom errors extend a common base class (${customBaseName})`,
1033
1036
  confidence,
1034
- occurrences: extendsCustomBase,
1037
+ occurrences: maxCount,
1035
1038
  examples: errorClasses.slice(0, 3).map((c) => ({
1036
1039
  file: c.file,
1037
1040
  line: c.line,
@@ -1042,7 +1045,7 @@ var ErrorsAnalyzer = class {
1042
1045
  rule: `Custom error classes should extend ${customBaseName}`,
1043
1046
  severity: "medium",
1044
1047
  scope: "src/**/*.ts",
1045
- verifier: "error-hierarchy"
1048
+ verifier: "errors"
1046
1049
  }
1047
1050
  });
1048
1051
  }
@@ -1113,10 +1116,11 @@ var ErrorsAnalyzer = class {
1113
1116
  } else if (text.startsWith("new ") && text.includes("Error")) {
1114
1117
  throwCustom++;
1115
1118
  if (examples.length < 3) {
1119
+ const snippet = text.length > 50 ? text.slice(0, 50) + "..." : text;
1116
1120
  examples.push({
1117
1121
  file: path,
1118
1122
  line: node.getStartLineNumber(),
1119
- snippet: `throw ${text.slice(0, 50)}${text.length > 50 ? "..." : ""}`
1123
+ snippet: `throw ${snippet}`
1120
1124
  });
1121
1125
  }
1122
1126
  }
@@ -1138,7 +1142,7 @@ var ErrorsAnalyzer = class {
1138
1142
  rule: "Throw custom error classes instead of generic Error",
1139
1143
  severity: "medium",
1140
1144
  scope: "src/**/*.ts",
1141
- verifier: "custom-errors-only"
1145
+ verifier: "errors"
1142
1146
  }
1143
1147
  });
1144
1148
  }
@@ -1277,6 +1281,12 @@ var inferCommand = new Command2("infer").description("Analyze codebase and detec
1277
1281
  cwd
1278
1282
  });
1279
1283
  spinner.succeed(`Scanned ${result.filesScanned} files in ${result.duration}ms`);
1284
+ if (options.save || options.output) {
1285
+ const outputPath = options.output || join3(getInferredDir(cwd), "patterns.json");
1286
+ await writeTextFile(outputPath, JSON.stringify(result, null, 2));
1287
+ console.log(chalk2.green(`
1288
+ Results saved to: ${outputPath}`));
1289
+ }
1280
1290
  if (result.patterns.length === 0) {
1281
1291
  console.log(chalk2.yellow("\nNo patterns detected above confidence threshold."));
1282
1292
  console.log(chalk2.dim(`Try lowering --min-confidence (current: ${minConfidence})`));
@@ -1287,12 +1297,6 @@ var inferCommand = new Command2("infer").description("Analyze codebase and detec
1287
1297
  } else {
1288
1298
  printPatterns(result.patterns);
1289
1299
  }
1290
- if (options.save || options.output) {
1291
- const outputPath = options.output || join3(getInferredDir(cwd), "patterns.json");
1292
- await writeTextFile(outputPath, JSON.stringify(result, null, 2));
1293
- console.log(chalk2.green(`
1294
- Results saved to: ${outputPath}`));
1295
- }
1296
1300
  if (!options.json) {
1297
1301
  console.log("");
1298
1302
  console.log(chalk2.cyan("Next steps:"));
@@ -2124,9 +2128,11 @@ var VerificationEngine = class {
2124
2128
  let passed = 0;
2125
2129
  let failed = 0;
2126
2130
  const skipped = 0;
2127
- const timeoutPromise = new Promise(
2128
- (resolve) => setTimeout(() => resolve("timeout"), timeout)
2129
- );
2131
+ let timeoutHandle = null;
2132
+ const timeoutPromise = new Promise((resolve) => {
2133
+ timeoutHandle = setTimeout(() => resolve("timeout"), timeout);
2134
+ timeoutHandle.unref();
2135
+ });
2130
2136
  const verificationPromise = this.verifyFiles(
2131
2137
  filesToVerify,
2132
2138
  decisions,
@@ -2142,17 +2148,25 @@ var VerificationEngine = class {
2142
2148
  }
2143
2149
  }
2144
2150
  );
2145
- const result = await Promise.race([verificationPromise, timeoutPromise]);
2146
- if (result === "timeout") {
2147
- return {
2148
- success: false,
2149
- violations: allViolations,
2150
- checked,
2151
- passed,
2152
- failed,
2153
- skipped: filesToVerify.length - checked,
2154
- duration: timeout
2155
- };
2151
+ let result;
2152
+ try {
2153
+ result = await Promise.race([verificationPromise, timeoutPromise]);
2154
+ if (result === "timeout") {
2155
+ return {
2156
+ success: false,
2157
+ violations: allViolations,
2158
+ checked,
2159
+ passed,
2160
+ failed,
2161
+ skipped: filesToVerify.length - checked,
2162
+ duration: timeout
2163
+ };
2164
+ }
2165
+ } finally {
2166
+ if (timeoutHandle) {
2167
+ clearTimeout(timeoutHandle);
2168
+ timeoutHandle = null;
2169
+ }
2156
2170
  }
2157
2171
  const hasBlockingViolations = allViolations.some((v) => {
2158
2172
  if (level === "commit") {
@@ -3097,7 +3111,7 @@ function formatMarkdownReport(report) {
3097
3111
  lines.push("| Decision | Status | Constraints | Violations | Compliance |");
3098
3112
  lines.push("|----------|--------|-------------|------------|------------|");
3099
3113
  for (const dec of report.byDecision) {
3100
- const complianceEmoji = dec.compliance >= 90 ? "" : dec.compliance >= 70 ? "" : "";
3114
+ const complianceEmoji = dec.compliance >= 90 ? "\u2705" : dec.compliance >= 70 ? "\u26A0\uFE0F" : "\u274C";
3101
3115
  lines.push(
3102
3116
  `| ${dec.title} | ${dec.status} | ${dec.constraints} | ${dec.violations} | ${complianceEmoji} ${dec.compliance}% |`
3103
3117
  );
@@ -3106,15 +3120,15 @@ function formatMarkdownReport(report) {
3106
3120
  }
3107
3121
  lines.push("---");
3108
3122
  lines.push("");
3109
- lines.push("*Generated by [SpecBridge](https://github.com/specbridge/specbridge)*");
3123
+ lines.push("*Generated by [SpecBridge](https://github.com/nouatzi/specbridge)*");
3110
3124
  return lines.join("\n");
3111
3125
  }
3112
3126
  function formatProgressBar(percentage) {
3113
3127
  const width = 20;
3114
3128
  const filled = Math.round(percentage / 100 * width);
3115
3129
  const empty = width - filled;
3116
- const filledChar = "";
3117
- const emptyChar = "";
3130
+ const filledChar = "\u2588";
3131
+ const emptyChar = "\u2591";
3118
3132
  return `\`${filledChar.repeat(filled)}${emptyChar.repeat(empty)}\` ${percentage}%`;
3119
3133
  }
3120
3134
 
@@ -3232,7 +3246,7 @@ function formatContextAsMarkdown(context) {
3232
3246
  lines.push("### Constraints");
3233
3247
  lines.push("");
3234
3248
  for (const constraint of decision.constraints) {
3235
- const typeEmoji = constraint.type === "invariant" ? "" : constraint.type === "convention" ? "" : "";
3249
+ const typeEmoji = constraint.type === "invariant" ? "\u{1F512}" : constraint.type === "convention" ? "\u{1F4CB}" : "\u{1F4A1}";
3236
3250
  const severityBadge = `[${constraint.severity.toUpperCase()}]`;
3237
3251
  lines.push(`- ${typeEmoji} **${severityBadge}** ${constraint.rule}`);
3238
3252
  }