xdrs-core 0.15.1 → 0.15.2

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.
@@ -77,12 +77,14 @@ Choose a title that clearly states the question this XDR answers, not the answer
77
77
  Use the mandatory template from `002-xdr-standards`:
78
78
 
79
79
  ```
80
- # [scope]-[type]-[number]: [Short Title]
80
+ ---
81
+ name: [scope]-[type]-[number]-[short-title]
82
+ description: [What this decision is about and when to use it]
83
+ applied-to: [Optional. Contexts this decision applies to, under 40 words]
84
+ valid-from: [Optional. ISO date YYYY-MM-DD from when enforcement begins]
85
+ ---
81
86
 
82
- ## Metadata
83
- [Optional. Include only when at least one metadata field is present]
84
- Valid: [Optional. Use from YYYY-MM-DD to set a convergence date for adoption]
85
- Applied to: [Optional short applicability scope, under 40 words]
87
+ # [scope]-[type]-[number]: [Short Title]
86
88
 
87
89
  ## Context and Problem Statement
88
90
  [background, who is impacted, and the explicit question being answered - under 40 words]
@@ -103,10 +105,10 @@ Applied to: [Optional short applicability scope, under 40 words]
103
105
  ```
104
106
 
105
107
  Mandatory rules to apply while drafting:
106
- - Include `## Metadata` only when `Valid:` and/or `Applied to:` adds value; omit the whole section when none of those fields is defined.
107
- - When present, place `## Metadata` immediately before `## Context and Problem Statement`.
108
- - Keep `Applied to:` under 40 words and use `Valid:` only with `from YYYY-MM-DD` format.
109
- - When metadata is present, write it so a reader can decide whether the XDR should be used for the current case without guessing. `Valid:` sets a convergence date for adoption, `Applied to:` narrows the contexts where the decision applies, and the decision text defines any remaining boundaries.
108
+ - Include frontmatter `applied-to:` only when it adds value by narrowing the decision scope; omit it when the decision applies broadly.
109
+ - Include frontmatter `valid-from:` only when there is a specific future enforcement date; omit it when the decision is immediately effective.
110
+ - Keep `applied-to:` under 40 words and use `valid-from:` only with `YYYY-MM-DD` ISO format.
111
+ - When frontmatter metadata is present, write it so a reader can decide whether the XDR should be used for the current case without guessing. `valid-from:` sets a convergence date for adoption, `applied-to:` narrows the contexts where the decision applies, and the decision text defines any remaining boundaries.
110
112
  - Use mandatory language ("must", "always", "never") only for hard requirements; use advisory language ("should", "recommended") for guidance.
111
113
  - Do not duplicate content already in referenced XDRs — link instead.
112
114
  - Keep the decision itself authoritative in the XDR. Supporting artifacts may elaborate, but they should not restate the full decision when a short reference is enough.
@@ -122,7 +124,7 @@ Mandatory rules to apply while drafting:
122
124
  Check every item before finalizing:
123
125
 
124
126
  1. **Length**: Is it under 1300 words? Trim verbose explanations. Move detailed skills to a separate file and link.
125
- 2. **Metadata**: If metadata exists, is it directly before Context, limited to `Valid:` / `Applied to:`, omitted entirely when both are absent, and specific enough for a reader to decide whether the XDR is currently valid and applicable?
127
+ 2. **Frontmatter**: Are `applied-to:` and `valid-from:` present only when they add value, omitted entirely when not needed, and specific enough for a reader to decide whether the XDR is currently valid and applicable?
126
128
  3. **Originality**: Does every sentence add value that cannot be found in a generic web search? Remove obvious advice. Keep only the project-specific decision.
127
129
  4. **Clarity**: Is the chosen option unambiguous? Is the "why" clear in one reading?
128
130
  5. **Redundancy**: Is the XDR the primary source for the decision itself, with related documents linked instead of duplicated wherever possible?
