xdrs-core 0.44.0 → 0.45.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.
@@ -26,14 +26,9 @@ Optional questions (ask only when genuinely unclear):
26
26
  - **Type**: Should the article primarily synthesize ADRs, BDRs, or EDRs? Ask only when the topic spans multiple types.
27
27
  - **Existing XDRS elements**: Are there specific Policies or Skills you want the article to reference or synthesize?
28
28
 
29
- **Prerequisites gate** — once the scope is confirmed from the answers above, perform ALL of the following checks before proceeding. If ANY check fails, output a FAIL result immediately and do not continue:
29
+ **Scope placement** — once the scope is known from the answers above, run the scope placement analysis from the shared module at `.xdrs/_core/adrs/principles/skills/.assets/scope-placement.md` to confirm it is the right placement for this content.
30
30
 
31
- - **Follows scopes:** If the scope declares `follows:` entries (e.g., `follows: myarea-core, shared-standards`), verify that each listed scope directory exists in the workspace AND contains an accessible `index.md` (e.g., `.xdrs/[scope-name]/index.md`). If any listed scope is missing or unreadable, output: `FAIL — Cannot proceed: scope \`[scope-name]\` is listed in \`follows\` but its policies are not present in the workspace. Install it before authoring documents in this scope, as the governance constraints cannot be verified.`
32
- - **Local meta-policies:** Scan the target scope's `[type]/principles/` directories for all files whose filename title starts with `core` (i.e., `NNN-core.md` or `NNN-core-{qualifier}.md`), excluding scope-type definition files. If any are found but cannot be read, output: `FAIL — Cannot proceed: local meta-policy \`[filename]\` exists in scope \`[scope-name]\` but could not be read. Without it, the document cannot be authored in full compliance with the scope's governance.` Zero matches is valid.
33
- - **Scope-type:** Read the scope's `scope-type` from its `index.md`. Normalise the value to a list: split on commas and trim whitespace (`scope-type: typeA, typeB` becomes `[typeA, typeB]`). For **each** type in the list, search the `[type]/principles/` directories of all `core`-type scopes in the workspace for a file whose name ends with `{scope-type}-scope-type.md`. If the scope declares any `scope-type` elements but no matching file is found for ANY of them, output: `FAIL — Cannot proceed: scope \`[scope-name]\` declares \`scope-type: [scope-type]\` but no \`[scope-type]-scope-type.md\` policy exists in any \`core\`-type scope in the workspace. The scope is READ-ONLY; install the scope-type governance before authoring documents in this scope.`
34
- - **Rationale:** Authoring a document without all mandatory governance layers loaded risks producing content that silently violates scope policies. Every governance layer MUST be present before writing begins.
35
-
36
- Once all prerequisites pass, load ALL scope-type governance (see `_core-adr-policy-010` rule 22): for each declared type, resolve its full ancestor chain (primary, companions alphabetically, parent chain). Deduplicate keeping first occurrence. Then run conflict detection using the shared module at `.xdrs/_core/adrs/principles/skills/.assets/conflict-detection.md` across all loaded governance layers. Halt if undeclared structural conflicts are found (see `_core-adr-policy-010` rules 24 and 26).
31
+ **Prerequisites gate** — once scope is confirmed, run the prerequisites gate from the shared module at `.xdrs/_core/adrs/principles/skills/.assets/prerequisites-gate.md`. Substitute `[DOCUMENT TYPE]` with `article`.
37
32
 
38
33
  Do NOT proceed to Phase 1 until you have at minimum a clear **topic** and **audience**.
39
34
 
@@ -49,8 +44,7 @@ Do NOT proceed to Phase 1 until you have at minimum a clear **topic** and **audi
49
44
 
50
45
  Consult `001-xdrs-standards` while making each choice in this phase. The summaries below are orientation only; when any detail is unclear, the standard decides.
51
46
 
52
- **Scope** — use `_local` unless the user explicitly names another scope.
53
- - If the user names a scope other than `_local`, check the workspace root `.filedist.lock` file. If any file under `.xdrs/[scope]/` appears in `.filedist.lock`, the scope is external and new documents MUST NOT be created there. Inform the user and ask them to choose a non-external scope.
47
+ **Scope** — confirmed in Phase 0. Follow the external-scope validation in `.xdrs/_core/adrs/principles/skills/.assets/scope-selection.md`.
54
48
 
55
49
  **Type** — match the type of the XDRS elements the article primarily synthesizes (`adrs`, `bdrs`, or `edrs`).
56
50
  If the topic spans multiple types, use `adrs`. Use the same rules as `002-write-policy` Phase 2:
@@ -118,15 +112,11 @@ Rules to apply while drafting:
118
112
  3. Add back-references in the Policies, Research documents, and Skills that the article synthesizes, under their `## References`
119
113
  section.
120
114
  4. Evaluate whether the scope index at `.xdrs/[scope]/index.md` should be updated to reflect the new article. If the scope index does not exist, create it following article standards and the scope index rules in `_core-adr-policy-001`.
121
- 5. **Meta-policy compliance**: Check the target scope's `index.md` for a `follows` frontmatter field. `_core` Policies always apply to all scopes. If `follows` lists additional core scope names, verify that each listed scope directory exists in the workspace (e.g., `.xdrs/[scope-name]/index.md`). If any listed scope is missing, STOP immediately and tell the user: "Scope `[scope-name]` is listed in `follows` but not found in the workspace. Install it before proceeding." Once all `follows` scopes are confirmed present, verify the article satisfies any content or structural requirements from those Policies. Last-listed scope in `follows` takes precedence.
115
+ 5. **Meta-policy compliance**: Run the shared module at `.xdrs/_core/adrs/principles/skills/.assets/meta-policy-compliance.md`. Substitute `[DOCUMENT]` with `article`.
122
116
 
123
117
  ### Phase 7: Verify with Lint
124
118
 
125
- 1. Run the CLI lint utility from the repository root:
126
- ```
127
- npx -y xdrs-core@latest lint
128
- ```
129
- 2. Fix all reported errors before considering the task complete.
119
+ Follow the lint verification steps in `.xdrs/_core/adrs/principles/skills/.assets/lint-verification.md`.
130
120
 
131
121
  ## Examples
132
122
 
@@ -17,16 +17,10 @@ This skill is interactive by design. Ask clarifying questions to the user at eac
17
17
 
18
18
  ## Instructions
19
19
 
20
- ### Phase 0: Prerequisites Gate — MUST complete before writing
20
+ ### Phase 0: Scope Placement and Prerequisites Gate — MUST complete before writing
21
21
 
