xdrs-core 0.52.2 → 0.53.0
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/.xdrs/_core/adrs/principles/001-xdrs-standards.md +3 -3
- package/.xdrs/_core/adrs/principles/002-policy-standards.md +1 -1
- package/.xdrs/_core/adrs/principles/003-skill-standards.md +12 -1
- package/.xdrs/_core/adrs/principles/006-research-standards.md +6 -6
- package/.xdrs/_core/adrs/principles/007-initiative-standards.md +9 -9
- package/.xdrs/_core/adrs/principles/008-policy-structured-standards.md +1 -1
- package/.xdrs/_core/adrs/principles/010-scope-governance.md +5 -5
- package/.xdrs/_core/adrs/principles/011-core-scope-type.md +1 -1
- package/.xdrs/_core/adrs/principles/022-scope-activation.md +12 -9
- package/.xdrs/_core/adrs/principles/skills/.assets/lint-verification.md +0 -1
- package/.xdrs/_core/adrs/principles/skills/compile-scope/SKILL.md +10 -8
- package/.xdrs/_core/adrs/principles/skills/review/SKILL.md +10 -8
- package/.xdrs/_core/adrs/principles/skills/write-article/SKILL.md +12 -10
- package/.xdrs/_core/adrs/principles/skills/write-initiative/SKILL.md +16 -14
- package/.xdrs/_core/adrs/principles/skills/write-policy/SKILL.md +12 -11
- package/.xdrs/_core/adrs/principles/skills/write-presentation/SKILL.md +5 -3
- package/.xdrs/_core/adrs/principles/skills/write-research/SKILL.md +17 -15
- package/.xdrs/_core/adrs/principles/skills/write-skill/SKILL.md +20 -12
- package/.xdrs/_core/adrs/principles/skills/write-xdrs-doc/SKILL.md +4 -2
- package/README.md +155 -59
- package/lib/lint.js +37 -73
- package/lib/lint.test.js +89 -47
- package/package.json +1 -1
package/lib/lint.test.js
CHANGED
|
@@ -2245,22 +2245,35 @@ test('reports error when follows references a scope not in workspace', () => {
|
|
|
2245
2245
|
const result = lintWorkspace(workspaceRoot, { ignoreExternal: false });
|
|
2246
2246
|
|
|
2247
2247
|
expect(result.errors.join('\n')).toContain('follows references scope "nonexistent-core" which does not exist in the workspace');
|
|
2248
|
-
expect(result.readOnlyScopes.has('myteam')).toBe(false);
|
|
2249
2248
|
});
|
|
2250
2249
|
|
|
2251
|
-
test('
|
|
2252
|
-
const workspaceRoot = createWorkspace(
|
|
2250
|
+
test.each([[true], [false]])('does not report a missing follows target of an external scope (ignoreExternal=%s) [022.06]', (ignoreExternal) => {
|
|
2251
|
+
const workspaceRoot = createWorkspace(`follows-missing-scope-external-${ignoreExternal}`, {
|
|
2253
2252
|
'.xdrs/index.md': rootIndex(['[myteam](myteam/index.md)']),
|
|
2254
2253
|
'.xdrs/myteam/index.md': '---\nscope-type: standard\nname: myteam\ndescription: Team.\napply-to: Test team\nvalid-from: 2026-01-01\nfollows: nonexistent-core\n---\n\n# myteam Scope Overview\n\n[ADRs](adrs/index.md)\n',
|
|
2255
2254
|
'.xdrs/myteam/adrs/index.md': teamAdrIndex(['- [001-team](principles/001-team.md) - Team decision']),
|
|
2256
2255
|
'.xdrs/myteam/adrs/principles/001-team.md': teamXdrDocument('Team decision.'),
|
|
2257
|
-
'.filedist.lock': '.xdrs/myteam/adrs/principles/001-team.md|some-package|1.0.0\n',
|
|
2256
|
+
'.filedist.lock': '.xdrs/myteam/index.md|some-package|1.0.0\n.xdrs/myteam/adrs/index.md|some-package|1.0.0\n.xdrs/myteam/adrs/principles/001-team.md|some-package|1.0.0\n',
|
|
2257
|
+
});
|
|
2258
|
+
|
|
2259
|
+
const result = lintWorkspace(workspaceRoot, { ignoreExternal });
|
|
2260
|
+
|
|
2261
|
+
expect(result.errors.join('\n')).not.toContain('nonexistent-core');
|
|
2262
|
+
expect(result.errors.join('\n')).not.toContain('022.06');
|
|
2263
|
+
});
|
|
2264
|
+
|
|
2265
|
+
test('does not report a missing extends target of an external scope [022.06]', () => {
|
|
2266
|
+
const workspaceRoot = createWorkspace('extends-missing-scope-external', {
|
|
2267
|
+
'.xdrs/index.md': rootIndex(['[myteam](myteam/index.md)']),
|
|
2268
|
+
'.xdrs/myteam/index.md': '---\nscope-type: standard\nname: myteam\ndescription: Team.\napply-to: Test team\nvalid-from: 2026-01-01\nextends: ghost-lib\n---\n\n# myteam Scope Overview\n\n[ADRs](adrs/index.md)\n',
|
|
2269
|
+
'.xdrs/myteam/adrs/index.md': teamAdrIndex(['- [001-team](principles/001-team.md) - Team decision']),
|
|
2270
|
+
'.xdrs/myteam/adrs/principles/001-team.md': teamXdrDocument('Team decision.'),
|
|
2271
|
+
'.filedist.lock': '.xdrs/myteam/index.md|some-package|1.0.0\n',
|
|
2258
2272
|
});
|
|
2259
2273
|
|
|
2260
2274
|
const result = lintWorkspace(workspaceRoot, { ignoreExternal: false });
|
|
2261
2275
|
|
|
2262
|
-
expect(result.errors.join('\n')).not.toContain('
|
|
2263
|
-
expect(result.readOnlyScopes.has('myteam')).toBe(true);
|
|
2276
|
+
expect(result.errors.join('\n')).not.toContain('ghost-lib');
|
|
2264
2277
|
});
|
|
2265
2278
|
|
|
2266
2279
|
test('accepts follows scope that exists in workspace', () => {
|
|
@@ -2525,7 +2538,6 @@ test('does not warn when extends-referenced scope also appears in root index', (
|
|
|
2525
2538
|
const result = lintWorkspace(workspaceRoot, { ignoreExternal: false });
|
|
2526
2539
|
|
|
2527
2540
|
expect(result.errors.join('\n')).not.toContain('[_core-adr-policy-010.35-extends-index]');
|
|
2528
|
-
expect(result.warnings.join('\n')).not.toContain('[_core-adr-policy-010.35-extends-index]');
|
|
2529
2541
|
expect(result.errors.join('\n')).not.toContain('_core-adr-policy-022');
|
|
2530
2542
|
});
|
|
2531
2543
|
|
|
@@ -5563,7 +5575,6 @@ function scopeFiles(name, extraFields = '', scopeType = 'standard') {
|
|
|
5563
5575
|
}
|
|
5564
5576
|
|
|
5565
5577
|
const activationErrors = (result) => result.errors.filter((e) => e.includes('_core-adr-policy-022')).join('\n');
|
|
5566
|
-
const activationWarnings = (result) => result.warnings.filter((e) => e.includes('_core-adr-policy-022')).join('\n');
|
|
5567
5578
|
|
|
5568
5579
|
test('passes activation checks when only _core is installed and linked (smoke)', () => {
|
|
5569
5580
|
const workspaceRoot = createWorkspace('activation-smoke', {
|
|
@@ -5572,7 +5583,7 @@ test('passes activation checks when only _core is installed and linked (smoke)',
|
|
|
5572
5583
|
});
|
|
5573
5584
|
const result = lintWorkspace(workspaceRoot, { ignoreExternal: false });
|
|
5574
5585
|
expect(activationErrors(result)).toBe('');
|
|
5575
|
-
expect(result.
|
|
5586
|
+
expect(Object.keys(result).sort()).toEqual(['errors', 'xdrsRoot']);
|
|
5576
5587
|
});
|
|
5577
5588
|
|
|
5578
5589
|
test('reports unlisted scope without type indexes with a paste-ready line [022.01]', () => {
|
|
@@ -5691,7 +5702,6 @@ test('accepts consumer narrowing: extends-only: false with an extends-only tag [
|
|
|
5691
5702
|
});
|
|
5692
5703
|
const result = lintWorkspace(workspaceRoot, { ignoreExternal: false });
|
|
5693
5704
|
expect(activationErrors(result)).toBe('');
|
|
5694
|
-
expect(activationWarnings(result)).toBe('');
|
|
5695
5705
|
});
|
|
5696
5706
|
|
|
5697
5707
|
test.each([['quoted', '"true"'], ['capitalised', 'True']])('reports %s extends-only value in a local scope [022.07]', (_label, value) => {
|
|
@@ -5703,7 +5713,7 @@ test.each([['quoted', '"true"'], ['capitalised', 'True']])('reports %s extends-o
|
|
|
5703
5713
|
expect(activationErrors(result)).toContain('extends-only must be unquoted lowercase true or false');
|
|
5704
5714
|
});
|
|
5705
5715
|
|
|
5706
|
-
test('
|
|
5716
|
+
test('does not report an invalid extends-only value in an external scope [022.07]', () => {
|
|
5707
5717
|
const workspaceRoot = createWorkspace('activation-field-invalid-external', {
|
|
5708
5718
|
'.xdrs/index.md': rootIndex(['[_core](_core/index.md)', '[View scope lib](lib/index.md)']),
|
|
5709
5719
|
...scopeFiles('lib', 'extends-only: yes\n'),
|
|
@@ -5711,7 +5721,6 @@ test('marks external scope with invalid extends-only value read-only, treated as
|
|
|
5711
5721
|
});
|
|
5712
5722
|
const result = lintWorkspace(workspaceRoot, { ignoreExternal: false });
|
|
5713
5723
|
expect(activationErrors(result)).toBe('');
|
|
5714
|
-
expect(result.readOnlyScopes.get('lib').join('\n')).toContain('invalid extends-only value "yes"');
|
|
5715
5724
|
});
|
|
5716
5725
|
|
|
5717
5726
|
test('reports extends-only on core-type and _local scopes [022.07]', () => {
|
|
@@ -5728,17 +5737,18 @@ test('reports extends-only on core-type and _local scopes [022.07]', () => {
|
|
|
5728
5737
|
expect(errors).toContain('_local/index.md [_core-adr-policy-022.07-extends-only-field]');
|
|
5729
5738
|
});
|
|
5730
5739
|
|
|
5731
|
-
test('
|
|
5740
|
+
test('reports an error when no active scope extends an extends-only scope [022.04]', () => {
|
|
5732
5741
|
const workspaceRoot = createWorkspace('activation-unused-extends-only', {
|
|
5733
5742
|
'.xdrs/index.md': rootIndex(['[_core](_core/index.md)', '[View scope lib](lib/index.md) `extends-only`', ACTIVATION_LEGEND]),
|
|
5734
5743
|
...scopeFiles('lib'),
|
|
5735
5744
|
});
|
|
5736
5745
|
const result = lintWorkspace(workspaceRoot, { ignoreExternal: false });
|
|
5737
|
-
expect(activationErrors(result)).
|
|
5738
|
-
expect(
|
|
5746
|
+
expect(activationErrors(result)).toContain('Scope "lib" is tagged `extends-only` but no active scope extends it');
|
|
5747
|
+
expect(activationErrors(result)).toContain('add it to the extends: of an active scope or tag it `disabled`');
|
|
5748
|
+
expect(activationErrors(result)).toContain('[_core-adr-policy-022.04-extends-only]');
|
|
5739
5749
|
});
|
|
5740
5750
|
|
|
5741
|
-
test('
|
|
5751
|
+
test('reports an extends-only scope reached only through a disabled scope, and the frozen extender [022.04, 022.06]', () => {
|
|
5742
5752
|
const workspaceRoot = createWorkspace('activation-reach-through-disabled', {
|
|
5743
5753
|
'.xdrs/index.md': rootIndex(['[_core](_core/index.md)', '[View scope lib](lib/index.md) `extends-only`', '[View scope mid](mid/index.md) `disabled`', '[View scope myteam](myteam/index.md)', ACTIVATION_LEGEND]),
|
|
5744
5754
|
...scopeFiles('lib'),
|
|
@@ -5746,11 +5756,11 @@ test('warns when an extends-only scope is reached only through a disabled scope
|
|
|
5746
5756
|
...scopeFiles('myteam', 'extends: mid\n'),
|
|
5747
5757
|
});
|
|
5748
5758
|
const result = lintWorkspace(workspaceRoot, { ignoreExternal: false });
|
|
5749
|
-
expect(
|
|
5750
|
-
expect(result.
|
|
5759
|
+
expect(activationErrors(result)).toContain('Scope "lib" is tagged `extends-only`');
|
|
5760
|
+
expect(activationErrors(result)).toContain('Local scope "myteam" is frozen (read-only) because extends: scope "mid" is tagged `disabled`');
|
|
5751
5761
|
});
|
|
5752
5762
|
|
|
5753
|
-
test('
|
|
5763
|
+
test('reports a local scope following a disabled scope as frozen while it still reaches extends-only scopes [022.04, 022.06]', () => {
|
|
5754
5764
|
const workspaceRoot = createWorkspace('activation-read-only-reaches', {
|
|
5755
5765
|
'.xdrs/index.md': rootIndex(['[_core](_core/index.md)', '[View scope area-core](area-core/index.md) `disabled`', '[View scope lib](lib/index.md) `extends-only`', '[View scope myteam](myteam/index.md)', ACTIVATION_LEGEND]),
|
|
5756
5766
|
...scopeFiles('area-core', '', 'core'),
|
|
@@ -5758,22 +5768,24 @@ test('makes a scope following a disabled scope read-only while it still reaches
|
|
|
5758
5768
|
...scopeFiles('myteam', 'follows: area-core\nextends: lib\n'),
|
|
5759
5769
|
});
|
|
5760
5770
|
const result = lintWorkspace(workspaceRoot, { ignoreExternal: false });
|
|
5761
|
-
|
|
5762
|
-
expect(
|
|
5771
|
+
const errors = activationErrors(result);
|
|
5772
|
+
expect(errors).not.toContain('022.04');
|
|
5773
|
+
expect(errors).toContain('Local scope "myteam" is frozen (read-only) because follows: scope "area-core" is tagged `disabled` in the root index; enable it or remove the follows: reference: ');
|
|
5774
|
+
expect(errors).toContain('myteam/index.md [_core-adr-policy-022.06-read-only-dependencies]');
|
|
5763
5775
|
});
|
|
5764
5776
|
|
|
5765
|
-
test('
|
|
5777
|
+
test('does not report an external scope extending a disabled scope [022.06]', () => {
|
|
5766
5778
|
const workspaceRoot = createWorkspace('activation-external-extends-disabled', {
|
|
5767
5779
|
'.xdrs/index.md': rootIndex(['[_core](_core/index.md)', '[View scope lib](lib/index.md) `disabled`', '[View scope vendor](vendor/index.md)', ACTIVATION_LEGEND]),
|
|
5768
5780
|
...scopeFiles('lib'),
|
|
5769
5781
|
...scopeFiles('vendor', 'extends: lib\n'),
|
|
5770
5782
|
'.filedist.lock': '.xdrs/vendor/index.md|some-package|1.0.0\n',
|
|
5771
5783
|
});
|
|
5772
|
-
|
|
5773
|
-
expect(
|
|
5784
|
+
expect(activationErrors(lintWorkspace(workspaceRoot))).toBe('');
|
|
5785
|
+
expect(activationErrors(lintWorkspace(workspaceRoot, { ignoreExternal: false }))).toBe('');
|
|
5774
5786
|
});
|
|
5775
5787
|
|
|
5776
|
-
test('
|
|
5788
|
+
test('reports _local extending a disabled scope as frozen [022.06]', () => {
|
|
5777
5789
|
const workspaceRoot = createWorkspace('activation-local-extends-disabled', {
|
|
5778
5790
|
'.xdrs/index.md': rootIndex(['[_core](_core/index.md)', '[View scope lib](lib/index.md) `disabled`', ACTIVATION_LEGEND]),
|
|
5779
5791
|
...scopeFiles('lib'),
|
|
@@ -5781,10 +5793,10 @@ test('makes _local extending a disabled scope read-only [022.06]', () => {
|
|
|
5781
5793
|
...coreWithScopeTypes(['standard', '_local']),
|
|
5782
5794
|
});
|
|
5783
5795
|
const result = lintWorkspace(workspaceRoot, { ignoreExternal: false });
|
|
5784
|
-
expect(result.
|
|
5796
|
+
expect(activationErrors(result)).toContain('Local scope "_local" is frozen (read-only)');
|
|
5785
5797
|
});
|
|
5786
5798
|
|
|
5787
|
-
test('
|
|
5799
|
+
test('reports a local scope whose type is defined only in a disabled scope as frozen [022.06]', () => {
|
|
5788
5800
|
const workspaceRoot = createWorkspace('activation-type-in-disabled-scope', {
|
|
5789
5801
|
'.xdrs/index.md': rootIndex(['[_core](_core/index.md)', '[View scope widgets-core](widgets-core/index.md) `disabled`', '[View scope shop](shop/index.md)', ACTIVATION_LEGEND]),
|
|
5790
5802
|
'.xdrs/widgets-core/index.md': scopeIndexWith('widgets-core', '', 'core'),
|
|
@@ -5795,10 +5807,22 @@ test('makes a scope whose type is defined only in a disabled scope read-only [02
|
|
|
5795
5807
|
});
|
|
5796
5808
|
const result = lintWorkspace(workspaceRoot, { ignoreExternal: false });
|
|
5797
5809
|
expect(result.errors.join('\n')).not.toContain('has no corresponding widget-scope-type policy');
|
|
5798
|
-
expect(result
|
|
5810
|
+
expect(activationErrors(result)).toContain('Local scope "shop" is frozen (read-only) because scope type "widget" is defined only in disabled scope(s) "widgets-core"');
|
|
5811
|
+
});
|
|
5812
|
+
|
|
5813
|
+
test('reports missing dependencies of a disabled local scope as errors [022.05]', () => {
|
|
5814
|
+
const workspaceRoot = createWorkspace('activation-disabled-local-missing-deps', {
|
|
5815
|
+
'.xdrs/index.md': rootIndex(['[_core](_core/index.md)', '[View scope myteam](myteam/index.md) `disabled`', ACTIVATION_LEGEND]),
|
|
5816
|
+
...scopeFiles('myteam', 'follows: ghost-core\nextends: ghost-lib\n', 'phantom'),
|
|
5817
|
+
...coreWithScopeTypes(['standard']),
|
|
5818
|
+
});
|
|
5819
|
+
const errors = lintWorkspace(workspaceRoot, { ignoreExternal: false }).errors.join('\n');
|
|
5820
|
+
expect(errors).toContain('follows references scope "ghost-core" which does not exist in the workspace');
|
|
5821
|
+
expect(errors).toContain('ghost-lib');
|
|
5822
|
+
expect(errors).toContain('phantom');
|
|
5799
5823
|
});
|
|
5800
5824
|
|
|
5801
|
-
test('prints
|
|
5825
|
+
test('prints frozen local scopes as errors and never prints warning or READ-ONLY lines [022.06]', () => {
|
|
5802
5826
|
const workspaceRoot = createWorkspace('activation-cli-read-only', {
|
|
5803
5827
|
'.xdrs/index.md': rootIndex(['[View scope _core](_core/index.md)', '[View scope area-core](area-core/index.md) `disabled`', '[View scope lib](lib/index.md) `disabled`', '[View scope myteam](myteam/index.md)', ACTIVATION_LEGEND]),
|
|
5804
5828
|
...scopeFiles('area-core', '', 'core'),
|
|
@@ -5806,22 +5830,40 @@ test('prints one read-only line per cause citing 022.06', () => {
|
|
|
5806
5830
|
...scopeFiles('myteam', 'follows: area-core\nextends: lib\n'),
|
|
5807
5831
|
});
|
|
5808
5832
|
const { runLintCli } = require('./lint');
|
|
5809
|
-
const
|
|
5810
|
-
const
|
|
5811
|
-
const
|
|
5812
|
-
const
|
|
5833
|
+
const out = [];
|
|
5834
|
+
const errOut = [];
|
|
5835
|
+
const logSpy = jest.spyOn(console, 'log').mockImplementation((msg) => out.push(msg));
|
|
5836
|
+
const errSpy = jest.spyOn(console, 'error').mockImplementation((msg) => errOut.push(msg));
|
|
5837
|
+
const warnSpy = jest.spyOn(console, 'warn').mockImplementation((msg) => out.push(msg));
|
|
5838
|
+
let code;
|
|
5813
5839
|
try {
|
|
5814
|
-
runLintCli(['--all', workspaceRoot]);
|
|
5840
|
+
code = runLintCli(['--all', workspaceRoot]);
|
|
5815
5841
|
} finally {
|
|
5816
5842
|
logSpy.mockRestore();
|
|
5817
5843
|
errSpy.mockRestore();
|
|
5818
5844
|
warnSpy.mockRestore();
|
|
5819
5845
|
}
|
|
5820
|
-
|
|
5821
|
-
|
|
5822
|
-
|
|
5823
|
-
expect(
|
|
5824
|
-
expect(
|
|
5846
|
+
expect(code).toBe(1);
|
|
5847
|
+
const frozenLines = errOut.filter((l) => l.includes('Local scope "myteam" is frozen (read-only)'));
|
|
5848
|
+
expect(frozenLines).toHaveLength(2);
|
|
5849
|
+
expect(frozenLines.every((l) => l.includes('[_core-adr-policy-022.06-read-only-dependencies]'))).toBe(true);
|
|
5850
|
+
expect([...out, ...errOut].join('\n')).not.toMatch(/\[warning\]|READ-ONLY/);
|
|
5851
|
+
});
|
|
5852
|
+
|
|
5853
|
+
test('silently skips freeze-reference exempt checks [002.01]', () => {
|
|
5854
|
+
const frozen = (name) => teamXdrDocument('Frozen decision.')
|
|
5855
|
+
.replace(/^---\n/, `---\nfreeze-reference: true\n`)
|
|
5856
|
+
.replace(/^name: .*$/m, `name: ${name}`);
|
|
5857
|
+
const workspaceRoot = createWorkspace('freeze-reference-silent', {
|
|
5858
|
+
'.xdrs/index.md': rootIndex(['[_core](_core/index.md)', '[View scope myteam](myteam/index.md)']),
|
|
5859
|
+
'.xdrs/myteam/index.md': scopeIndexWith('myteam'),
|
|
5860
|
+
'.xdrs/myteam/adrs/index.md': teamAdrIndex(['- [850-frozen](principles/850-frozen.md) - Frozen']),
|
|
5861
|
+
'.xdrs/myteam/adrs/principles/850-frozen.md': frozen('wrong-name'),
|
|
5862
|
+
});
|
|
5863
|
+
const errors = lintWorkspace(workspaceRoot, { ignoreExternal: false }).errors.join('\n');
|
|
5864
|
+
expect(errors).not.toContain('017.05');
|
|
5865
|
+
expect(errors).not.toContain('Policy frontmatter name must be');
|
|
5866
|
+
expect(errors).not.toContain('Policy title must start with');
|
|
5825
5867
|
});
|
|
5826
5868
|
|
|
5827
5869
|
test('dry-run workspace: disabled core, extends-only ref, unlisted legacy scope', () => {
|
|
@@ -5834,10 +5876,10 @@ test('dry-run workspace: disabled core, extends-only ref, unlisted legacy scope'
|
|
|
5834
5876
|
});
|
|
5835
5877
|
const disabledIndexBefore = fs.readFileSync(path.join(workspaceRoot, '.xdrs/ecomm-core/index.md'), 'utf8');
|
|
5836
5878
|
const result = lintWorkspace(workspaceRoot, { ignoreExternal: false });
|
|
5837
|
-
|
|
5838
|
-
expect(
|
|
5839
|
-
expect(
|
|
5840
|
-
expect(
|
|
5879
|
+
const errors = activationErrors(result).split('\n');
|
|
5880
|
+
expect(errors).toHaveLength(2);
|
|
5881
|
+
expect(errors.join('\n')).toContain('[View scope legacy](legacy/index.md)');
|
|
5882
|
+
expect(errors.join('\n')).toContain('Local scope "checkout" is frozen (read-only) because follows: scope "ecomm-core" is tagged `disabled`');
|
|
5841
5883
|
expect(fs.readFileSync(path.join(workspaceRoot, '.xdrs/ecomm-core/index.md'), 'utf8')).toBe(disabledIndexBefore);
|
|
5842
5884
|
});
|
|
5843
5885
|
|
|
@@ -5872,7 +5914,7 @@ test('treats a type whose parent chain reaches core as core-type (naming, follow
|
|
|
5872
5914
|
expect(errors).toContain('_core-adr-policy-010.12-def-core-scope-policies-must-be-structured');
|
|
5873
5915
|
});
|
|
5874
5916
|
|
|
5875
|
-
test('reports missing scope-type for local scopes
|
|
5917
|
+
test('reports missing scope-type for local scopes only [010.10, 022.06]', () => {
|
|
5876
5918
|
const noType = (name) => `---\nname: ${name}\ndescription: Scope ${name}.\napply-to: Test teams\nvalid-from: 2026-01-01\n---\n\n# ${name}\n`;
|
|
5877
5919
|
const workspaceRoot = createWorkspace('scope-type-missing-local-external', {
|
|
5878
5920
|
'.xdrs/index.md': rootIndex(['[_core](_core/index.md)', '[View scope localone](localone/index.md)', '[View scope vendor](vendor/index.md)']),
|
|
@@ -5885,10 +5927,10 @@ test('reports missing scope-type for local scopes and marks external ones read-o
|
|
|
5885
5927
|
const missingTypeErrors = result.errors.filter((e) => e.includes('must include a scope-type field'));
|
|
5886
5928
|
expect(missingTypeErrors.join('\n')).toContain('localone/index.md');
|
|
5887
5929
|
expect(missingTypeErrors.join('\n')).not.toContain('vendor/index.md');
|
|
5888
|
-
expect(result.
|
|
5930
|
+
expect(result.errors.join('\n')).not.toContain('022.06');
|
|
5889
5931
|
});
|
|
5890
5932
|
|
|
5891
|
-
test('reports missing parent type for local scopes
|
|
5933
|
+
test('reports missing parent type for local scopes only [010.09, 022.06]', () => {
|
|
5892
5934
|
const workspaceRoot = createWorkspace('scope-type-missing-parent', {
|
|
5893
5935
|
'.xdrs/index.md': rootIndex(['[_core](_core/index.md)', '[View scope localone](localone/index.md)', '[View scope vendor](vendor/index.md)']),
|
|
5894
5936
|
...scopeFiles('localone', '', 'widget'),
|
|
@@ -5900,7 +5942,7 @@ test('reports missing parent type for local scopes and marks external ones read-
|
|
|
5900
5942
|
const parentErrors = result.errors.filter((e) => e.includes('declares parent "ghost"'));
|
|
5901
5943
|
expect(parentErrors).toHaveLength(1);
|
|
5902
5944
|
expect(parentErrors[0]).toContain('localone/index.md');
|
|
5903
|
-
expect(result.
|
|
5945
|
+
expect(result.errors.join('\n')).not.toMatch(/vendor\/index\.md.*ghost/);
|
|
5904
5946
|
});
|
|
5905
5947
|
|
|
5906
5948
|
test('accepts compiled combined with a non-core type [011.10]', () => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xdrs-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.53.0",
|
|
4
4
|
"description": "XDRS (eXtensible Decision Record System): 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",
|