package/lib/lint.js CHANGED
@@ -31,8 +31,10 @@ function runLintCli(args) {
31
31
  return 0;
32
32
  }
33
33
 
34
- const targetPath = args[0] || '.';
35
- const result = lintWorkspace(targetPath);
34
+ const all = args.includes('--all');
35
+ const pathArgs = args.filter((a) => !a.startsWith('--'));
36
+ const targetPath = pathArgs[0] || '.';
37
+ const result = lintWorkspace(targetPath, { ignoreReadOnly: !all });
36
38
 
37
39
  if (result.errors.length === 0) {
38
40
  console.log(`Lint passed for ${toDisplayPath(result.xdrsRoot)}`);
@@ -48,12 +50,15 @@ function runLintCli(args) {
48
50
  }
49
51
 
50
52
  function printHelp() {
51
- console.log('Usage: xdrs-core lint [path]\n');
53
+ console.log('Usage: xdrs-core lint [options] [path]\n');
52
54
  console.log('Lint the XDR tree rooted at [path]/.xdrs or at [path] when [path] already points to .xdrs.');
53
- console.log('All other commands continue to be delegated to the bundled filedist CLI.');
55
+ console.log('\nOptions:');
56
+ console.log(' --all Check all files, including read-only files from other scopes (default: skip read-only files)');
57
+ console.log('\nAll other commands continue to be delegated to the bundled filedist CLI.');
54
58
  }
55
59
 
56
- function lintWorkspace(targetPath) {
60
+ function lintWorkspace(targetPath, options = {}) {
61
+ const { ignoreReadOnly = true } = options;
57
62
  const resolvedTarget = path.resolve(targetPath);
58
63
  const xdrsRoot = path.basename(resolvedTarget) === '.xdrs'
59
64
  ? resolvedTarget
@@ -76,7 +81,7 @@ function lintWorkspace(targetPath) {
76
81
  }
77
82
 
78
83
  for (const scopeEntry of scopeEntries) {
79
- lintScopeDirectory(xdrsRoot, scopeEntry.name, errors, actualTypeIndexes);
84
+ lintScopeDirectory(xdrsRoot, scopeEntry.name, errors, actualTypeIndexes, ignoreReadOnly);
80
85
  }
81
86
 
82
87
  const rootIndexPath = path.join(xdrsRoot, 'index.md');
@@ -124,9 +129,13 @@ function lintRootIndex(rootIndexPath, xdrsRoot, actualTypeIndexes, errors) {
124
129
  }
125
130
  }
126
131
 
127
- function lintScopeDirectory(xdrsRoot, scopeName, errors, actualTypeIndexes) {
132
+ function lintScopeDirectory(xdrsRoot, scopeName, errors, actualTypeIndexes, ignoreReadOnly) {
128
133
  const scopePath = path.join(xdrsRoot, scopeName);
129
134
 
135
+ if (ignoreReadOnly && isReadOnly(scopePath)) {
136
+ return;
137
+ }
138
+
130
139
  if (!isValidScopeName(scopeName)) {
131
140
  errors.push(`Invalid scope name: ${toDisplayPath(scopePath)}`);
132
141
  }
@@ -744,6 +753,15 @@ function existsFile(filePath) {
744
753
  }
745
754
  }
746
755
 
756
+ function isReadOnly(filePath) {
757
+ try {
758
+ fs.accessSync(filePath, fs.constants.W_OK);
759
+ return false;
760
+ } catch {
761
+ return true;
762
+ }
763
+ }
764
+
747
765
  function displayPath(indexPath, targetPath) {
748
766
  return `${toDisplayPath(indexPath)} -> ${toDisplayPath(targetPath)}`;
749
767
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "xdrs-core",
3
- "version": "0.15.1",
3
+ "version": "0.15.2",
4
4
  "description": "A standard way to organize Decision Records (XDRs) across scopes, subjects, and teams so that AI agents can reliably query and follow them.",
5
5
  "repository": {
6
6
  "type": "git",