22
- Identify the target scope from the user's request; use `_local` if none is specified. Read the scope's `index.md` frontmatter and perform ALL of the following checks. If ANY check fails, output a FAIL result immediately and do not proceed:
23
-
24
- - **Follows scopes:** If the scope declares `follows:` entries (e.g., `follows: myarea-core, shared-standards`), verify that each listed scope directory exists in the workspace AND contains an accessible `index.md` (e.g., `.xdrs/[scope-name]/index.md`). If any listed scope is missing or unreadable, output: `FAIL — Cannot proceed: scope \`[scope-name]\` is listed in \`follows\` but its policies are not present in the workspace. Install it before authoring documents in this scope, as the governance constraints cannot be verified.`
25
- - **Local meta-policies:** Scan the target scope's `[type]/principles/` directories for all files whose filename title starts with `core` (i.e., `NNN-core.md` or `NNN-core-{qualifier}.md`), excluding scope-type definition files. If any are found but cannot be read, output: `FAIL — Cannot proceed: local meta-policy \`[filename]\` exists in scope \`[scope-name]\` but could not be read. Without it, the document cannot be authored in full compliance with the scope's governance.` Zero matches is valid.
26
- - **Scope-type:** Read the scope's `scope-type` from its `index.md`. Normalise the value to a list: split on commas and trim whitespace (`scope-type: typeA, typeB` becomes `[typeA, typeB]`). For **each** type in the list, search the `[type]/principles/` directories of all `core`-type scopes in the workspace for a file whose name ends with `{scope-type}-scope-type.md`. If the scope declares any `scope-type` elements but no matching file is found for ANY of them, output: `FAIL — Cannot proceed: scope \`[scope-name]\` declares \`scope-type: [scope-type]\` but no \`[scope-type]-scope-type.md\` policy exists in any \`core\`-type scope in the workspace. The scope is READ-ONLY; install the scope-type governance before authoring documents in this scope.`
27
- - **Rationale:** Authoring a document without all mandatory governance layers loaded risks producing content that silently violates scope policies. Every governance layer MUST be present before writing begins.
28
-
29
- Once all prerequisites pass, load ALL scope-type governance (see `_core-adr-policy-010` rule 22): for each declared type, resolve its full ancestor chain (primary, companions alphabetically, parent chain). Deduplicate keeping first occurrence. Then run conflict detection using the shared module at `.xdrs/_core/adrs/principles/skills/.assets/conflict-detection.md` across all loaded governance layers. Halt if undeclared structural conflicts are found (see `_core-adr-policy-010` rules 24 and 26).
22
+ 1. Run the scope placement analysis from the shared module at `.xdrs/_core/adrs/principles/skills/.assets/scope-placement.md` to determine and confirm the target scope.
23
+ 2. Once the scope is confirmed, run the prerequisites gate from the shared module at `.xdrs/_core/adrs/principles/skills/.assets/prerequisites-gate.md`. Substitute `[DOCUMENT TYPE]` with `research`.
30
24
 
31
25
  ### Phase 1: Understand the Research Goal
32
26
 
@@ -46,8 +40,7 @@ If the answers from Phase 1 leave scope, type, or subject ambiguous, use `vscode
46
40
 
47
41
  Consult `001-xdrs-standards` while making each choice in this phase. The summaries below are orientation only; when any detail matters, the standard decides.
48
42
 
49
- **Scope** — use `_local` unless the user explicitly names another scope.
50
- - If the user names a scope other than `_local`, check the workspace root `.filedist.lock` file. If any file under `.xdrs/[scope]/` appears in `.filedist.lock`, the scope is external and new documents MUST NOT be created there. Inform the user and ask them to choose a non-external scope.
43
+ **Scope** — confirmed in Phase 0. Follow the external-scope validation in `.xdrs/_core/adrs/principles/skills/.assets/scope-selection.md`.
51
44
 
52
45
  **Type** — match the type of decision this research supports (`adrs`, `bdrs`, or `edrs`). Use the same rules as `002-write-policy` Phase 2:
53
46
  - **BDR**: business process, product policy, strategic rule, operational procedure
@@ -248,7 +241,7 @@ Before writing files, verify:
248
241
  5. **Standalone focus**: Does the text read as a technical paper rather than commentary about future ADRs, repository process, or artifact management?
249
242
  6. **Ratio fit**: Does the document stay within section word limits and pass the Python ratio check, or does the introduction explicitly justify the deviation?
250
243
  7. **References**: Are all related Policies, research docs, skills, articles, and external sources linked when relevant?
251
- 8. **Meta-policy compliance**: Check the target scope's `index.md` for a `follows` frontmatter field. `_core` Policies always apply to all scopes. If `follows` lists additional core scope names, verify that each listed scope directory exists in the workspace (e.g., `.xdrs/[scope-name]/index.md`). If any listed scope is missing, STOP immediately and tell the user: "Scope `[scope-name]` is listed in `follows` but not found in the workspace. Install it before proceeding." Once all `follows` scopes are confirmed present, verify the research satisfies any structural or content requirements from those Policies. Last-listed scope in `follows` takes precedence.
244
+ 8. **Meta-policy compliance**: Run the shared module at `.xdrs/_core/adrs/principles/skills/.assets/meta-policy-compliance.md`. Substitute `[DOCUMENT]` with `research`.
252
245
 
253
246
  If any check fails, revise before continuing.
254
247
 
@@ -261,11 +254,7 @@ If any check fails, revise before continuing.
261
254
 
262
255
  ### Phase 12: Verify with Lint
263
256
 
264
- 1. Run the CLI lint utility from the repository root:
265
- ```
266
- npx -y xdrs-core@latest lint
267
- ```
268
- 2. Fix all reported errors before considering the task complete.
257
+ Follow the lint verification steps in `.xdrs/_core/adrs/principles/skills/.assets/lint-verification.md`.
269
258
 
270
259
  ## Examples
271
260
 
@@ -15,16 +15,10 @@ Guides the creation of a well-structured plan document by following `_core-adr-p
15
15
 
16
16
  ## Instructions
17
17
 
18
- ### Phase 0: Prerequisites Gate — MUST complete before writing
18
+ ### Phase 0: Scope Placement and Prerequisites Gate — MUST complete before writing
19
19
 
