xdrs-core 0.53.1 → 0.54.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.
@@ -50,7 +50,7 @@ ADR and EDR share the same six technical subject names (see [`_core-adr-policy-0
50
50
  `[xdrs-root]/[scope]/[type]/[subject]/[number]-[short-title].md`
51
51
  where `[xdrs-root]` is the XDRS root folder (default: `.xdrs/`).
52
52
  - MUST ignore symlinks paths. MUST NOT create or update documents inside symlinked folders.
53
- - **Files listed in `.filedist.lock` are external XDRs.** A file whose path appears in the workspace root `.filedist.lock` file was distributed from an external source repository. It MUST NOT be modified locally. To change it, submit the change to the source repository and re-extract the updated package. The `.filedist.lock` format is one entry per line: `<relative-path>|<package>|<version>`. A scope is considered external when any of its files appear in `.filedist.lock`. The `.filedist.lock` file can also be used to detect which files changed when bumping an external scope to a newer version: compare the version field in `.filedist.lock` entries before and after the upgrade and diff the affected paths to understand what decisions were added, updated, or removed.
53
+ - **Files listed in `.filedist.lock` are external XDRs.** A file whose path appears in the workspace root `.filedist.lock` file was distributed from an external source repository. It MUST NOT be modified locally. To change it, submit the change to the source repository and re-extract the updated package. The `.filedist.lock` lists managed files under `files:` per output directory, one `<path>|<package>|<version>|...` entry each. A scope is considered external when any of its files appear in `.filedist.lock`. The `.filedist.lock` file can also be used to detect which files changed when bumping an external scope to a newer version: compare the version field in `.filedist.lock` entries before and after the upgrade and diff the affected paths to understand what decisions were added, updated, or removed.
54
54
  - Optional supporting artifacts under the same subject:
55
55
  - `[xdrs-root]/[scope]/[type]/[subject]/researches/[number]-[short-title].md`
56
56
  - `[xdrs-root]/[scope]/[type]/[subject]/skills/[skill-name]/SKILL.md`
@@ -78,7 +78,9 @@ Policy documents are the authoritative source of truth for their scope, type, an
78
78
  - Conflict handling applies to Policy documents:
79
79
  - For cross-scope and cross-scope-type overrides, document the decision conflict in the Policy `## Conflicts` section of the Policy that overrides another scope or scope-type policy. Local meta-policies (`NNN-core.md` and companion files) and scope-type definition policies MAY include a `## Conflicts` section for this purpose. See `_core-adr-policy-010` rule 26-conflict-declarations for the required structure.
80
80
  - **Within-scope conflicts:** Policies within the same type+scope MUST NOT conflict. If two Policies appear to conflict, one SHOULD be updated, removed, or the conflict resolved through a new Policy.
81
- - When research exists for a decision, the Policy SHOULD mention the related research documents after the `## Considered Options` list.
81
+ - Policies MUST NOT contain a `## Considered Options` section.
82
+ - When a Research document backs the decision, the Policy SHOULD list it in a `## Related Researches` section. Each entry MUST be a single list item with a link to the research, a ` - ` separator, and a summary of fewer than 20 words (see the template below).
83
+ - Options that MUST NOT be used, with or without a backing Research, MUST be stated as ordinary rules in the Policy (for example in `### Details`), like any other rule.
82
84
  - MUST NOT use emojis in contents.
83
85
  - File names MUST be lowercase.
84
86
  - For diagram and asset rules in Policy documents, see [`_core-adr-policy-020`](020-media-and-asset-standards.md). Rule `02-policy-diagram-restrictions` below summarises the overall stance; the detailed rules are in policy 020.
@@ -129,8 +131,8 @@ Question: In the end, state explicitly the question that needs to be answered. E
129
131
 
130
132
  ## Decision Outcome
131
133
 
132
- **[Chosen Option Title]**
133
- [Very short description of what is the decision, aligned with the titles on the Considered Options section]
134
+ **[Decision Title]**
135
+ [Very short description of what is the decision]
134
136
 
135
137
  [Short description of implementation details for the chosen path]
136
138
 
@@ -138,11 +140,11 @@ Question: In the end, state explicitly the question that needs to be answered. E
138
140
 
139
141
  [Optional section with implementation specifics, applicability boundaries, rules, concise examples, or do/don't guidance. This is the answer to the question in the "Context and Problem Statement". (<1300 words)]
140
142
 
141
- ## Considered Options
142
- [this section is present ONLY if the user explicitely indicated that there were multiple options to choose from while making this decision or have a backing research document]
143
+ ## Related Researches
143
144
 
144
- [Related research, if any]
145
- - [Research document title](researches/001-example.md) - Brief description of what it informed
145
+ [Optional section, present ONLY if a Research document backs this decision]
146
+
147
+ - [Research document title](researches/001-example.md) - Brief summary of what it informed (<20 words)
146
148
 
147
149
  ## Conflicts
148
150
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: _core-adr-policy-003-skill-standards
3
- description: Defines skill package standards including structure, SKILL.md format, and co-location with XDRS packages. Use when creating or reviewing skills.
3
+ description: Defines skill package standards including structure, SKILL.md format, co-location with XDRS packages, and default placement of runtime work files. Use when creating or reviewing skills.
4
4
  apply-to: All skill packages
5
5
  valid-from: 2025-01-01
6
6
  ---
@@ -124,7 +124,7 @@ Brief description of the skill goal.
124
124
  - Generated files or chat-delivered results, one bullet per item, or "None" (e.g., `Release notes (<300 words)`).
125
125
 
126
126
  #### Changes
127
- - External system mutations that are part of the skill's main objective, one bullet per item, or "None".
127
+ - External system mutations or persistent workspace files changed by the main objective, one bullet per item, or "None". Never list work files under `.tmp/`.
128
128
 
129
129
  ### Halt Conditions
130
130
  - Specific triggers that make the skill stop before completing, one bullet per item, or "None".
@@ -168,7 +168,7 @@ Rules:
168
168
  - `### Inputs` MUST contain `#### Required` (the bare minimum needed to invoke the skill) then `#### Optional` (helpful extra context), in that order.
169
169
  - `## Instructions` is REQUIRED. It SHOULD state one clearly recommended approach first when multiple are viable, noting alternatives briefly afterward, and SHOULD include verification steps or acceptance criteria at the end of the task or major phases.
170
170
  - `## Examples` SHOULD include 2-3 example prompts a user could give as input, alongside a short description of what to expect during execution and as output for each.
171
- - `### Outputs` covers only end-objective results, not intermediary items, and MUST contain `#### Contents` (generated files or chat-delivered results) then `#### Changes` (external system mutations that are part of the main objective), in that order.
171
+ - `### Outputs` covers only end-objective results, not intermediary items, and MUST contain `#### Contents` (generated files or chat-delivered results) then `#### Changes` (external system mutations or persistent workspace files changed by the main objective, never `.tmp/` work files), in that order. Chat-only skills MUST declare "None".
172
172
  - `### Halt Conditions` MUST list this skill's specific stop-before-completing triggers, grounded in at least missing required input, dubious/ambiguous input, and insufficient agent confidence — distinct from `## Edge Cases` (activation/boundary conditions) and `## Anti-Patterns` (execution mistakes). A partial or empty result the skill still finishes and returns belongs in `### Outputs`, not `### Halt Conditions`.
173
173
  - `### User Interaction` is OPTIONAL: documents human-in-the-loop (HITL) exchanges during execution — a clarifying question or an approval gate — distinct from `### Halt Conditions` (which stop execution rather than pause-and-resume it) and `### Runtime Requirements` (static prerequisites, not an interactive exchange); omit the section entirely when the skill has none.
174
174
  - `### Runtime Requirements` is OPTIONAL: free-form bullets for tooling, network, or environment prerequisites beyond the LLM itself; omit the section entirely when none apply.
@@ -190,6 +190,17 @@ When a skill includes `scripts/`:
190
190
  - SHOULD support both human-readable and machine-readable (JSON) output when producing analysis or report output.
191
191
  - SHOULD stay single-purpose.
192
192
 
193
+ **Work files**
194
+
195
+ Files a skill creates while running MUST follow this default unless the skill or user sets another location:
196
+ - `.tmp/[skill-name]-[YYYYMMDDHHMMSS]/` at the workspace root (local start time), created lazily, suffixed `-2`, `-3`... if taken, holds final outputs.
197
+ - Its `.work/` holds intermediate files (caches, staging); `.work/scripts/` holds ad-hoc scripts generated at runtime.
198
+ - Keep it after the run. Workspaces SHOULD gitignore `.tmp/`; skills MUST NOT edit `.gitignore` for it.
199
+ - MUST NOT write secrets there; use environment or harness, or the OS temp dir (deleted after use) when a file is unavoidable.
200
+ - On a read-only workspace, use the same layout under the OS temp dir.
201
+ - When the dir was created, even on halt or failure, end the final chat message with `results-path: [dir]/`.
202
+ - Skills that create files MUST restate the parts of this layout they use in their own `## Instructions`, to run standalone.
203
+
193
204
  **Quality gate**
194
205
 
195
206
  Before creating a skill, confirm it removes real ambiguity or repetitive effort compared to not having it. Skills that only restate a Policy, or that handle a one-off task unlikely to recur, SHOULD NOT be created.
@@ -221,17 +232,6 @@ Use the [skills-ref](https://github.com/agentskills/agentskills/tree/main/skills
221
232
  skills-ref validate .xdrs/[scope]/[type]/[subject]/skills/[skill-name]
222
233
  ```
223
234
 
224
- ## Considered Options
225
-
226
- * (REJECTED) **Top-level `skills/` directory separate from XDRS** - Decouples skills from the decisions that govern them.
227
- * Reason: Breaks the natural association between a decision (Policy) and the skill that implements it; makes navigation harder.
228
- * (CHOSEN) **agentskills-compatible packages co-located with XDRS** - Standardized format with scoped discovery and clear ownership.
229
- * Reason: Reuses proven agentskills tooling, aligns with the existing XDRS scope/subject hierarchy, and keeps skills close to the decisions they implement.
230
- * (REJECTED) **Fully self-contained skills, no shared assets** - Every skill package repeats any instruction shared with another skill.
231
- * Reason: Duplicates maintenance burden across skills and drifts out of sync over time.
232
- * (CHOSEN) **Shared `.assets/` modules plus optional bundling** - Keep shared instruction modules DRY in-repo, and offer an optional per-skill bundling mechanism for standalone distribution.
233
- * Reason: Preserves DRY authoring for the common case while still allowing a single skill to be distributed as a self-contained artifact when needed ([`_core-adr-policy-021`](021-skill-bundling.md)).
234
-
235
235
  ## References
236
236
 
237
237
  - [agentskills specification](https://agentskills.io/specification)
@@ -21,6 +21,7 @@ Articles are Markdown documents placed inside a subject folder alongside policie
21
21
 
22
22
  ### Details
23
23
 
24
+ - Articles MUST NOT be kept in a documentation repository separate from the Policies they reference.
24
25
  **Human-first writing**
25
26
 
26
27
  - The primary objective of an article is to make information available and accessible to humans. Good copywriting style, storytelling, clear organization, and clustering of related information are essential. Avoid repetitive content; each sentence SHOULD add new value.
@@ -89,13 +90,6 @@ when referencing an information from those documents.]
89
90
  - [Policy id or Skill name](relative/path/to/file.md) - Brief description of relevance
90
91
  ```
91
92
 
92
- ## Considered Options
93
-
94
- * (REJECTED) **Separate documentation repository** - Removes drift risk but decouples docs from decisions.
95
- * Reason: Increases maintenance burden and makes it easy for articles to go stale relative to the Policies they reference.
96
- * (CHOSEN) **Subject-level articles folder co-located with Policies** - Keeps articles alongside the policies and skills they reference, with `principles` as the fallback for cross-subject articles.
97
- * Reason: Easy to discover, consistent with where skills are placed, and clearly distinct from the policies themselves.
98
-
99
93
  ## References
100
94
 
101
95
  - [_core-adr-policy-001 - XDRS standards](001-xdrs-standards.md)
@@ -21,6 +21,7 @@ Research documents are Markdown files placed inside a subject folder alongside p
21
21
 
22
22
  ### Details
23
23
 
24
+ - Research MUST NOT be written inline inside a Policy; Policies only link to it.
24
25
  - Research is evidence and exploration, not the adopted decision. When a research document and a Policy disagree, the Policy takes precedence.
25
26
  - `Research` is the artifact name. `researches/` is only the folder name used alongside `skills/` and `articles/`.
26
27
  - Research documents MUST live under `researches/` inside the relevant subject folder:
@@ -121,15 +122,6 @@ Prefer tables and bullet lists for simple comparisons; for diagrams see _core-ad
121
122
  - [Another related Policy if this research informed multiple decisions](relative/path.md) - Why it matters
122
123
  ```
123
124
 
124
- ## Considered Options
125
-
126
- - Related research: `001-research-and-decision-lifecycle` (workspace-local research)
127
-
128
- * (REJECTED) **Inline long-form analysis inside the Policy** - Put all research and decision text in one file.
129
- * Reason: Makes Policies too long, mixes evidence with the adopted rule set, and hurts fast retrieval by humans and AI agents.
130
- * (CHOSEN) **IMRAD-based subject-level research beside XDRS** - Keep exploratory material beside the decisions, skills, and articles it informs, using an IMRAD-inspired structure adapted to company work.
131
- * Reason: Preserves lifecycle context, keeps the Policy concise, gives readers a predictable structure, and raises evidence quality without demanding full academic rigor.
132
-
133
125
  ## References
134
126
 
135
127
  - [_core-adr-policy-001 - XDRS standards](001-xdrs-standards.md)
@@ -21,6 +21,7 @@ Initiatives are Markdown documents placed inside a subject folder alongside poli
21
21
 
22
22
  ### Details
23
23
 
24
+ - Initiatives MUST NOT be embedded inside Policies.
24
25
  - Initiatives describe a problem (why), what we will do to solve the problem, and the approach and activities needed to solve it (how).
25
26
  - Initiatives are NOT the source of truth. When an initiative and a Policy disagree, the Policy takes precedence.
26
27
  - Initiatives are ephemeral. They MUST be deleted after full implementation. The lasting outputs of an initiative are actual actions or Decisions, Skills, Articles, Research documents, and other artifacts that result from execution.
@@ -125,13 +126,6 @@ Due date: YYYY-MM-DD
125
126
  - [Related Policy or artifact](relative/path.md) - Brief description of relevance
126
127
  ```
127
128
 
128
- ## Considered Options
129
-
130
- * (REJECTED) **Inline planning in Policies** — Embed planning details inside policies.
131
- * Reason: Initiatives are ephemeral execution documents while Policies are lasting decisions. Mixing them bloats Policies and creates confusion about what to delete after execution.
132
- * (CHOSEN) **Subject-level initiatives folder co-located with Policies** — Keeps initiatives alongside the decisions they implement, with clear lifecycle expectations.
133
- * Reason: Consistent with how skills, articles, and research are organized. The explicit deletion requirement after implementation keeps the document base clean.
134
-
135
129
  ## References
136
130
 
137
131
  - [_core-adr-policy-001 - XDRS standards](001-xdrs-standards.md) - Framework elements: types, scopes, subjects, folder structure
@@ -114,7 +114,7 @@ All access control implementations MUST enforce the principle of least privilege
114
114
 
115
115
  Unless a meta-policy declares `Source storage: temporary`, fetched and selected source content (rule 13) MUST be persisted under `[type]/principles/.assets/sources/[name]/`, sibling to the governing meta-policy, where `[name]` is the source's slug from its `## Sources` bullet. This location is shared across every subject compiled under that meta-policy's type folder; it is not duplicated per subject. Persisted source content MUST be committed to version control and MUST NOT be gitignored.
116
116
 
117
- When `Source storage: temporary` is declared, bulk fetched content MUST instead be written only to the ephemeral compilation working directory and removed at the end of the compilation run.
117
+ When `Source storage: temporary` is declared, bulk fetched content MUST instead be written only to the compilation execution dir's `.work/staging/` (see Work files in `_core-adr-policy-003`) and MUST NOT be persisted under `.assets/sources/`.
118
118
 
119
119
  Regardless of storage mode, a tracking file `.assets/sources/[name]/source.md` MUST always be created and kept up to date, with the following format:
120
120
 
@@ -85,7 +85,7 @@ The framework is easiest to understand as a lifecycle rather than a static folde
85
85
  later support more than one decision record.
86
86
  2. **Decide** — Once a direction is chosen, a Policy captures the final answer in concise,
87
87
  authoritative form. The Policy should make clear when the decision applies, and may link back to
88
- the Research that informed its considered options.
88
+ the Research that informed it.
89
89
  3. **Execute** — If the decision affects daily work, a Skill explains how to apply it in practice.
90
90
  The Skill operationalizes the decision without turning the Policy into a procedure manual.
91
91
  4. **Explain** — When the topic becomes broad or cross-cutting, an Article synthesizes the Policy,
@@ -4,13 +4,13 @@ description: >
4
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
5
  metadata:
6
6
  author: flaviostutz
7
- version: "1.1.0"
8
- updated: 2026-09-24
7
+ version: "1.2.0"
8
+ updated: 2026-09-30
9
9
  ---
10
10
 
11
11
  ## Overview
12
12
 
13
- Performs a full compilation cycle for any scope declaring `scope-type: compiled`: discovers compilation meta-policies across all type folders, checks whether each governed source is already in sync before doing any work, fetches due sources into a temporary staging directory, plans which policies to create, update, or remove, migrates them one at a time with full source traceability, runs lint and review, persists selected source content and updates sync tracking, and documents any inconsistencies found in the source. Works for both initial compilation and re-compilation (updates). By default, fetched source content used to compile policies is persisted under `.assets/sources/[name]/` in the scope (see `_core-adr-policy-019` rules 12–13); this content is never authoritative for decisions (rule 14). Requires `_core-adr-policy-019-compiled-scope-type` to be present in the workspace.
13
+ Performs a full compilation cycle for any scope declaring `scope-type: compiled`: discovers compilation meta-policies across all type folders, checks whether each governed source is already in sync before doing any work, fetches due sources into a staging directory under `.tmp/`, plans which policies to create, update, or remove, migrates them one at a time with full source traceability, runs lint and review, persists selected source content and updates sync tracking, and documents any inconsistencies found in the source. Works for both initial compilation and re-compilation (updates). By default, fetched source content used to compile policies is persisted under `.assets/sources/[name]/` in the scope (see `_core-adr-policy-019` rules 12–13); this content is never authoritative for decisions (rule 14). Requires `_core-adr-policy-019-compiled-scope-type` to be present in the workspace.
14
14
 
15
15
  ### Inputs
16
16
 
@@ -76,11 +76,11 @@ Keep every question and intermediate message <100 words and the final summary <1
76
76
 
77
77
  ### Phase 2: Fetch Sources
78
78
 
79
- 1. Create a temporary directory `.tmp/compilation-[YYYYMMDDHHMMSS]/staging/` in the workspace root.
79
+ 1. Create the execution dir `.tmp/compile-scope-[YYYYMMDDHHMMSS]/` at the workspace root (local start time; append `-2`, `-3`... if it exists) with a `.work/staging/` folder. Put any other intermediate file in `.work/` and any ad-hoc script you generate in `.work/scripts/`; never write secrets there (pass them via environment, or use the OS temp dir and delete the file after use). If the workspace is read-only, use the same layout under the OS temp dir. Keep the dir after the run.
80
80
  2. For each due source (from Phase 1), fetch in the following preference order (first available wins; if all equivalent URLs are listed, try each in order):
81
- - **Git URL** (`- [git] ...`): run `git clone --depth=1 [url] .tmp/compilation-[ts]/staging/[name]/`. If `git` is not available or the clone fails, fall back to the next option.
82
- - **Local folder** (`- [local] ...`): copy the folder contents to `.tmp/compilation-[ts]/staging/[name]/`.
83
- - **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]/staging/[name]/`.
81
+ - **Git URL** (`- [git] ...`): run `git clone --depth=1 [url] .tmp/compile-scope-[ts]/.work/staging/[name]/`. If `git` is not available or the clone fails, fall back to the next option.
82
+ - **Local folder** (`- [local] ...`): copy the folder contents to `.tmp/compile-scope-[ts]/.work/staging/[name]/`.
83
+ - **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/compile-scope-[ts]/.work/staging/[name]/`.
84
84
  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.
85
85
  4. Immediately after a source is fetched successfully, create `.assets/sources/[name]/source.md` if absent and update its `last-fetch-timestamp` to the current time — this happens regardless of the `Source storage` setting, since the tracking file always persists per `_core-adr-policy-019` rule 12.
86
86
  5. Convert non-markdown documents found in the fetched source directories. Use only the format converters actually needed:
@@ -170,15 +170,15 @@ After writing each policy:
170
170
  4. For every source processed in this run, update `.assets/sources/[name]/source.md`'s `last-compilation-timestamp` to the current time — this step only runs after Phase 6 (Lint) and Phase 7 (Review) both pass for the policies depending on that source.
171
171
  5. If a source's `Source storage` setting changed since its last run, reconcile per `_core-adr-policy-019` rule 12: switching to `temporary` removes previously persisted bulk content (keeping `source.md`); switching away from `temporary` begins persisting bulk content from this run onward.
172
172
 
173
- ### Phase 10: Cleanup
173
+ ### Phase 10: Report
174
174
 
175
- 1. Remove the temporary staging directory `.tmp/compilation-[ts]/` created in Phase 2. Never remove or modify persisted content under `.assets/sources/` — that is governed solely by Phase 9.
176
- 2. Report a compilation summary:
175
+ 1. Report a compilation summary:
177
176
  - Policies created, updated, removed.
178
177
  - Inconsistencies documented (with file path).
179
178
  - Any sources that could not be fetched.
180
179
  - Lint and review pass/fail status.
181
180
  - Sources persisted, kept temporary, or skipped as already in sync.
181
+ 2. Whenever the execution dir was created, including when halting in an earlier phase, end the final message with `results-path: .tmp/compile-scope-[ts]/` (the actual dir).
182
182
 
183
183
  ## Anti-Patterns
184
184
 
@@ -190,10 +190,6 @@ After writing each policy:
190
190
  **Why it happens:** Momentum from prior compilations makes the plan feel obviously correct.
191
191
  **Instead:** Always present the TODO list and wait for explicit confirmation before Phase 4.
192
192
 
193
- - **Mistake:** Leaving the temporary `.tmp/compilation-[ts]/` directory behind after a run.
194
- **Why it happens:** Phase 10 cleanup is easy to forget once the summary is reported.
195
- **Instead:** Always remove the temporary directory as the first Phase 10 step, even on partial failures.
196
-
197
193
  - **Mistake:** Persisting every fetched file under `.assets/sources/[name]/`, including content excluded by selectors or no longer used by any compiled policy.
198
194
  **Why it happens:** It feels safer to keep everything "just in case" rather than compute what is actually still used.
199
195
  **Instead:** Persist only Selector-matched content actually referenced by a compiled policy's `## Source` section, per `_core-adr-policy-019` rule 13; remove the rest during Phase 9.
@@ -6,8 +6,8 @@ description: >
6
6
  Also activate when you identify a need to check compliance with Policies during implementation.
7
7
  metadata:
8
8
  author: flaviostutz
9
- version: "1.1.0"
10
- updated: 2026-09-24
9
+ version: "1.2.0"
10
+ updated: 2026-09-30
11
11
  ---
12
12
 
13
13
  ## Overview
@@ -81,12 +81,12 @@ Keep every question and intermediate message <100 words.
81
81
  ### Phase 3: Diagram Validation
82
82
 
83
83
  1. For every file in scope, scan for fenced ` ```mermaid ` code blocks.
84
- 2. For each diagram found, extract the content to a temporary `.mmd` file and run:
84
+ 2. For each diagram found, extract the content to a `.mmd` file in `.tmp/review-[YYYYMMDDHHMMSS]/.work/` at the workspace root (local start time; create it only when the first diagram is found, append `-2`, `-3`... if it exists, and use the same layout under the OS temp dir if the workspace is read-only) and run:
85
85
  ```bash
86
- npx -y @mermaid-js/mermaid-cli -i <tempfile>.mmd --quiet 2>&1
86
+ npx -y @mermaid-js/mermaid-cli -i <file>.mmd --quiet 2>&1
87
87
  ```
88
88
  3. For each diagram that fails validation, report as ERROR: include the file path, approximate location (heading or surrounding text), and the error message from `mmdc`.
89
- 4. Delete all temporary files created in this step.
89
+ 4. Keep the extracted files after the run; if this step created the dir, fill the `results-path` line of the report template.
90
90
 
91
91
  ### Phase 4: Policy Review
92
92
 
@@ -134,6 +134,8 @@ Scope: [scope identifier]
134
134
  - Errors: [count]
135
135
  - Warnings: [count]
136
136
  - Outcome: [PASS|FAIL]
137
+
138
+ results-path: [.tmp/review-[ts]/ actual dir; include this line only when Phase 3 created it]
137
139
  ```
138
140
 
139
141
  ### Constraints
@@ -5,8 +5,8 @@ description: >
5
5
  Activate this skill when the user asks to create, add, or write a new Policy (ADR, BDR, or EDR).
6
6
  metadata:
7
7
  author: flaviostutz
8
- version: "1.1.0"
9
- updated: 2026-09-24
8
+ version: "1.2.0"
9
+ updated: 2026-09-30
10
10
  ---
11
11
 
12
12
  ## Overview
@@ -156,7 +156,7 @@ valid-from: [Required. ISO date YYYY-MM-DD. Defaults to today's date when not sp
156
156
  ### Details
157
157
  [Rules, applicability boundaries, concise examples, and optional do/don't guidance — <1300 words]
158
158
 
159
- ## Considered Options (only if the user explicitly indicated multiple options)
159
+ ## Related Researches (only if a backing Research exists; one list item per Research: link + summary <20 words)
160
160
 
161
161
  ## Conflicts (mandatory if conflicts found in Phase 3)
162
162
 
@@ -172,7 +172,7 @@ Mandatory rules to apply while drafting:
172
172
  - Do not duplicate content already in referenced Policies — link instead.
173
173
  - Keep the decision itself authoritative in the Policy. Supporting artifacts may elaborate, but they should not restate the full decision when a short reference is enough.
174
174
  - Make clear when the decision applies and any important exception boundaries.
175
- - Keep exploratory option analysis in a related Research document when it would distract from the final decision text.
175
+ - Keep exploratory option analysis in a related Research document; never add a `## Considered Options` section. State options that must not be used as ordinary rules in the Policy.
176
176
  - For diagrams and non-Markdown assets, follow `_core-adr-policy-020`: prefer plain Markdown tables/lists first, then ASCII art for very simple cases, then Mermaid.js (sequence, state, activity, entity diagrams) for complex ones, then draw.io when Mermaid is insufficient — save as Editable Vector (File → Save As → Editable Vector) and store as `.svg` in the sibling `.assets/` folder.
177
177
  - If the Policy genuinely needs local images or supporting files, store them in `.xdrs/[scope]/[type]/[subject]/.assets/` and link them using a same-folder relative path (e.g., `.assets/image.png`).
178
178
  - Use relative paths for all links; never use absolute paths starting with `/`.
@@ -199,7 +199,7 @@ If any check fails, revise and re-run this phase before proceeding.
199
199
  1. Create the Policy file at `[xdrs-root]/[scope]/[type]/[subject]/[number]-[short-title].md` (default root: `.xdrs/`).
200
200
  2. Add an entry to `[xdrs-root]/[scope]/[type]/index.md` (create the file if it does not exist); keep its description <15 words.
201
201
  3. Add or verify the scope entry in the Policy root `index.md` (exactly one link per scope; when the scope index declares `extends-only: true`, append the tag `` `extends-only` `` right after the link and make sure the legend line is present, see `_core-adr-policy-022`).
202
- 4. If significant research was produced or already exists, link it from the Policy `## Considered Options` section.
202
+ 4. If significant research was produced or already exists, link it from the Policy `## Related Researches` section with a summary under 20 words.
203
203
  5. If concise rules, examples, or do/don't bullets help readers apply the decision correctly, add them inside `### Details` without turning the Policy into a long procedure.
204
204
  6. Evaluate whether the scope index at `[xdrs-root]/[scope]/index.md` should be updated to reflect the new content. If the scope index does not exist, create it following article standards and the scope index rules in `_core-adr-policy-001`.
205
205
 
@@ -6,8 +6,8 @@ description: >
6
6
  Activate this skill when the user asks to create, add, or write a new skill, agent skill, or SKILL.md file.
7
7
  metadata:
8
8
  author: flaviostutz
9
- version: "1.1.0"
10
- updated: 2026-09-24
9
+ version: "1.2.0"
10
+ updated: 2026-09-30
11
11
  ---
12
12
 
13
13
  ## Overview
@@ -172,6 +172,7 @@ Rules:
172
172
  - Mention tools or prerequisites when they are required to complete the task reliably.
173
173
  - Declare a "<N words" cap, or "(uncapped: <reason>)", exactly once for every natural-language content the skill generates, per **Generated content caps** in `_core-adr-policy-003`.
174
174
  - Do not duplicate content from referenced Policies — link instead.
175
+ - If the skill creates files at runtime (outputs, intermediate files, ad-hoc scripts), restate in `## Instructions`, next to the steps writing them, the parts of **Work files** in `_core-adr-policy-003` it uses: `.tmp/[skill-name]-[YYYYMMDDHHMMSS]/`, `.work/`, `.work/scripts/`, and the final `results-path:` line. This restatement is the one exception to the no-duplication rule, so the skill runs standalone.
175
176
  - Do not present the skill itself as policy; mandatory behavior must come from referenced Policies or other policy artifacts.
176
177
  - When the skill depends on Policies, make the activation logic and instructions consistent with the Policy metadata so the skill does not operationalize inactive or out-of-scope decisions.
177
178
  - For diagrams and non-Markdown assets, follow `_core-adr-policy-020`: prefer plain Markdown tables/lists first, then ASCII art for very simple cases, then Mermaid.js (sequence, state, activity, entity diagrams) for complex ones, then draw.io when Mermaid is insufficient — save as Editable Vector (File → Save As → Editable Vector) and store as `.svg` in the sibling `.assets/` folder.
@@ -192,6 +193,7 @@ Before writing files, verify:
192
193
  7. **Required sections**: Does `## Overview` contain nested `### Inputs` (Required/Optional bullets), `### Outputs` (Contents/Changes bullets), and `### Halt Conditions` listing this skill's specific stop triggers — each bullet under 20 words or a single "None"?
193
194
  8. **Meta-policy compliance**: Run the shared module at `.xdrs/_core/adrs/principles/skills/.assets/meta-policy-compliance.md`. Substitute `[DOCUMENT]` with `skill`.
194
195
  9. **Output caps**: Does every natural-language content the skill generates (files, messages, questions, summaries, external posts) have exactly one "<N words" cap or "(uncapped: <reason>)" marker, with code and structured data unmarked?
196
+ 10. **Work files**: If the skill creates files at runtime, do its Instructions restate the default execution dir, `.work/`, `.work/scripts/`, and `results-path:` line from `_core-adr-policy-003`, or an explicit alternative location?
195
197
 
196
198
  If any check fails, revise before continuing.
197
199
 
@@ -5,8 +5,8 @@ description: >
5
5
  Activate this skill when the user asks to create or write any XDRS element.
6
6
  metadata:
7
7
  author: flaviostutz
8
- version: "1.1.0"
9
- updated: 2026-09-24
8
+ version: "1.2.0"
9
+ updated: 2026-09-30
10
10
  ---
11
11
 
12
12
  ## Overview
@@ -81,12 +81,12 @@ Read the full content of the skill file for the inferred type, then follow all i
81
81
  ### Phase 3: Validate Mermaid Diagrams
82
82
 
83
83
  1. After the delegated skill completes, scan all files written in this session for fenced ` ```mermaid ` code blocks.
84
- 2. For each diagram found, extract the content to a temporary `.mmd` file and run:
84
+ 2. For each diagram found, extract the content to a `.mmd` file in `.tmp/write-xdrs-doc-[YYYYMMDDHHMMSS]/.work/` at the workspace root (local start time; create it only when the first diagram is found, append `-2`, `-3`... if it exists, and use the same layout under the OS temp dir if the workspace is read-only) and run:
85
85
  ```bash
86
- npx -y @mermaid-js/mermaid-cli -i <tempfile>.mmd --quiet 2>&1
86
+ npx -y @mermaid-js/mermaid-cli -i <file>.mmd --quiet 2>&1
87
87
  ```
88
88
  3. For each diagram that fails validation, report the error to the user and ask them to correct the diagram before saving.
89
- 4. Delete all temporary files created in this step.
89
+ 4. Keep the extracted files after the run; if this step created the dir, end the final summary with `results-path: .tmp/write-xdrs-doc-[ts]/` (the actual dir), including when halting.
90
90
 
91
91
  ### Constraints
92
92
 
package/README.md CHANGED
@@ -320,7 +320,7 @@ flowchart LR
320
320
  The published package exposes the `xdrs-core` CLI.
321
321
 
322
322
  - `npx -y xdrs-core` (or `install`) installs or updates the managed XDRS files; `npx -y xdrs-core check` fails if managed files drifted from the package. Both are delegated to [filedist](https://github.com/flaviostutz/filedist) (run `--help` for all options).
323
- - `npx -y xdrs-core lint [path]` validates the XDRS tree. Scopes listed in the workspace `.filedist.lock` are treated as external and skipped; use `--all` to include them.
323
+ - `npx -y xdrs-core lint [path]` validates the XDRS tree. Scopes whose files are listed in the `.filedist.lock` written by filedist are treated as external and skipped; use `--all` to include them.
324
324
 
325
325
  The `lint` command reads `.xdrs/**` (or `[path]` directly when it contains an `index.md`) and reports errors citing the violated Policy rule. It checks:
326
326
 
package/lib/lint.js CHANGED
@@ -40,6 +40,7 @@ const SLIDE_FILE_RE = /^.+-slides(?:-[a-z0-9-]+)?\.md$/;
40
40
  const SLIDE_MAX_NAME_LENGTH = 64;
41
41
  const EMOJI_RE = /\p{Extended_Pictographic}/u;
42
42
  const POLICY_MAX_WORDS = 2600;
43
+ const POLICY_RESEARCH_SUMMARY_MAX_WORDS = 20;
43
44
  const ARTICLE_MAX_WORDS = 8000;
44
45
  const RESEARCH_MAX_WORDS = 5000;
45
46
  const SKILL_MAX_WORDS = 7000;
@@ -1135,6 +1136,7 @@ function lintXdrsElementFile(xdrsRoot, scopeName, typeName, subjectName, filePat
1135
1136
  lintCompiledPolicySourceSection(content, filePath, errors);
1136
1137
  }
1137
1138
  lintRequiredSections(content, filePath, XDRS_REQUIRED_SECTIONS, 'Policy', errors);
1139
+ lintPolicyResearchSections(content, filePath, errors);
1138
1140
  lintNoEmojis(content, filePath, 'Policy', errors);
1139
1141
  lintWordCount(content, filePath, 'Policy', POLICY_MAX_WORDS, errors);
1140
1142
  lintDocumentLinks(filePath, xdrsRoot, scopeName, errors, externalScopes);
@@ -1907,6 +1909,45 @@ function lintNoEmojis(content, filePath, docType, errors) {
1907
1909
  }
1908
1910
  }
1909
1911
 
1912
+ /**
1913
+ * Validates Policy research sections per _core-adr-policy-002: "## Considered Options" is forbidden and
1914
+ * "## Related Researches" entries must be a link plus a summary of fewer than POLICY_RESEARCH_SUMMARY_MAX_WORDS words.
1915
+ */
1916
+ function lintPolicyResearchSections(content, filePath, errors) {
1917
+ const lines = content.split(/\r?\n/);
1918
+ const ignoredLines = findIgnoredMarkdownLines(lines);
1919
+ const headingLines = findHeadingLines(lines, ignoredLines);
1920
+
1921
+ for (const index of headingLines) {
1922
+ if (/^##\s+considered\s+options\s*$/i.test(lines[index].trim())) {
1923
+ errors.push(`Policy must not contain a "## Considered Options" section; state options that must not be used as rules and list backing research under "## Related Researches": ${toDisplayPath(filePath)}:${index + 1} [_core-adr-policy-002]`);
1924
+ }
1925
+ }
1926
+
1927
+ const sectionIndex = headingLines.find((index) => /^##\s+related\s+researches\s*$/i.test(lines[index].trim()));
1928
+ if (sectionIndex === undefined) {
1929
+ return;
1930
+ }
1931
+ const nextHeading = headingLines.find((index) => index > sectionIndex);
1932
+ const sectionEnd = nextHeading === undefined ? lines.length : nextHeading;
1933
+
1934
+ let entries = 0;
1935
+ for (let i = sectionIndex + 1; i < sectionEnd; i += 1) {
1936
+ const line = lines[i].trim();
1937
+ if (!line || ignoredLines[i]) continue;
1938
+ entries += 1;
1939
+ const match = line.match(/^[-*]\s+\[[^\]]+\]\([^)\s]+\)\s+[-\u2013\u2014]\s+(.+)$/);
1940
+ if (!match) {
1941
+ errors.push(`Policy "## Related Researches" entries must be list items formatted as "- [Research title](link.md) - Summary": ${toDisplayPath(filePath)}:${i + 1} [_core-adr-policy-002]`);
1942
+ } else if (countWords(match[1]) >= POLICY_RESEARCH_SUMMARY_MAX_WORDS) {
1943
+ errors.push(`Policy "## Related Researches" summary must have fewer than ${POLICY_RESEARCH_SUMMARY_MAX_WORDS} words: ${toDisplayPath(filePath)}:${i + 1} [_core-adr-policy-002]`);
1944
+ }
1945
+ }
1946
+ if (entries === 0) {
1947
+ errors.push(`Policy "## Related Researches" section must not be empty; remove it when no Research backs the decision: ${toDisplayPath(filePath)}:${sectionIndex + 1} [_core-adr-policy-002]`);
1948
+ }
1949
+ }
1950
+
1910
1951
  function lintWordCount(content, filePath, docType, maxWords, errors) {
1911
1952
  const body = stripFrontmatter(content);
1912
1953
  const wordCount = countWords(body);
@@ -2622,21 +2663,64 @@ function existsFile(filePath) {
2622
2663
  }
2623
2664
  }
2624
2665
 
2666
+ // Finds the nearest .filedist.lock starting at startDir and walking up, since filedist may be run from a parent of the XDRS root.
2667
+ function findFiledistLock(startDir) {
2668
+ let dir = startDir;
2669
+ for (;;) {
2670
+ const candidate = path.join(dir, '.filedist.lock');
2671
+ if (existsFile(candidate)) return candidate;
2672
+ const parent = path.dirname(dir);
2673
+ if (parent === dir) return null;
2674
+ dir = parent;
2675
+ }
2676
+ }
2677
+
2678
+ function unquoteYamlScalar(value) {
2679
+ if (value.length >= 2 && value.startsWith("'") && value.endsWith("'")) {
2680
+ return value.slice(1, -1).replace(/''/g, "'");
2681
+ }
2682
+ if (value.length >= 2 && value.startsWith('"') && value.endsWith('"')) {
2683
+ return value.slice(1, -1);
2684
+ }
2685
+ return value;
2686
+ }
2687
+
2688
+ // Returns absolute paths of all files tracked in the nearest .filedist.lock. Supports the YAML lockfile written by filedist
2689
+ // (files: { <outputDir>: ["<path>|<package>|<version>|..."] }, paths relative to <outputDir>) and the legacy format
2690
+ // with one "<path>|<package>|<version>" entry per line (paths relative to the lockfile directory).
2625
2691
  function loadFiledist(repoRoot) {
2626
- const filedistPath = path.join(repoRoot, '.filedist.lock');
2627
- if (!existsFile(filedistPath)) {
2692
+ const filedistPath = findFiledistLock(repoRoot);
2693
+ if (!filedistPath) {
2628
2694
  return new Set();
2629
2695
  }
2696
+ const lockDir = path.dirname(filedistPath);
2630
2697
  const content = fs.readFileSync(filedistPath, 'utf8');
2631
2698
  const paths = new Set();
2699
+ let inFiles = false;
2700
+ let outputDir = '.';
2632
2701
  for (const line of content.split(/\r?\n/)) {
2633
- const trimmed = line.trim();
2634
- if (!trimmed) {
2702
+ if (!line.trim()) {
2703
+ continue;
2704
+ }
2705
+ if (/^files:\s*$/.test(line)) {
2706
+ inFiles = true;
2635
2707
  continue;
2636
2708
  }
2637
- const parts = trimmed.split('|');
2709
+ if (/^\S/.test(line)) {
2710
+ inFiles = false;
2711
+ }
2712
+ if (inFiles) {
2713
+ const dirMatch = line.match(/^ {2}(\S.*?):\s*$/);
2714
+ if (dirMatch) {
2715
+ outputDir = unquoteYamlScalar(dirMatch[1]);
2716
+ continue;
2717
+ }
2718
+ }
2719
+ const entry = unquoteYamlScalar(line.trim().replace(/^-\s+/, ''));
2720
+ const parts = entry.split('|');
2638
2721
  if (parts.length >= 2) {
2639
- paths.add(normalizePath(path.join(repoRoot, parts[0])));
2722
+ const baseDir = inFiles ? path.join(lockDir, outputDir) : lockDir;
2723
+ paths.add(normalizePath(path.join(baseDir, parts[0])));
2640
2724
  }
2641
2725
  }
2642
2726
  return paths;
package/lib/lint.test.js CHANGED
@@ -2276,6 +2276,57 @@ test('does not report a missing extends target of an external scope [022.06]', (
2276
2276
  expect(result.errors.join('\n')).not.toContain('ghost-lib');
2277
2277
  });
2278
2278
 
2279
+ const yamlFiledistLock = (outputDir, entries) => [
2280
+ 'version: 1',
2281
+ 'packages:',
2282
+ ' some-package: 1.0.0',
2283
+ 'files:',
2284
+ ` ${outputDir}:`,
2285
+ ...entries.map((e) => ` - ${e}|some-package|1.0.0|file|abc123|0`),
2286
+ 'checksum: abc',
2287
+ '',
2288
+ ].join('\n');
2289
+
2290
+ const missingFollowsFiles = (prefix) => ({
2291
+ [`${prefix}.xdrs/index.md`]: rootIndex(['[myteam](myteam/index.md)']),
2292
+ [`${prefix}.xdrs/myteam/index.md`]: '---\nscope-type: standard\nname: myteam\ndescription: Team.\napply-to: Test team\nvalid-from: 2026-01-01\nfollows: nonexistent-core\n---\n\n# myteam Scope Overview\n\n[ADRs](adrs/index.md)\n',
2293
+ [`${prefix}.xdrs/myteam/adrs/index.md`]: teamAdrIndex(['- [001-team](principles/001-team.md) - Team decision']),
2294
+ [`${prefix}.xdrs/myteam/adrs/principles/001-team.md`]: teamXdrDocument('Team decision.'),
2295
+ });
2296
+
2297
+ test('detects external scope from YAML .filedist.lock written by filedist [022.06]', () => {
2298
+ const workspaceRoot = createWorkspace('follows-missing-scope-yaml-lock', {
2299
+ ...missingFollowsFiles(''),
2300
+ '.filedist.lock': yamlFiledistLock('.', ['.xdrs/myteam/index.md', '.xdrs/myteam/adrs/index.md']),
2301
+ });
2302
+
2303
+ const result = lintWorkspace(workspaceRoot);
2304
+
2305
+ expect(result.errors.join('\n')).not.toContain('nonexistent-core');
2306
+ });
2307
+
2308
+ test('detects external scope from YAML .filedist.lock with a custom output dir and quoted entries [022.06]', () => {
2309
+ const workspaceRoot = createWorkspace('follows-missing-scope-yaml-lock-outdir', {
2310
+ ...missingFollowsFiles('output/'),
2311
+ '.filedist.lock': yamlFiledistLock('output', ['.xdrs/myteam/index.md']).replace(/ - (.*)\n/, " - '$1'\n"),
2312
+ });
2313
+
2314
+ const result = lintWorkspace(path.join(workspaceRoot, 'output'));
2315
+
2316
+ expect(result.errors.join('\n')).not.toContain('nonexistent-core');
2317
+ });
2318
+
2319
+ test('still reports missing follows target of a scope absent from the YAML .filedist.lock', () => {
2320
+ const workspaceRoot = createWorkspace('follows-missing-scope-yaml-lock-local', {
2321
+ ...missingFollowsFiles(''),
2322
+ '.filedist.lock': yamlFiledistLock('.', ['.xdrs/other/index.md']),
2323
+ });
2324
+
2325
+ const result = lintWorkspace(workspaceRoot);
2326
+
2327
+ expect(result.errors.join('\n')).toContain('follows references scope "nonexistent-core" which does not exist in the workspace');
2328
+ });
2329
+
2279
2330
  test('accepts follows scope that exists in workspace', () => {
2280
2331
  const workspaceRoot = createWorkspace('follows-existing-scope', {
2281
2332
  '.xdrs/index.md': rootIndex(['[myteam](myteam/index.md)', '[myarea-core](myarea-core/index.md)']),
@@ -4442,6 +4493,69 @@ test('does not report emoji inside a code block in XDRS element documents', () =
4442
4493
  expect(result.errors.join('\n')).not.toContain('must not contain emojis');
4443
4494
  });
4444
4495
 
4496
+ // ─── Policy research section checks ───────────────────────────────────────────
4497
+
4498
+ function lintPolicyBody(name, body) {
4499
+ const workspaceRoot = createWorkspace(name, {
4500
+ '.xdrs/index.md': rootIndex(),
4501
+ '.xdrs/_local/adrs/index.md': localAdrIndex([
4502
+ '- [001-main](principles/001-main.md) - Main decision'
4503
+ ]),
4504
+ '.xdrs/_local/adrs/principles/001-main.md': xdrDocument(body),
4505
+ });
4506
+ return lintWorkspace(workspaceRoot).errors.join('\n');
4507
+ }
4508
+
4509
+ test('reports a Considered Options section in a Policy', () => {
4510
+ const errors = lintPolicyBody('policy-considered-options', 'Context.\n\n## Considered Options\n\n* (CHOSEN) **A** - reason');
4511
+
4512
+ expect(errors).toContain('Policy must not contain a "## Considered Options" section');
4513
+ });
4514
+
4515
+ test('does not report Considered Options inside a code block in a Policy', () => {
4516
+ const errors = lintPolicyBody('policy-considered-options-code', '```markdown\n## Considered Options\n```');
4517
+
4518
+ expect(errors).not.toContain('Considered Options');
4519
+ });
4520
+
4521
+ test('accepts a valid Related Researches section in a Policy', () => {
4522
+ const errors = lintPolicyBody('policy-related-researches-valid', 'Context.\n\n## Related Researches\n\n- [Options study](https://example.com/study) - Compares three storage engines by cost and latency');
4523
+
4524
+ expect(errors).not.toContain('Related Researches');
4525
+ });
4526
+
4527
+ test('reports a Related Researches entry without a summary', () => {
4528
+ const errors = lintPolicyBody('policy-related-researches-no-summary', 'Context.\n\n## Related Researches\n\n- [Options study](https://example.com/study)');
4529
+
4530
+ expect(errors).toContain('entries must be list items formatted as');
4531
+ });
4532
+
4533
+ test('reports a Related Researches entry that is not a list item', () => {
4534
+ const errors = lintPolicyBody('policy-related-researches-prose', 'Context.\n\n## Related Researches\n\nSee the options study.');
4535
+
4536
+ expect(errors).toContain('entries must be list items formatted as');
4537
+ });
4538
+
4539
+ test('reports a Related Researches summary with 20 or more words', () => {
4540
+ const summary = Array.from({ length: 20 }, (_, i) => `w${i}`).join(' ');
4541
+ const errors = lintPolicyBody('policy-related-researches-long', `Context.\n\n## Related Researches\n\n- [Options study](https://example.com/study) - ${summary}`);
4542
+
4543
+ expect(errors).toContain('summary must have fewer than 20 words');
4544
+ });
4545
+
4546
+ test('accepts a Related Researches summary with 19 words', () => {
4547
+ const summary = Array.from({ length: 19 }, (_, i) => `w${i}`).join(' ');
4548
+ const errors = lintPolicyBody('policy-related-researches-19', `Context.\n\n## Related Researches\n\n- [Options study](https://example.com/study) - ${summary}`);
4549
+
4550
+ expect(errors).not.toContain('Related Researches');
4551
+ });
4552
+
4553
+ test('reports an empty Related Researches section in a Policy', () => {
4554
+ const errors = lintPolicyBody('policy-related-researches-empty', 'Context.\n\n## Related Researches\n');
4555
+
4556
+ expect(errors).toContain('"## Related Researches" section must not be empty');
4557
+ });
4558
+
4445
4559
  test('reports emoji in SKILL.md', () => {
4446
4560
  const workspaceRoot = createWorkspace('skill-emoji', {
4447
4561
  '.xdrs/index.md': rootIndex(),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xdrs-core",
3
- "version": "0.53.1",
3
+ "version": "0.54.0",
4
4
  "description": "XDRS (eXtensible Decision Record System): a framework to structure, compile and distribute Architectural (ADR), Business (BDR), and Engineering (EDR) decision records contents so that AI agents and humans can reliably find and use them with hierarchical scopes and controlled rollout in the format of distributable versioned packages.",
5
5
  "repository": {
6
6
  "type": "git",