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/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('marks scope read-only instead of erroring when follows references a missing scope for an external scope', () => {
2252
- const workspaceRoot = createWorkspace('follows-missing-scope-external', {
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('follows references scope "nonexistent-core" which does not exist in the workspace');
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.readOnlyScopes.size).toBe(0);
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('marks external scope with invalid extends-only value read-only, treated as absent [022.06]', () => {
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('warns when no active scope extends an extends-only scope [022.04]', () => {
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)).toBe('');
5738
- expect(activationWarnings(result)).toContain('Scope "lib" is tagged `extends-only` but no active scope extends it');
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('warns when an extends-only scope is reached only through a disabled scope [022.04]', () => {
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(activationWarnings(result)).toContain('Scope "lib" is tagged `extends-only`');
5750
- expect(result.readOnlyScopes.get('myteam').join('\n')).toContain('extends: scope "mid" is tagged `disabled`');
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('makes a scope following a disabled scope read-only while it still reaches extends-only scopes [022.04, 022.06]', () => {
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
- expect(activationWarnings(result)).toBe('');
5762
- expect(result.readOnlyScopes.get('myteam').join('\n')).toContain('follows: scope "area-core" is tagged `disabled` in the root index; enable it or remove the follows: reference');
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('makes an external scope extending a disabled scope read-only [022.06]', () => {
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
- const result = lintWorkspace(workspaceRoot);
5773
- expect(result.readOnlyScopes.has('vendor')).toBe(true);
5784
+ expect(activationErrors(lintWorkspace(workspaceRoot))).toBe('');
5785
+ expect(activationErrors(lintWorkspace(workspaceRoot, { ignoreExternal: false }))).toBe('');
5774
5786
  });
5775
5787
 
5776
- test('makes _local extending a disabled scope read-only [022.06]', () => {
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.readOnlyScopes.has('_local')).toBe(true);
5796
+ expect(activationErrors(result)).toContain('Local scope "_local" is frozen (read-only)');
5785
5797
  });
5786
5798
 
5787
- test('makes a scope whose type is defined only in a disabled scope read-only [022.06]', () => {
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.readOnlyScopes.get('shop').join('\n')).toContain('scope type "widget" is defined only in disabled scope(s) "widgets-core"');
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 one read-only line per cause citing 022.06', () => {
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 logs = [];
5810
- const logSpy = jest.spyOn(console, 'log').mockImplementation((msg) => logs.push(msg));
5811
- const errSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
5812
- const warnSpy = jest.spyOn(console, 'warn').mockImplementation(() => {});
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
- const result = lintWorkspace(workspaceRoot, { ignoreExternal: false });
5821
- expect(result.readOnlyScopes.get('myteam')).toHaveLength(2);
5822
- const readOnlyLines = logs.filter((l) => l.includes('READ-ONLY scope "myteam"'));
5823
- expect(readOnlyLines).toHaveLength(2);
5824
- expect(readOnlyLines.every((l) => l.includes('[_core-adr-policy-022.06-read-only-dependencies]'))).toBe(true);
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
- expect(result.readOnlyScopes.get('checkout')).toHaveLength(1);
5838
- expect(activationWarnings(result)).toBe('');
5839
- expect(activationErrors(result)).toContain('[View scope legacy](legacy/index.md)');
5840
- expect(activationErrors(result).split('\n')).toHaveLength(1);
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 and marks external ones read-only [010.10, 022.06]', () => {
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.readOnlyScopes.get('vendor').join('\n')).toContain('no scope-type');
5930
+ expect(result.errors.join('\n')).not.toContain('022.06');
5889
5931
  });
5890
5932
 
5891
- test('reports missing parent type for local scopes and marks external ones read-only [010.09, 022.06]', () => {
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.readOnlyScopes.get('vendor').join('\n')).toContain('scope type "ghost"');
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.52.2",
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",