20
- Identify the target scope from the user's request; use `_local` if none is specified. Read the scope's `index.md` frontmatter and perform ALL of the following checks. If ANY check fails, output a FAIL result immediately and do not proceed:
21
-
22
- - **Follows scopes:** If the scope declares `follows:` entries (e.g., `follows: myarea-core, shared-standards`), verify that each listed scope directory exists in the workspace AND contains an accessible `index.md` (e.g., `.xdrs/[scope-name]/index.md`). If any listed scope is missing or unreadable, output: `FAIL — Cannot proceed: scope \`[scope-name]\` is listed in \`follows\` but its policies are not present in the workspace. Install it before authoring documents in this scope, as the governance constraints cannot be verified.`
23
- - **Local meta-policies:** Scan the target scope's `[type]/principles/` directories for all files whose filename title starts with `core` (i.e., `NNN-core.md` or `NNN-core-{qualifier}.md`), excluding scope-type definition files. If any are found but cannot be read, output: `FAIL — Cannot proceed: local meta-policy \`[filename]\` exists in scope \`[scope-name]\` but could not be read. Without it, the document cannot be authored in full compliance with the scope's governance.` Zero matches is valid.
24
- - **Scope-type:** Read the scope's `scope-type` from its `index.md`. Normalise the value to a list: split on commas and trim whitespace (`scope-type: typeA, typeB` becomes `[typeA, typeB]`). For **each** type in the list, search the `[type]/principles/` directories of all `core`-type scopes in the workspace for a file whose name ends with `{scope-type}-scope-type.md`. If the scope declares any `scope-type` elements but no matching file is found for ANY of them, output: `FAIL — Cannot proceed: scope \`[scope-name]\` declares \`scope-type: [scope-type]\` but no \`[scope-type]-scope-type.md\` policy exists in any \`core\`-type scope in the workspace. The scope is READ-ONLY; install the scope-type governance before authoring documents in this scope.`
25
- - **Rationale:** Authoring a document without all mandatory governance layers loaded risks producing content that silently violates scope policies. Every governance layer MUST be present before writing begins.
26
-
27
- Once all prerequisites pass, load ALL scope-type governance (see `_core-adr-policy-010` rule 22): for each declared type, resolve its full ancestor chain (primary, companions alphabetically, parent chain). Deduplicate keeping first occurrence. Then run conflict detection using the shared module at `.xdrs/_core/adrs/principles/skills/.assets/conflict-detection.md` across all loaded governance layers. Halt if undeclared structural conflicts are found (see `_core-adr-policy-010` rules 24 and 26).
20
+ 1. Run the scope placement analysis from the shared module at `.xdrs/_core/adrs/principles/skills/.assets/scope-placement.md` to determine and confirm the target scope.
21
+ 2. Once the scope is confirmed, run the prerequisites gate from the shared module at `.xdrs/_core/adrs/principles/skills/.assets/prerequisites-gate.md`. Substitute `[DOCUMENT TYPE]` with `plan`.
28
22
 
29
23
  ### Phase 1: Understand the Plan Goal
30
24
 
