xdrs-core 0.35.0 → 0.35.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.
- package/lib/lint.js +6 -17
- package/lib/lint.test.js +28 -0
- package/package.json +1 -1
package/lib/lint.js
CHANGED
|
@@ -99,23 +99,12 @@ function lintWorkspace(targetPath, options = {}) {
|
|
|
99
99
|
const rootEntries = safeReadDir(xdrsRoot, errors, 'read XDRS root directory');
|
|
100
100
|
const scopeEntries = rootEntries.filter((entry) => entry.isDirectory() && !entry.name.startsWith('.'));
|
|
101
101
|
|
|
102
|
-
// Pass 1: collect
|
|
103
|
-
const coreScopes = [];
|
|
104
|
-
for (const scopeEntry of scopeEntries) {
|
|
105
|
-
const scopeIndexPath = path.join(xdrsRoot, scopeEntry.name, 'index.md');
|
|
106
|
-
if (!existsFile(scopeIndexPath)) continue;
|
|
107
|
-
const content = fs.readFileSync(scopeIndexPath, 'utf8');
|
|
108
|
-
const scopeTypeMatch = content.match(/^scope-type:\s*(.+)$/m);
|
|
109
|
-
if (scopeTypeMatch && scopeTypeMatch[1].trim() === 'core') {
|
|
110
|
-
coreScopes.push(path.join(xdrsRoot, scopeEntry.name));
|
|
111
|
-
}
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
// Pass 2: collect known scope-type names from NNN-{scope-type}-scope-type.md files in core scopes
|
|
102
|
+
// Pass 1: collect known scope-type names from NNN-{scope-type}-scope-type.md files in any scope
|
|
115
103
|
const knownScopeTypes = new Map(); // scopeTypeName -> filePath
|
|
116
|
-
for (const
|
|
104
|
+
for (const scopeEntry of scopeEntries) {
|
|
105
|
+
const scopePath = path.join(xdrsRoot, scopeEntry.name);
|
|
117
106
|
for (const typeName of Object.keys(TYPE_TO_ID)) {
|
|
118
|
-
const principlesDir = path.join(
|
|
107
|
+
const principlesDir = path.join(scopePath, typeName, 'principles');
|
|
119
108
|
if (!existsDirectory(principlesDir)) continue;
|
|
120
109
|
let principlesEntries;
|
|
121
110
|
try { principlesEntries = fs.readdirSync(principlesDir, { withFileTypes: true }); } catch { continue; }
|
|
@@ -261,7 +250,7 @@ function lintScopeIndexFrontmatter(scopeIndexPath, scopeName, errors, xdrsRoot,
|
|
|
261
250
|
return;
|
|
262
251
|
}
|
|
263
252
|
if (!knownScopeTypes.has(scopeType) && scopeType !== '_local') {
|
|
264
|
-
errors.push(`Scope index scope-type "${scopeType}" has no corresponding ${scopeType}-scope-type policy in the principles
|
|
253
|
+
errors.push(`Scope index scope-type "${scopeType}" has no corresponding ${scopeType}-scope-type policy in the principles of any scope: ${toDisplayPath(scopeIndexPath)}`);
|
|
265
254
|
return;
|
|
266
255
|
}
|
|
267
256
|
// Validate inheritance chain: ensure all ancestors exist in knownScopeTypes
|
|
@@ -278,7 +267,7 @@ function lintScopeIndexFrontmatter(scopeIndexPath, scopeName, errors, xdrsRoot,
|
|
|
278
267
|
break;
|
|
279
268
|
}
|
|
280
269
|
if (!knownScopeTypes.has(parentType)) {
|
|
281
|
-
errors.push(`Scope type "${currentType}" declares parent "${parentType}" but no ${parentType}-scope-type policy exists in any
|
|
270
|
+
errors.push(`Scope type "${currentType}" declares parent "${parentType}" but no ${parentType}-scope-type policy exists in any scope: ${toDisplayPath(scopeIndexPath)}`);
|
|
282
271
|
break;
|
|
283
272
|
}
|
|
284
273
|
visited.add(parentType);
|
package/lib/lint.test.js
CHANGED
|
@@ -885,6 +885,34 @@ test('accepts custom scope-type when policy exists in core-type scope', () => {
|
|
|
885
885
|
expect(result.errors.join('\n')).not.toContain('Scope index frontmatter');
|
|
886
886
|
});
|
|
887
887
|
|
|
888
|
+
test('accepts custom scope-type when policy exists in a non-core scope', () => {
|
|
889
|
+
// domain-scope-type.md is in a platform-type scope (not a core scope), but lint should still find it
|
|
890
|
+
const workspaceRoot = createWorkspace('custom-scope-type-in-non-core-scope', {
|
|
891
|
+
'.xdrs/index.md': rootIndex(['[nnb-mt-domain](nnb-mt-domain/index.md)', '[my-platform](my-platform/index.md)']),
|
|
892
|
+
'.xdrs/nnb-mt-domain/index.md': '---\nscope-type: domain\nname: nnb-mt-domain\ndescription: Domain scope.\napply-to: All\nvalid-from: 2026-01-01\n---\n\n# nnb-mt-domain Scope Overview\n\nDomain scope.\n\n[ADRs](adrs/index.md)\n',
|
|
893
|
+
'.xdrs/nnb-mt-domain/adrs/index.md': teamAdrIndex([]),
|
|
894
|
+
// scope-type 'domain' defined in a non-core platform scope (not _core)
|
|
895
|
+
'.xdrs/my-platform/index.md': '---\nscope-type: platform\nname: my-platform\ndescription: Platform scope.\napply-to: All\nvalid-from: 2026-01-01\n---\n\n# my-platform Scope Overview\n\nPlatform scope.\n\n[ADRs](adrs/index.md)\n',
|
|
896
|
+
'.xdrs/my-platform/adrs/index.md': [
|
|
897
|
+
'# my-platform ADR Index', '', 'Platform ADRs.', '', '## principles', '',
|
|
898
|
+
'- [001-domain-scope-type](principles/001-domain-scope-type.md) - domain type', ''
|
|
899
|
+
].join('\n'),
|
|
900
|
+
'.xdrs/my-platform/adrs/principles/001-domain-scope-type.md': [
|
|
901
|
+
'---', 'name: my-platform-adr-policy-001-domain-scope-type',
|
|
902
|
+
'description: Defines the domain scope type.', 'apply-to: All', 'valid-from: 2026-01-01', '---', '',
|
|
903
|
+
'# my-platform-adr-policy-001: domain scope type', '', '## Context and Problem Statement', '',
|
|
904
|
+
'Defines the domain scope type.', '', '## Decision Outcome', '', 'Use scope-type: domain.', ''
|
|
905
|
+
].join('\n'),
|
|
906
|
+
// _core only defines 'core' and 'platform' — no 'domain' here
|
|
907
|
+
...coreWithScopeTypes(['platform']),
|
|
908
|
+
});
|
|
909
|
+
|
|
910
|
+
const result = lintWorkspace(workspaceRoot, { ignoreExternal: false });
|
|
911
|
+
|
|
912
|
+
expect(result.errors.join('\n')).not.toContain('has no corresponding');
|
|
913
|
+
expect(result.errors.join('\n')).not.toContain('Scope index frontmatter');
|
|
914
|
+
});
|
|
915
|
+
|
|
888
916
|
test('reports custom scope-type when no policy exists', () => {
|
|
889
917
|
const workspaceRoot = createWorkspace('custom-scope-type-missing-policy', {
|
|
890
918
|
'.xdrs/index.md': rootIndex(['[my-scope](my-scope/index.md)']),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xdrs-core",
|
|
3
|
-
"version": "0.35.
|
|
3
|
+
"version": "0.35.1",
|
|
4
4
|
"description": "A framework to structure, compile and distribute Architectural (ADR), Business (BDR), and Engineering (EDR) decision records contents so that AI agents and humans can reliably find and use them with hierarchical scopes and controlled rollout in the format of distributable versioned packages.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|