sot-validator 0.1.0 → 0.1.1

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.
Files changed (2) hide show
  1. package/index.js +75 -31
  2. package/package.json +1 -1
package/index.js CHANGED
@@ -12,7 +12,7 @@
12
12
  * @license CC-BY-4.0
13
13
  */
14
14
 
15
- const VERSION = '0.1.0';
15
+ const VERSION = '0.1.1';
16
16
 
17
17
  /**
18
18
  * Validate a Source of Truth (.sot) file
@@ -23,8 +23,11 @@ function validate(content) {
23
23
  const errors = [];
24
24
  const warnings = [];
25
25
 
26
- // Required: Header block
27
- if (!content.includes('-- Source of Truth')) {
26
+ // Strip BOM if present
27
+ const cleanContent = content.replace(/^\uFEFF/, '');
28
+
29
+ // Required: Header block (case-insensitive)
30
+ if (!/--\s*source\s+of\s+truth/i.test(cleanContent)) {
28
31
  errors.push({
29
32
  code: 'MISSING_HEADER',
30
33
  message: 'Document must contain "-- Source of Truth" header',
@@ -32,17 +35,32 @@ function validate(content) {
32
35
  });
33
36
  }
34
37
 
35
- // Required: Last Updated
36
- if (!content.match(/\*\*Last Updated:\*\*/)) {
38
+ // Required: Last Updated (case-insensitive)
39
+ if (!/\*\*last\s*updated:\*\*/i.test(cleanContent)) {
37
40
  errors.push({
38
41
  code: 'MISSING_DATE',
39
42
  message: 'Document must contain "**Last Updated:**" field',
40
43
  line: null
41
44
  });
45
+ } else {
46
+ // Validate date is not in future
47
+ const dateMatch = cleanContent.match(/\*\*last\s*updated:\*\*\s*(\d{4}-\d{2}-\d{2})/i);
48
+ if (dateMatch) {
49
+ const docDate = new Date(dateMatch[1]);
50
+ const today = new Date();
51
+ today.setHours(0, 0, 0, 0);
52
+ if (docDate > today) {
53
+ errors.push({
54
+ code: 'FUTURE_DATE',
55
+ message: `Last Updated date (${dateMatch[1]}) is in the future`,
56
+ line: null
57
+ });
58
+ }
59
+ }
42
60
  }
43
61
 
44
- // Required: Owner
45
- if (!content.match(/\*\*Owner:\*\*/)) {
62
+ // Required: Owner (case-insensitive)
63
+ if (!/\*\*owner:\*\*/i.test(cleanContent)) {
46
64
  errors.push({
47
65
  code: 'MISSING_OWNER',
48
66
  message: 'Document must contain "**Owner:**" field',
@@ -50,8 +68,8 @@ function validate(content) {
50
68
  });
51
69
  }
52
70
 
53
- // Required: Status
54
- if (!content.match(/\*\*Status:\*\*/)) {
71
+ // Required: Status (case-insensitive)
72
+ if (!/\*\*status:\*\*/i.test(cleanContent)) {
55
73
  errors.push({
56
74
  code: 'MISSING_STATUS',
57
75
  message: 'Document must contain "**Status:**" field',
@@ -59,17 +77,28 @@ function validate(content) {
59
77
  });
60
78
  }
61
79
 
62
- // Required: Verification Status table
63
- if (!content.includes('## Verification Status')) {
80
+ // Required: Verification Status section (case-insensitive)
81
+ const hasVerificationSection = /##\s*verification\s+status/i.test(cleanContent);
82
+ if (!hasVerificationSection) {
64
83
  errors.push({
65
84
  code: 'MISSING_VERIFICATION_TABLE',
66
85
  message: 'Document must contain "## Verification Status" section',
67
86
  line: null
68
87
  });
88
+ } else {
89
+ // BUG FIX #4: Check that section contains a table
90
+ const verificationSection = cleanContent.match(/##\s*verification\s+status[\s\S]*?(?=##|$)/i);
91
+ if (verificationSection && !verificationSection[0].includes('|')) {
92
+ warnings.push({
93
+ code: 'NO_TABLE_IN_VERIFICATION',
94
+ message: 'Verification Status section should contain a markdown table',
95
+ line: null
96
+ });
97
+ }
69
98
  }
70
99
 
71
- // Warning: Status says VERIFIED without qualification
72
- if (content.match(/Status:\*\*\s*VERIFIED\s*$/m)) {
100
+ // Warning: Status says VERIFIED without qualification (case-insensitive)
101
+ if (/status:\*\*\s*verified\s*$/im.test(cleanContent)) {
73
102
  warnings.push({
74
103
  code: 'UNQUALIFIED_VERIFIED',
75
104
  message: 'Status "VERIFIED" should be qualified (e.g., "with noted exceptions")',
@@ -77,23 +106,37 @@ function validate(content) {
77
106
  });
78
107
  }
79
108
 
80
- // Warning: Estimates in Verified Data section
81
- const verifiedDataSection = content.match(/## Verified Data[\s\S]*?(?=##|$)/);
82
- if (verifiedDataSection && verifiedDataSection[0].match(/~\d|estimated|approximately/i)) {
83
- warnings.push({
84
- code: 'ESTIMATES_IN_VERIFIED',
85
- message: 'Estimates found in Verified Data section - move to Estimates section',
86
- line: null
87
- });
88
- }
109
+ // Extract Verified Data section for scoped checks
110
+ const verifiedDataMatch = cleanContent.match(/##\s*verified\s+data[\s\S]*?(?=##|$)/i);
111
+ const verifiedDataSection = verifiedDataMatch ? verifiedDataMatch[0] : null;
89
112
 
90
- // Warning: Missing staleness markers
91
- if (content.includes('## Verified Data') && !content.match(/\[STABLE\]|\[VOLATILE\]|\[CHECK BEFORE CITING\]|\[SNAPSHOT\]/)) {
92
- warnings.push({
93
- code: 'MISSING_STALENESS',
94
- message: 'Verified Data should include staleness markers ([STABLE], [VOLATILE], etc.)',
95
- line: null
96
- });
113
+ if (verifiedDataSection) {
114
+ // BUG FIX #4: Check that Verified Data contains a table
115
+ if (!verifiedDataSection.includes('|')) {
116
+ warnings.push({
117
+ code: 'NO_TABLE_IN_VERIFIED_DATA',
118
+ message: 'Verified Data section should contain a markdown table',
119
+ line: null
120
+ });
121
+ }
122
+
123
+ // Warning: Estimates in Verified Data section
124
+ if (/~\d|estimated|approximately/i.test(verifiedDataSection)) {
125
+ warnings.push({
126
+ code: 'ESTIMATES_IN_VERIFIED',
127
+ message: 'Estimates found in Verified Data section - move to Estimates section',
128
+ line: null
129
+ });
130
+ }
131
+
132
+ // BUG FIX #1: Check staleness markers WITHIN Verified Data section only
133
+ if (!/\[STABLE\]|\[VOLATILE\]|\[CHECK BEFORE CITING\]|\[SNAPSHOT\]/i.test(verifiedDataSection)) {
134
+ warnings.push({
135
+ code: 'MISSING_STALENESS',
136
+ message: 'Verified Data should include staleness markers ([STABLE], [VOLATILE], etc.)',
137
+ line: null
138
+ });
139
+ }
97
140
  }
98
141
 
99
142
  return {
@@ -119,8 +162,9 @@ function isValid(content) {
119
162
  * @returns {boolean} True if appears to be .sot format
120
163
  */
121
164
  function detect(content) {
122
- return content.includes('-- Source of Truth') &&
123
- content.includes('## Verification Status');
165
+ const cleanContent = content.replace(/^\uFEFF/, '');
166
+ return /--\s*source\s+of\s+truth/i.test(cleanContent) &&
167
+ /##\s*verification\s+status/i.test(cleanContent);
124
168
  }
125
169
 
126
170
  module.exports = {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sot-validator",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Validator for Source of Truth (.sot) files - epistemic quality verification for AI-safe documentation",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",