@@ -43,8 +37,7 @@ Once all prerequisites pass, load ALL scope-type governance (see `_core-adr-poli
43
37
 
44
38
  Consult `001-xdrs-standards` while making each choice in this phase. The summaries below are orientation only; when any detail is unclear, the standard decides.
45
39
 
46
- **Scope** — use `_local` unless the user explicitly names another scope.
47
- - If the user names a scope other than `_local`, check the workspace root `.filedist.lock` file. If any file under `.xdrs/[scope]/` appears in `.filedist.lock`, the scope is external and new documents MUST NOT be created there. Inform the user and ask them to choose a non-external scope.
40
+ **Scope** — confirmed in Phase 0. Follow the external-scope validation in `.xdrs/_core/adrs/principles/skills/.assets/scope-selection.md`.
48
41
 
49
42
  **Type** — match the type of the Policies the plan primarily implements or relates to (`adrs`, `bdrs`, or `edrs`).
50
43
  - **BDR**: business process, product policy, strategic rule, operational procedure
@@ -136,15 +129,11 @@ Rules to apply while drafting:
136
129
  2. Add a link to the plan in the canonical index for that scope+type (`.xdrs/[scope]/[type]/index.md`).
137
130
  3. Add back-references in the Policies, Research documents, and Skills that the plan relates to, under their `## References` section.
138
131
  4. Evaluate whether the scope index at `.xdrs/[scope]/index.md` should be updated to reflect the new plan. If the scope index does not exist, create it following article standards and the scope index rules in `_core-adr-policy-001`.
139
- 5. **Meta-policy compliance**: Check the target scope's `index.md` for a `follows` frontmatter field. `_core` Policies always apply to all scopes. If `follows` lists additional core scope names, verify that each listed scope directory exists in the workspace (e.g., `.xdrs/[scope-name]/index.md`). If any listed scope is missing, STOP immediately and tell the user: "Scope `[scope-name]` is listed in `follows` but not found in the workspace. Install it before proceeding." Once all `follows` scopes are confirmed present, verify the plan satisfies any structural or content requirements from those Policies. Last-listed scope in `follows` takes precedence.
132
+ 5. **Meta-policy compliance**: Run the shared module at `.xdrs/_core/adrs/principles/skills/.assets/meta-policy-compliance.md`. Substitute `[DOCUMENT]` with `plan`.
140
133
 
141
134
  ### Phase 7: Verify with Lint
142
135
 
143
- 1. Run the CLI lint utility from the repository root:
144
- ```
145
- npx -y xdrs-core@latest lint
146
- ```
147
- 2. Fix all reported errors before considering the task complete.
136
+ Follow the lint verification steps in `.xdrs/_core/adrs/principles/skills/.assets/lint-verification.md`.
148
137
 
149
138
  ## Examples
150
139
 
@@ -16,14 +16,7 @@ Guides the creation of a Marp Markdown slide presentation that supports an exist
16
16
 
17
17
  ### Phase 0: Prerequisites Gate — MUST complete before writing
18
18
 
19
- Identify the target scope from the parent document's path or the user's request; use `_local` if none is specified. Read the scope's `index.md` frontmatter and perform ALL of the following checks. If ANY check fails, output a FAIL result immediately and do not proceed:
20
-
21
- - **Follows scopes:** If the scope declares `follows:` entries (e.g., `follows: myarea-core, shared-standards`), verify that each listed scope directory exists in the workspace AND contains an accessible `index.md` (e.g., `.xdrs/[scope-name]/index.md`). If any listed scope is missing or unreadable, output: `FAIL — Cannot proceed: scope \`[scope-name]\` is listed in \`follows\` but its policies are not present in the workspace. Install it before authoring documents in this scope, as the governance constraints cannot be verified.`
22
- - **Local meta-policies:** Scan the target scope's `[type]/principles/` directories for all files whose filename title starts with `core` (i.e., `NNN-core.md` or `NNN-core-{qualifier}.md`), excluding scope-type definition files. If any are found but cannot be read, output: `FAIL — Cannot proceed: local meta-policy \`[filename]\` exists in scope \`[scope-name]\` but could not be read. Without it, the document cannot be authored in full compliance with the scope's governance.` Zero matches is valid.
23
- - **Scope-type:** Read the scope's `scope-type` from its `index.md`. Normalise the value to a list: split on commas and trim whitespace (`scope-type: typeA, typeB` becomes `[typeA, typeB]`). For **each** type in the list, search the `[type]/principles/` directories of all `core`-type scopes in the workspace for a file whose name ends with `{scope-type}-scope-type.md`. If the scope declares any `scope-type` elements but no matching file is found for ANY of them, output: `FAIL — Cannot proceed: scope \`[scope-name]\` declares \`scope-type: [scope-type]\` but no \`[scope-type]-scope-type.md\` policy exists in any \`core\`-type scope in the workspace. The scope is READ-ONLY; install the scope-type governance before authoring documents in this scope.`
24
- - **Rationale:** Authoring a document without all mandatory governance layers loaded risks producing content that silently violates scope policies. Every governance layer MUST be present before writing begins.
25
-
26
- Once all prerequisites pass, load ALL scope-type governance (see `_core-adr-policy-010` rule 22): for each declared type, resolve its full ancestor chain (primary, companions alphabetically, parent chain). Deduplicate keeping first occurrence. Then run conflict detection using the shared module at `.xdrs/_core/adrs/principles/skills/.assets/conflict-detection.md` across all loaded governance layers. Halt if undeclared structural conflicts are found (see `_core-adr-policy-010` rules 24 and 26).
19
+ Identify the target scope from the parent document's path or the user's request; use `_local` if none is specified. Then run the prerequisites gate from the shared module at `.xdrs/_core/adrs/principles/skills/.assets/prerequisites-gate.md`. Substitute `[DOCUMENT TYPE]` with `presentation`.
27
20
 
28
21
  ### Phase 1: Identify the Parent Document
29
22
 
@@ -135,11 +128,7 @@ Rules:
135
128
 
136
129
  ### Phase 8: Verify with Lint
137
130
 
138
- 1. Run the CLI lint utility from the repository root:
139
- ```
140
- npx -y xdrs-core@latest lint
141
- ```
142
- 2. Fix all reported errors before considering the task complete.
131
+ Follow the lint verification steps in `.xdrs/_core/adrs/principles/skills/.assets/lint-verification.md`.
143
132
 
144
133
  ### Constraints
145
134
 
@@ -28,14 +28,7 @@ Routes the request to the appropriate XDRS authoring skill based on the type of
28
28
 
29
29
  ### Phase 1.5: Prerequisites Gate — MUST complete before writing
30
30
 
31
- For the target scope where the document will be created, read its `index.md` frontmatter and perform ALL of the following checks. If ANY check fails, output a FAIL result immediately and do not proceed:
32
-
33
- - **Follows scopes:** If the scope declares `follows:` entries (e.g., `follows: myarea-core, shared-standards`), verify that each listed scope directory exists in the workspace AND contains an accessible `index.md` (e.g., `.xdrs/[scope-name]/index.md`). If any listed scope is missing or unreadable, output: `FAIL — Cannot proceed: scope \`[scope-name]\` is listed in \`follows\` but its policies are not present in the workspace. Install it before authoring documents in this scope, as the governance constraints cannot be verified.`
34
- - **Local meta-policies:** Scan the target scope's `[type]/principles/` directories for all files whose filename title starts with `core` (i.e., `NNN-core.md` or `NNN-core-{qualifier}.md`), excluding scope-type definition files. If any are found but cannot be read, output: `FAIL — Cannot proceed: local meta-policy \`[filename]\` exists in scope \`[scope-name]\` but could not be read. Without it, the document cannot be authored in full compliance with the scope's governance.` Zero matches is valid. When routing to the policy skill, apply all found local meta-policies as mandatory conventions.
35
- - **Scope-type:** Read the scope's `scope-type` from its `index.md`. Normalise the value to a list: split on commas and trim whitespace (`scope-type: typeA, typeB` becomes `[typeA, typeB]`). For **each** type in the list, search the `[type]/principles/` directories of all `core`-type scopes in the workspace for a file whose name ends with `{scope-type}-scope-type.md`. If the scope declares any `scope-type` elements but no matching file is found for ANY of them, output: `FAIL — Cannot proceed: scope \`[scope-name]\` declares \`scope-type: [scope-type]\` but no \`[scope-type]-scope-type.md\` policy exists in any \`core\`-type scope in the workspace. The scope is READ-ONLY; install the scope-type governance before authoring documents in this scope.`
36
- - **Rationale:** Authoring a document without all mandatory governance layers loaded risks producing content that silently violates scope policies. Every governance layer MUST be present before writing begins.
37
-
38
- Once all prerequisites pass, load ALL scope-type governance (see `_core-adr-policy-010` rule 22): for each declared type, resolve its full ancestor chain (primary, companions alphabetically, parent chain). Deduplicate keeping first occurrence. Then run conflict detection using the shared module at `.xdrs/_core/adrs/principles/skills/.assets/conflict-detection.md` across all loaded governance layers. Halt if undeclared structural conflicts are found (see `_core-adr-policy-010` rules 24 and 26).
31
+ For the target scope where the document will be created, run the prerequisites gate from the shared module at `.xdrs/_core/adrs/principles/skills/.assets/prerequisites-gate.md`. Substitute `[DOCUMENT TYPE]` with `document`. When routing to the policy skill, apply all local meta-policies found during the gate as mandatory conventions.
39
32
 
40
33
  ### Phase 2: Load and Follow Target Skill
41
34
 
@@ -0,0 +1,121 @@
1
+ ---
2
+ name: 009-compile-scope
3
+ description: >
4
+ Compiles or updates any scope declaring `scope-type: compiled` from its configured external sources. Reads compilation meta-policies to discover sources, fetches content (git clone, local copy, or web scrape), plans policy changes, migrates policies one at a time with structured format, `## Source` sections, and `**compilation-note:**` markers, then runs lint and review. Documents source inconsistencies without inventing fixes. Activate when the user asks to compile, update, sync, refresh, or recompile a compiled scope.
5
+ metadata:
6
+ author: flaviostutz
7
+ version: "1.0"
8
+ ---
9
+
10
+ ## Overview
11
+
12
+ Performs a full compilation cycle for any scope declaring `scope-type: compiled`: discovers compilation meta-policies across all type folders, fetches sources into a temporary directory, plans which policies to create, update, or remove, migrates them one at a time with full source traceability, runs lint and review, and documents any inconsistencies found in the source. Works for both initial compilation and re-compilation (updates). Requires `_core-adr-policy-019-compiled-scope-type` to be present in the workspace.
13
+
14
+ ## Instructions
15
+
16
+ ### Phase 0: Prerequisites Gate
17
+
18
+ 1. Identify the target scope. Verify its `index.md` declares `scope-type: compiled` (alone or combined with other types). If not, output: `FAIL — [scope-name] does not declare scope-type: compiled. This skill only applies to compiled scopes.`
19
+ 2. Scan ALL type folders (`adrs/`, `bdrs/`, `edrs/`) inside the target scope for local meta-policy files whose filename matches `NNN-core.md` in `[type]/principles/`. Collect every match.
20
+ 3. If no meta-policy is found in any type folder, output: `FAIL — Compilation cannot proceed: no local meta-policy NNN-core.md found in any [type]/principles/ folder of [scope-name]. Create it first with ## Sources and ## Selectors sections per _core-adr-policy-019 rule 07.`
21
+ 4. Read every found meta-policy fully. For each, extract:
22
+ - **Sources**: parse all `- [web] ...`, `- [git] ...`, `- [local] ...` bullet entries from the `## Sources` section.
23
+ - **Selectors**: read the `## Selectors` section content (used in Phase 2).
24
+ - **Fetch Procedures**: read the `## Fetch Procedures` section if present (used in Phase 1).
25
+ - **Compilation Notes**: read if present (used in Phase 2–3).
26
+ - **Review Notes**: read if present (used in Phase 6).
27
+
28
+ ### Phase 1: Fetch Sources
29
+
30
+ 1. Create a temporary directory `.tmp/compilation-[YYYYMMDDHHMMSS]/` in the workspace root.
31
+ 2. For each source discovered in Phase 0, fetch in the following preference order (first available wins; if all equivalent URLs are listed, try each in order):
32
+ - **Git URL** (`- [git] ...`): run `git clone --depth=1 [url] .tmp/compilation-[ts]/source-N/`. If `git` is not available or the clone fails, fall back to the next option.
33
+ - **Local folder** (`- [local] ...`): copy the folder contents to `.tmp/compilation-[ts]/source-N/`.
34
+ - **Web URL** (`- [web] ...`): before scraping, search the workspace for a skill specialized in fetching content from this URL or website domain (e.g., a skill whose description or name references the domain, the product, or the content type). Some websites require special handling — SSO, client certificates, shadow DOM, iframes, CAPTCHAs, or other quirks. If a specialized fetch skill is found, follow it to retrieve the content. If no specialized skill exists, fall back to `npx --package=@playwright/cli@latest playwright-cli`; run with `--help` to confirm available commands and save output as `.html` file(s) in `.tmp/compilation-[ts]/source-N/`.
35
+ 3. If any source cannot be fetched via any listed option, report the failure and continue with remaining sources. Do not abort the entire compilation for a single failed source.
36
+ 4. Convert non-markdown documents found in the fetched source directories. Use only the format converters actually needed:
37
+ - PDF files: `uvx markitdown[pdf] <file> -o <output>.md`
38
+ - XLSX files: `uvx markitdown[xlsx] <file> -o <output>.md`
39
+ - DOCX files: `uvx markitdown[docx] <file> -o <output>.md`
40
+ - PPTX files: `uvx markitdown[pptx] <file> -o <output>.md`
41
+ - HTML files saved from web scraping: `uvx markitdown <file.html> -o <output>.md`
42
+
43
+ ### Phase 2: Plan Policies
44
+
45
+ 1. For each type folder governed by a meta-policy (from Phase 0), apply the `## Selectors` rules to the fetched content to determine which portions to include.
46
+ 2. List all existing policies in the scope under each governed type folder.
47
+ 3. Analyse the selected content and produce a TODO list showing for each expected policy:
48
+ - **CREATE** — new policy to be compiled from source (does not yet exist in scope)
49
+ - **UPDATE** — existing policy whose source content has changed
50
+ - **REMOVE** — existing policy whose source content no longer exists or is excluded by selectors
51
+ - **KEEP** — existing policy that is up to date; no action needed
52
+ 4. Present the TODO list to the user and wait for confirmation before proceeding to Phase 3.
53
+
54
+ ### Phase 3: Migrate Policies (one at a time)
55
+
56
+ For each TODO item marked CREATE or UPDATE, in sequence:
57
+
58
+ 1. **Write the policy file** following `_core-adr-policy-019` and `_core-adr-policy-008`:
59
+ - Use standard policy frontmatter: `name`, `description`, `apply-to`, `valid-from`. Generate these from the source text; they are exempt from the no-invented-content rule per policy-019 rule 05.
60
+ - Use structured numbered rule blocks for all claims (per `_core-adr-policy-008`).
61
+ - Transcribe source content faithfully. Do NOT invent, infer beyond what the source states, or fill gaps. Preserve ambiguities as-is.
62
+
63
+ 2. **Add a `**compilation-note:**` marker** inside a rule body only when the mapping from source to rule is not obvious — for example: indirect references, conclusions drawn by connecting multiple source sections, interpretations that required judgment, or cases where the same source passage could be read differently. Do NOT add a marker when the rule is a direct, literal transcription of a single source statement. Place the marker as a new sentence at the end of the rule body. Example:
64
+ ```
65
+ All API keys MUST be rotated every 90 days. **compilation-note:** derived from [source-1/security/api-guidelines.md section 4.2] — "Keys should be invalidated and replaced quarterly"; frequency inferred as 90 days from the term "quarterly".
66
+ ```
67
+
68
+ 3. **Add a `## Source` section** as the LAST section of the policy (after `## References` if present, otherwise after `## Decision Outcome`). List the relative file path(s) of the source document(s) as plain text — NOT as markdown links. Paths are relative to the temporary source directory (e.g., `source-1/docs/chapter-3.md`).
69
+ ```
70
+ ## Source
71
+
72
+ - source-1/standards/section-4/api-security.md
73
+ ```
74
+
75
+ 4. For REMOVE items: delete the policy file and remove its entry from the type's `index.md`.
76
+
77
+ 5. Note any content in the source that is inconsistent, contradictory, or cannot be faithfully expressed as a valid XDRS policy. Record these for Phase 7.
78
+
79
+ ### Phase 4: Verify Each Policy
80
+
81
+ After writing each policy:
82
+
83
+ 1. Confirm the `## Source` section is present with at least one path entry.
84
+ 2. Confirm the policy uses structured numbered rule blocks (`_core-adr-policy-008`).
85
+ 3. Confirm `**compilation-note:**` markers are present for every rule whose mapping from source is non-obvious (indirect references, multi-source conclusions, interpretations requiring judgment). Direct literal transcriptions do not require a marker.
86
+ 4. Confirm no normative statements are present that cannot be traced to the listed source file.
87
+ 5. Add any new inconsistencies found to the list for Phase 7.
88
+
89
+ ### Phase 5: Lint
90
+
91
+ 1. Run `npx xdrs-core lint` from the workspace root (or `make lint` if a `Makefile` is present with a `lint` target).
92
+ 2. Review all reported errors and warnings.
93
+ 3. Fix only mechanical and structural errors (missing required frontmatter fields, naming violations, index link mismatches). Do NOT invent content to satisfy content-level findings.
94
+ 4. Re-run lint after fixes until it passes or only unfixable findings remain.
95
+
96
+ ### Phase 6: Review
97
+
98
+ 1. Read and follow `.xdrs/_core/adrs/principles/skills/001-review/SKILL.md` fully, scoped to the compiled scope.
99
+ 2. Apply any review notes from the meta-policy's `## Review Notes` section as additional mandatory conventions during this review.
100
+ 3. Fix only mechanical and structural findings from the review. Do NOT invent content to resolve content-level findings.
101
+ 4. Any review findings that require inventing or guessing content MUST be recorded as inconsistencies in Phase 7.
102
+
103
+ ### Phase 7: Document Inconsistencies
104
+
105
+ 1. Collect all inconsistencies noted during Phases 3, 4, and 6.
106
+ 2. If any inconsistencies exist:
107
+ - Determine the next available policy number in the `001–100` principles block for the relevant type folder.
108
+ - Create or update `[type]/principles/NNN-inconsistencies-from-source.md` in the scope.
109
+ - The policy `name` field MUST be `[scope]-[type]-policy-NNN-inconsistencies-from-source`.
110
+ - List each inconsistency with: source file path, affected compiled policy, and a description of the inconsistency.
111
+ - Do NOT propose or include fixes.
112
+ 3. If no inconsistencies exist, report that the compilation is clean.
113
+
114
+ ### Phase 8: Cleanup
115
+
116
+ 1. Remove the temporary directory `.tmp/compilation-[ts]/` created in Phase 1.
117
+ 2. Report a compilation summary:
118
+ - Policies created, updated, removed.
119
+ - Inconsistencies documented (with file path).
120
+ - Any sources that could not be fetched.
121
+ - Lint and review pass/fail status.
package/README.md CHANGED
@@ -62,13 +62,14 @@ Different teams at different organizational levels make decisions that apply to
62
62
 
63
63
  ### Scope types and `follows`
64
64
 
65
- Every scope declares a `scope-type` in its `index.md` frontmatter. The five built-in types are `core`, `reference`, `platform`, `standard`, and `_local`, each with different governance rules. Custom types (e.g. `business-area`) can be introduced by adding a `{type}-scope-type` policy to any `core`-type scope.
65
+ Every scope declares a `scope-type` in its `index.md` frontmatter. The six built-in types are `core`, `reference`, `platform`, `compiled`, `standard`, and `_local`, each with different governance rules. Custom types (e.g. `business-area`) can be introduced by adding a `{type}-scope-type` policy to any `core`-type scope.
66
66
 
67
67
  | Type | Purpose | Naming pattern | Root index order |
68
68
  |---|---|---|---|
69
69
  | `core` | Meta-governance: authoring standards, scope-type definitions, process guidance. No consumable policies. | `{name}-core` (e.g. `myorg-core`) | 1st |
70
70
  | `reference` | Standards, regulations, or reference models to be adopted or adapted (e.g. PCI-DSS, ISO 27001, reference architectures). Not a live service. | `{domain}-ref-{name}` (e.g. `security-ref-baseline`) | 2nd |
71
71
  | `platform` | Existing live services or infrastructure teams can consume directly (e.g. cloud platform, shared data service). | `{domain}-plat-{name}` (e.g. `cloud-plat-infra`) | 3rd |
72
+ | `compiled` | Policies compiled directly from external sources (web pages, git repos, local folders). All content must trace to source; no invented content. Combinable with other types. | Free-form (no naming requirement) | any, before `_local` |
72
73
  | `standard` | Business areas, products, teams, or any general-purpose scope. Default type when nothing else fits. | Any valid scope name (e.g. `checkout`, `mobile-app`) | 4th |
73
74
  | `_local` | Workspace-local overrides. Reserved exclusively for the `_local` scope. Never distributed. | `_local` (reserved) | last |
74
75
 
@@ -78,6 +79,18 @@ Every scope declares a `scope-type` in its `index.md` frontmatter. The five buil
78
79
 
79
80
  **`platform` scopes** document what already exists and is ready to use. A scope is of type `platform` when its content describes a live operational capability — what is available, how to access it, and what constraints apply.
80
81
 
82
+ **`compiled` scopes** hold policies compiled directly from external authoritative sources. All content must trace back to the source; no invented content is allowed. A `compiled` scope requires a local meta-policy with source URLs and compilation configuration, and uses the `009-compile-scope` skill to fetch, compile, and keep content up to date. The `compiled` type may be combined with another type (e.g. `scope-type: compiled, reference`) when the combined type's governance also applies.
83
+
84
+ ```yaml
85
+ # .xdrs/owasp-top10/adrs/principles/001-core.md (compilation meta-policy)
86
+ ## Sources
87
+ - [web] https://owasp.org/Top10/
88
+ - [git] https://github.com/OWASP/Top10.git
89
+
90
+ ## Selectors
91
+ Include A01–A10 risk entries only.
92
+ ```
93
+
81
94
  **`standard` scopes** are the default. Use them for team, product, or business-area scopes that do not fit the other types.
82
95
 
83
96
  **`_local` scopes** hold workspace-specific content such as local tooling decisions, transition overrides, or temporary decisions. This content is never packaged or distributed.
@@ -94,7 +107,7 @@ description: Checkout team decisions
94
107
  ---
95
108
  ```
96
109
 
97
- The recommended ordering in `.xdrs/index.md` is: `core → reference → platform → standard → _local`. Scopes listed later override earlier ones on the same topic.
110
+ The recommended ordering in `.xdrs/index.md` is: `core → reference → platform → compiled/standard → _local`. Scopes listed later override earlier ones on the same topic.
98
111
 
99
112
  Custom scope types can be introduced by adding a `{type}-scope-type` policy to any `core`-type scope. The `checkout` scope in [examples/typed-scope](examples/typed-scope) uses a custom `business-area` type defined in `ecomm-core`.
100
113
 
package/lib/lint.js CHANGED
@@ -27,7 +27,7 @@ const SKILL_PACKAGE_OPTIONAL_DIRS = new Set(['scripts', 'references', RESOURCE_D
27
27
 
28
28
  const POLICY_ALLOWED_FRONTMATTER_KEYS = new Set(['name', 'description', 'apply-to', 'valid-from', 'license', 'metadata', 'freeze-reference']);
29
29
  const SKILL_ALLOWED_FRONTMATTER_KEYS = new Set(['name', 'description', 'license', 'metadata', 'compatibility', 'allowed-tools']);
30
- const SCOPE_INDEX_ALLOWED_FRONTMATTER_KEYS = new Set(['scope-type', 'name', 'description', 'apply-to', 'valid-from', 'license', 'metadata', 'follows', 'related-scopes']);
30
+ const SCOPE_INDEX_ALLOWED_FRONTMATTER_KEYS = new Set(['scope-type', 'name', 'description', 'apply-to', 'valid-from', 'license', 'metadata', 'follows', 'extends']);
31
31
  const IMAGE_EXTENSIONS = new Set(['.png', '.jpg', '.jpeg', '.gif', '.svg', '.webp', '.bmp']);
32
32
  const SLIDE_FILE_RE = /^.+-slides(?:-[a-z0-9-]+)?\.md$/;
33
33
  const SLIDE_MAX_NAME_LENGTH = 64;
@@ -220,21 +220,48 @@ function lintWorkspace(targetPath, options = {}) {
220
220
  }
221
221
  }
222
222
 
223
+ // Populated during lintScopeIndexFrontmatter: scopeName -> [extendedScopeNames...]
224
+ const extendsRelationships = new Map();
225
+
223
226
  for (const scopeEntry of scopeEntries) {
224
- lintScopeDirectory(xdrsRoot, scopeEntry.name, errors, warnings, actualTypeIndexes, ignoreExternal, effectiveExternalScopes, knownScopeTypes, readOnlyScopes);
227
+ lintScopeDirectory(xdrsRoot, scopeEntry.name, errors, warnings, actualTypeIndexes, ignoreExternal, effectiveExternalScopes, knownScopeTypes, readOnlyScopes, extendsRelationships);
228
+ }
229
+
230
+ // Detect cycles in extends: chains (DFS)
231
+ const visitedCycle = new Set();
232
+ const inStackCycle = new Set();
233
+ const detectExtendsCycles = (scope, stack) => {
234
+ if (inStackCycle.has(scope)) {
235
+ const cycleStart = stack.indexOf(scope);
236
+ const cycle = stack.slice(cycleStart).concat(scope).join(' -> ');
237
+ errors.push(`Circular extends chain detected: ${cycle} [_core-adr-policy-010.31-extends-no-cycle]`);
238
+ return;
239
+ }
240
+ if (visitedCycle.has(scope)) return;
241
+ visitedCycle.add(scope);
242
+ inStackCycle.add(scope);
243
+ stack.push(scope);
244
+ for (const target of (extendsRelationships.get(scope) || [])) {
245
+ detectExtendsCycles(target, stack);
246
+ }
247
+ stack.pop();
248
+ inStackCycle.delete(scope);
249
+ };
250
+ for (const scope of extendsRelationships.keys()) {
251
+ detectExtendsCycles(scope, []);
225
252
  }
226
253
 
227
254
  const rootIndexPath = path.join(xdrsRoot, 'index.md');
228
255
  if (!existsFile(rootIndexPath)) {
229
256
  errors.push('Missing required root index: .xdrs/index.md [_core-adr-policy-001]');
230
257
  } else {
231
- lintRootIndex(rootIndexPath, xdrsRoot, actualTypeIndexes, errors, effectiveExternalScopes);
258
+ lintRootIndex(rootIndexPath, xdrsRoot, actualTypeIndexes, errors, warnings, effectiveExternalScopes, extendsRelationships);
232
259
  }
233
260
 
234
261
  return { xdrsRoot, errors, warnings, readOnlyScopes };
235
262
  }
236
263
 
237
- function lintRootIndex(rootIndexPath, xdrsRoot, actualTypeIndexes, errors, externalScopes = new Set()) {
264
+ function lintRootIndex(rootIndexPath, xdrsRoot, actualTypeIndexes, errors, warnings = [], externalScopes = new Set(), extendsRelationships = new Map()) {
238
265
  const content = fs.readFileSync(rootIndexPath, 'utf8');
239
266
  const repoRoot = path.dirname(xdrsRoot);
240
267
 
@@ -263,6 +290,12 @@ function lintRootIndex(rootIndexPath, xdrsRoot, actualTypeIndexes, errors, exter
263
290
  }
264
291
  }
265
292
 
293
+ // Build set of all scopes referenced via any extends: chain
294
+ const extendsReferencedScopes = new Set();
295
+ for (const entries of extendsRelationships.values()) {
296
+ for (const e of entries) extendsReferencedScopes.add(e);
297
+ }
298
+
266
299
  // Collect non-_local scopes that have type indexes and check their scope index is linked
267
300
  const scopesWithTypeIndexes = new Set();
268
301
  for (const indexPath of actualTypeIndexes) {
@@ -275,7 +308,16 @@ function lintRootIndex(rootIndexPath, xdrsRoot, actualTypeIndexes, errors, exter
275
308
  for (const scopeName of scopesWithTypeIndexes) {
276
309
  const scopeIndexPath = normalizePath(path.join(xdrsRoot, scopeName, 'index.md'));
277
310
  if (!linkedSet.has(scopeIndexPath)) {
311
+ // Exempt scopes that are only reachable via extends: chains
312
+ if (extendsReferencedScopes.has(scopeName)) {
313
+ // Scope is extends-only (not in root index) — exempt from mandatory link requirement
314
+ // No warning here: absence from root index is expected and valid for extends-only scopes
315
+ continue;
316
+ }
278
317
  errors.push(`Root index is missing scope index link: ${toDisplayPath(path.join(xdrsRoot, scopeName, 'index.md'))} [_core-adr-policy-001]`);
318
+ } else if (extendsReferencedScopes.has(scopeName)) {
319
+ // Scope is in BOTH root index and extends chain — ambiguous dual precedence
320
+ warnings.push(`Scope "${scopeName}" appears in both the root index and an extends: chain; precedence may be ambiguous for consumers that do not follow extends declarations: ${toDisplayPath(path.join(xdrsRoot, scopeName, 'index.md'))} [_core-adr-policy-010.35-extends-index]`);
279
321
  }
280
322
  }
281
323
  }
@@ -316,7 +358,7 @@ function extractParentScopeType(policyFilePath) {
316
358
  return bodyMatch ? bodyMatch[1] : null;
317
359
  }
318
360
 
319
- function lintScopeIndexFrontmatter(scopeIndexPath, scopeName, errors, xdrsRoot, knownScopeTypes = new Map(), readOnlyScopes = new Set()) {
361
+ function lintScopeIndexFrontmatter(scopeIndexPath, scopeName, errors, xdrsRoot, knownScopeTypes = new Map(), readOnlyScopes = new Set(), extendsRelationships = new Map()) {
320
362
  const content = fs.readFileSync(scopeIndexPath, 'utf8');
321
363
  const fmMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---(?:\r?\n|$)/);
322
364
  if (!fmMatch) {
@@ -528,24 +570,27 @@ function lintScopeIndexFrontmatter(scopeIndexPath, scopeName, errors, xdrsRoot,
528
570
  }
529
571
  }
530
572
 
531
- const relatedScopesLineMatch = block.match(/^related-scopes:[ \t]*(.*)/m);
532
- if (relatedScopesLineMatch !== null) {
533
- const relatedScopesValue = relatedScopesLineMatch[1].trim();
573
+ // Parse extends: field
574
+ let extendsEntries = [];
575
+ const extendsLineMatch = block.match(/^extends:[ \t]*(.*)/m);
576
+ if (extendsLineMatch !== null) {
577
+ const extendsValue = extendsLineMatch[1].trim();
534
578
  const scopeNamePattern = /^[a-zA-Z_][a-zA-Z0-9_-]*$/;
535
- if (relatedScopesValue) {
536
- if (!scopeNamePattern.test(relatedScopesValue)) {
537
- errors.push(`Scope index frontmatter related-scopes must be a scope name or list of scope names: ${toDisplayPath(scopeIndexPath)} [_core-adr-policy-001]`);
579
+ const commaSeparatedPattern = /^[a-zA-Z_][a-zA-Z0-9_-]*(?:\s*,\s*[a-zA-Z_][a-zA-Z0-9_-]*)+$/;
580
+ if (extendsValue) {
581
+ if (!scopeNamePattern.test(extendsValue) && !commaSeparatedPattern.test(extendsValue)) {
582
+ errors.push(`Scope index frontmatter extends must be a scope name or list of scope names: ${toDisplayPath(scopeIndexPath)} [_core-adr-policy-010.27-extends-declaration]`);
538
583
  }
539
584
  } else {
540
- const relatedScopesStart = block.indexOf(relatedScopesLineMatch[0]) + relatedScopesLineMatch[0].length;
585
+ const extendsStart = block.indexOf(extendsLineMatch[0]) + extendsLineMatch[0].length;
541
586
  const listItems = [];
542
587
  let hasInvalidEntry = false;
543
- for (const line of block.slice(relatedScopesStart).split('\n')) {
588
+ for (const line of block.slice(extendsStart).split('\n')) {
544
589
  const itemMatch = line.match(/^\s+-\s*(\S.*)?$/);
545
590
  if (itemMatch) {
546
591
  const item = (itemMatch[1] || '').trim();
547
592
  if (!item || !scopeNamePattern.test(item)) {
548
- errors.push(`Scope index frontmatter related-scopes entries must be non-empty scope names: ${toDisplayPath(scopeIndexPath)} [_core-adr-policy-001]`);
593
+ errors.push(`Scope index frontmatter extends entries must be non-empty scope names: ${toDisplayPath(scopeIndexPath)} [_core-adr-policy-010.27-extends-declaration]`);
549
594
  hasInvalidEntry = true;
550
595
  break;
551
596
  }
@@ -555,45 +600,63 @@ function lintScopeIndexFrontmatter(scopeIndexPath, scopeName, errors, xdrsRoot,
555
600
  }
556
601
  }
557
602
  if (!hasInvalidEntry && listItems.length === 0) {
558
- errors.push(`Scope index frontmatter related-scopes must be a scope name or list of scope names: ${toDisplayPath(scopeIndexPath)} [_core-adr-policy-001]`);
603
+ errors.push(`Scope index frontmatter extends must be a scope name or list of scope names: ${toDisplayPath(scopeIndexPath)} [_core-adr-policy-010.27-extends-declaration]`);
559
604
  }
560
605
  }
561
- if (xdrsRoot) {
562
- const entries = relatedScopesValue ? [relatedScopesValue] : (() => {
606
+ const rawEntries = extendsValue
607
+ ? (commaSeparatedPattern.test(extendsValue)
608
+ ? extendsValue.split(',').map((s) => s.trim())
609
+ : (scopeNamePattern.test(extendsValue) ? [extendsValue] : []))
610
+ : (() => {
563
611
  const items = [];
564
- const relatedScopesStart = block.indexOf(relatedScopesLineMatch[0]) + relatedScopesLineMatch[0].length;
565
- for (const line of block.slice(relatedScopesStart).split('\n')) {
612
+ const extendsStart = block.indexOf(extendsLineMatch[0]) + extendsLineMatch[0].length;
613
+ for (const line of block.slice(extendsStart).split('\n')) {
566
614
  const itemMatch = line.match(/^\s+-\s*(\S.*)?$/);
567
615
  if (itemMatch && itemMatch[1]) items.push(itemMatch[1].trim());
568
616
  else if (line.trim() && !/^\s/.test(line)) break;
569
617
  }
570
618
  return items;
571
619
  })();
572
- const seenRelated = new Set();
573
- for (const entry of entries) {
574
- if (seenRelated.has(entry)) {
575
- errors.push(`Scope index frontmatter related-scopes has duplicate entry "${entry}": ${toDisplayPath(scopeIndexPath)} [_core-adr-policy-001]`);
620
+ const seenExtends = new Set();
621
+ for (const entry of rawEntries) {
622
+ if (seenExtends.has(entry)) {
623
+ errors.push(`Scope index frontmatter extends has duplicate entry "${entry}": ${toDisplayPath(scopeIndexPath)} [_core-adr-policy-010.27-extends-declaration]`);
624
+ continue;
625
+ }
626
+ seenExtends.add(entry);
627
+ if (!scopeNamePattern.test(entry)) continue;
628
+ if (entry === scopeName) {
629
+ errors.push(`Scope index frontmatter extends must not reference the scope itself: ${toDisplayPath(scopeIndexPath)} [_core-adr-policy-010.30-extends-no-self]`);
630
+ continue;
631
+ }
632
+ if (entry === '_local' || entry === '_core') {
633
+ errors.push(`Scope index frontmatter extends must not reference reserved scope "${entry}": ${toDisplayPath(scopeIndexPath)} [_core-adr-policy-010.29-extends-reserved-scopes]`);
634
+ continue;
635
+ }
636
+ extendsEntries.push(entry);
637
+ }
638
+ if (xdrsRoot) {
639
+ const seenValid = new Set();
640
+ for (const entry of extendsEntries) {
641
+ if (seenValid.has(entry)) continue;
642
+ seenValid.add(entry);
643
+ if (followsEntries.includes(entry)) {
644
+ errors.push(`Scope index frontmatter extends must not repeat scope "${entry}" already declared in follows: ${toDisplayPath(scopeIndexPath)} [_core-adr-policy-010.28-extends-disjoint]`);
645
+ continue;
576
646
  }
577
- seenRelated.add(entry);
578
- if (/^[a-zA-Z_][a-zA-Z0-9_-]*$/.test(entry)) {
579
- if (entry === scopeName) {
580
- errors.push(`Scope index frontmatter related-scopes must not reference the scope itself: ${toDisplayPath(scopeIndexPath)} [_core-adr-policy-001]`);
581
- continue;
582
- }
583
- if (followsEntries.includes(entry)) {
584
- errors.push(`Scope index frontmatter related-scopes must not repeat scope "${entry}" already declared in follows: ${toDisplayPath(scopeIndexPath)} [_core-adr-policy-001]`);
585
- continue;
586
- }
587
- if (!existsFile(path.join(xdrsRoot, entry, 'index.md'))) {
588
- errors.push(`Scope index frontmatter related-scopes references scope "${entry}" which does not exist in the workspace: ${toDisplayPath(scopeIndexPath)} [_core-adr-policy-001]`);
589
- }
647
+ if (!existsFile(path.join(xdrsRoot, entry, 'index.md'))) {
648
+ errors.push(`Scope index frontmatter extends references scope "${entry}" which does not exist in the workspace: ${toDisplayPath(scopeIndexPath)} [_core-adr-policy-010.32-extends-mandatory-presence]`);
590
649
  }
591
650
  }
651
+ if (extendsEntries.length > 0) {
652
+ extendsRelationships.set(scopeName, extendsEntries.filter((e) => existsFile(path.join(xdrsRoot, e, 'index.md'))));
653
+ }
592
654
  }
593
655
  }
594
656
  }
595
657
 
596
- function lintScopeDirectory(xdrsRoot, scopeName, errors, warnings, actualTypeIndexes, ignoreExternal, externalScopes, knownScopeTypes = new Map(), readOnlyScopes = new Set()) {
658
+
659
+ function lintScopeDirectory(xdrsRoot, scopeName, errors, warnings, actualTypeIndexes, ignoreExternal, externalScopes, knownScopeTypes = new Map(), readOnlyScopes = new Set(), extendsRelationships = new Map()) {
597
660
  const scopePath = path.join(xdrsRoot, scopeName);
598
661
 
599
662
  if (ignoreExternal && externalScopes.has(scopeName)) {
@@ -653,7 +716,7 @@ function lintScopeDirectory(xdrsRoot, scopeName, errors, warnings, actualTypeInd
653
716
  errors.push(`Missing required scope index: ${toDisplayPath(scopeIndexPath)} [_core-adr-policy-001]`);
654
717
  } else {
655
718
  lintScopeIndex(scopeIndexPath, xdrsRoot, scopeName, typeIndexesInScope, errors, externalScopes);
656
- lintScopeIndexFrontmatter(scopeIndexPath, scopeName, errors, xdrsRoot, knownScopeTypes, readOnlyScopes);
719
+ lintScopeIndexFrontmatter(scopeIndexPath, scopeName, errors, xdrsRoot, knownScopeTypes, readOnlyScopes, extendsRelationships);
657
720
  }
658
721
  lintLocalMetaPolicies(xdrsRoot, scopeName, errors);
659
722
  }