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.
- package/.xdrs/_core/adrs/index.md +2 -0
- package/.xdrs/_core/adrs/principles/001-xdrs-standards.md +4 -2
- package/.xdrs/_core/adrs/principles/010-scope-governance.md +64 -16
- package/.xdrs/_core/adrs/principles/019-compiled-scope-type.md +117 -0
- package/.xdrs/_core/adrs/principles/skills/.assets/lint-verification.md +12 -0
- package/.xdrs/_core/adrs/principles/skills/.assets/meta-policy-compliance.md +12 -0
- package/.xdrs/_core/adrs/principles/skills/.assets/prerequisites-gate.md +19 -0
- package/.xdrs/_core/adrs/principles/skills/.assets/scope-placement.md +47 -0
- package/.xdrs/_core/adrs/principles/skills/.assets/scope-selection.md +25 -0
- package/.xdrs/_core/adrs/principles/skills/001-review/SKILL.md +7 -1
- package/.xdrs/_core/adrs/principles/skills/002-write-policy/SKILL.md +7 -23
- package/.xdrs/_core/adrs/principles/skills/003-write-skill/SKILL.md +7 -18
- package/.xdrs/_core/adrs/principles/skills/004-write-article/SKILL.md +5 -15
- package/.xdrs/_core/adrs/principles/skills/005-write-research/SKILL.md +6 -17
- package/.xdrs/_core/adrs/principles/skills/006-write-plan/SKILL.md +6 -17
- package/.xdrs/_core/adrs/principles/skills/007-write-presentation/SKILL.md +2 -13
- package/.xdrs/_core/adrs/principles/skills/008-write-xdrs-doc/SKILL.md +1 -8
- package/.xdrs/_core/adrs/principles/skills/009-compile-scope/SKILL.md +121 -0
- package/README.md +15 -2
- package/lib/lint.js +101 -38
- package/lib/lint.test.js +156 -41
- package/package.json +1 -1
|
@@ -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
|
-
**
|
|
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
|
-
|
|
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** —
|
|
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**:
|
|
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
|
-
|
|
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
|
-
|
|
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** —
|
|
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**:
|
|
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
|
-
|
|
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
|
-
|
|
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** —
|
|
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**:
|
|
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
|
-
|
|
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.
|
|
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
|
-
|
|
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,
|
|
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
|
|
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', '
|
|
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
|
-
|
|
532
|
-
|
|
533
|
-
|
|
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
|
-
|
|
536
|
-
|
|
537
|
-
|
|
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
|
|
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(
|
|
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
|
|
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
|
|
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
|
-
|
|
562
|
-
|
|
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
|
|
565
|
-
for (const line of block.slice(
|
|
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
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
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
|
-
|
|
578
|
-
|
|
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
|
-
|
|
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
|
}
|