openlore 2.0.6 → 2.0.8

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.
Files changed (97) hide show
  1. package/README.md +140 -23
  2. package/dist/cli/commands/decisions.d.ts.map +1 -1
  3. package/dist/cli/commands/decisions.js +187 -174
  4. package/dist/cli/commands/decisions.js.map +1 -1
  5. package/dist/cli/commands/mcp.d.ts +694 -0
  6. package/dist/cli/commands/mcp.d.ts.map +1 -1
  7. package/dist/cli/commands/mcp.js +417 -203
  8. package/dist/cli/commands/mcp.js.map +1 -1
  9. package/dist/constants.d.ts +6 -0
  10. package/dist/constants.d.ts.map +1 -1
  11. package/dist/constants.js +14 -0
  12. package/dist/constants.js.map +1 -1
  13. package/dist/core/analyzer/artifact-generator.d.ts.map +1 -1
  14. package/dist/core/analyzer/artifact-generator.js +59 -4
  15. package/dist/core/analyzer/artifact-generator.js.map +1 -1
  16. package/dist/core/analyzer/call-graph.d.ts +19 -1
  17. package/dist/core/analyzer/call-graph.d.ts.map +1 -1
  18. package/dist/core/analyzer/call-graph.js +128 -28
  19. package/dist/core/analyzer/call-graph.js.map +1 -1
  20. package/dist/core/architecture/check.d.ts +63 -0
  21. package/dist/core/architecture/check.d.ts.map +1 -0
  22. package/dist/core/architecture/check.js +192 -0
  23. package/dist/core/architecture/check.js.map +1 -0
  24. package/dist/core/architecture/rules.d.ts +73 -0
  25. package/dist/core/architecture/rules.d.ts.map +1 -0
  26. package/dist/core/architecture/rules.js +201 -0
  27. package/dist/core/architecture/rules.js.map +1 -0
  28. package/dist/core/decisions/lock.d.ts +10 -0
  29. package/dist/core/decisions/lock.d.ts.map +1 -0
  30. package/dist/core/decisions/lock.js +77 -0
  31. package/dist/core/decisions/lock.js.map +1 -0
  32. package/dist/core/decisions/project.d.ts +59 -0
  33. package/dist/core/decisions/project.d.ts.map +1 -0
  34. package/dist/core/decisions/project.js +68 -0
  35. package/dist/core/decisions/project.js.map +1 -0
  36. package/dist/core/decisions/verifier.d.ts +10 -0
  37. package/dist/core/decisions/verifier.d.ts.map +1 -1
  38. package/dist/core/decisions/verifier.js +48 -5
  39. package/dist/core/decisions/verifier.js.map +1 -1
  40. package/dist/core/provenance/change-coupling.d.ts +68 -0
  41. package/dist/core/provenance/change-coupling.d.ts.map +1 -0
  42. package/dist/core/provenance/change-coupling.js +134 -0
  43. package/dist/core/provenance/change-coupling.js.map +1 -0
  44. package/dist/core/provenance/git-provenance.d.ts +67 -0
  45. package/dist/core/provenance/git-provenance.d.ts.map +1 -0
  46. package/dist/core/provenance/git-provenance.js +177 -0
  47. package/dist/core/provenance/git-provenance.js.map +1 -0
  48. package/dist/core/provenance/project.d.ts +37 -0
  49. package/dist/core/provenance/project.d.ts.map +1 -0
  50. package/dist/core/provenance/project.js +46 -0
  51. package/dist/core/provenance/project.js.map +1 -0
  52. package/dist/core/services/edge-store.d.ts +41 -0
  53. package/dist/core/services/edge-store.d.ts.map +1 -1
  54. package/dist/core/services/edge-store.js +251 -3
  55. package/dist/core/services/edge-store.js.map +1 -1
  56. package/dist/core/services/llm-service.d.ts +9 -0
  57. package/dist/core/services/llm-service.d.ts.map +1 -1
  58. package/dist/core/services/llm-service.js +15 -4
  59. package/dist/core/services/llm-service.js.map +1 -1
  60. package/dist/core/services/mcp-handlers/architecture.d.ts +19 -0
  61. package/dist/core/services/mcp-handlers/architecture.d.ts.map +1 -0
  62. package/dist/core/services/mcp-handlers/architecture.js +104 -0
  63. package/dist/core/services/mcp-handlers/architecture.js.map +1 -0
  64. package/dist/core/services/mcp-handlers/change-coupling.d.ts +16 -0
  65. package/dist/core/services/mcp-handlers/change-coupling.d.ts.map +1 -0
  66. package/dist/core/services/mcp-handlers/change-coupling.js +57 -0
  67. package/dist/core/services/mcp-handlers/change-coupling.js.map +1 -0
  68. package/dist/core/services/mcp-handlers/graph.d.ts +27 -0
  69. package/dist/core/services/mcp-handlers/graph.d.ts.map +1 -1
  70. package/dist/core/services/mcp-handlers/graph.js +98 -16
  71. package/dist/core/services/mcp-handlers/graph.js.map +1 -1
  72. package/dist/core/services/mcp-handlers/orient.d.ts.map +1 -1
  73. package/dist/core/services/mcp-handlers/orient.js +122 -2
  74. package/dist/core/services/mcp-handlers/orient.js.map +1 -1
  75. package/dist/core/services/mcp-handlers/reachability.d.ts +30 -0
  76. package/dist/core/services/mcp-handlers/reachability.d.ts.map +1 -0
  77. package/dist/core/services/mcp-handlers/reachability.js +222 -0
  78. package/dist/core/services/mcp-handlers/reachability.js.map +1 -0
  79. package/dist/core/services/mcp-handlers/structural-diff.d.ts +31 -0
  80. package/dist/core/services/mcp-handlers/structural-diff.d.ts.map +1 -0
  81. package/dist/core/services/mcp-handlers/structural-diff.js +268 -0
  82. package/dist/core/services/mcp-handlers/structural-diff.js.map +1 -0
  83. package/dist/core/services/mcp-handlers/test-impact.d.ts +34 -0
  84. package/dist/core/services/mcp-handlers/test-impact.d.ts.map +1 -0
  85. package/dist/core/services/mcp-handlers/test-impact.js +221 -0
  86. package/dist/core/services/mcp-handlers/test-impact.js.map +1 -0
  87. package/dist/core/services/mcp-handlers/tool-guard.d.ts +45 -0
  88. package/dist/core/services/mcp-handlers/tool-guard.d.ts.map +1 -0
  89. package/dist/core/services/mcp-handlers/tool-guard.js +81 -0
  90. package/dist/core/services/mcp-handlers/tool-guard.js.map +1 -0
  91. package/dist/core/services/mcp-handlers/utils.d.ts.map +1 -1
  92. package/dist/core/services/mcp-handlers/utils.js +15 -1
  93. package/dist/core/services/mcp-handlers/utils.js.map +1 -1
  94. package/dist/core/services/mcp-watcher.d.ts.map +1 -1
  95. package/dist/core/services/mcp-watcher.js +9 -0
  96. package/dist/core/services/mcp-watcher.js.map +1 -1
  97. package/package.json +9 -8
@@ -0,0 +1,192 @@
1
+ /**
2
+ * Architecture invariant checker (spec-23).
3
+ *
4
+ * Deterministic, offline passes over the file-level dependency graph. Two entry
5
+ * points:
6
+ * - `scanViolations` — the full current-violations report (continuous reporting).
7
+ * - `canImport` — the pre-edit query: "may a file under A import B?", answered
8
+ * BEFORE the edge is written. Pure; writes nothing.
9
+ *
10
+ * The `layers` kind reuses `classifyLayerEdge` from the call-graph analyzer so the
11
+ * layering convention has exactly one source of truth.
12
+ */
13
+ import { classifyLayerEdge } from '../analyzer/call-graph.js';
14
+ /** Normalize a path to forward slashes with no trailing slash. */
15
+ function norm(p) {
16
+ return p.replace(/\\/g, '/').replace(/\/+$/, '');
17
+ }
18
+ /**
19
+ * Prefix/dir match: `pattern` is treated as a path prefix (a directory or an exact
20
+ * file). Trailing `/`, `/*`, `/**`, or `*` are tolerated and stripped. Deterministic;
21
+ * no full glob engine (kept to the well-understood dir-prefix vocabulary).
22
+ */
23
+ export function pathMatches(rel, pattern) {
24
+ const r = norm(rel);
25
+ const p = norm(pattern.replace(/\/\*\*$/, '').replace(/\/\*$/, '').replace(/\*+$/, ''));
26
+ if (!p)
27
+ return false;
28
+ return r === p || r.startsWith(p + '/');
29
+ }
30
+ /**
31
+ * Evaluate one directed edge (relative paths) against one rule. Returns a reason
32
+ * string when the edge violates the rule, or null when it's legal under that rule.
33
+ */
34
+ function edgeViolation(fromRel, toRel, rule) {
35
+ switch (rule.kind) {
36
+ case 'layers': {
37
+ const cls = classifyLayerEdge(fromRel, toRel, rule.layers);
38
+ if (!cls)
39
+ return null;
40
+ return `layer "${cls.fromLayer}" must not depend on upper layer "${cls.toLayer}"`;
41
+ }
42
+ case 'forbidden': {
43
+ if (pathMatches(fromRel, rule.from) && pathMatches(toRel, rule.to)) {
44
+ return rule.reason ?? `"${rule.from}" must not depend on "${rule.to}"`;
45
+ }
46
+ return null;
47
+ }
48
+ case 'allowedOnly': {
49
+ if (!pathMatches(fromRel, rule.module))
50
+ return null;
51
+ // Intra-module dependencies are always allowed.
52
+ if (pathMatches(toRel, rule.module))
53
+ return null;
54
+ if (rule.mayDependOn.some(allowed => pathMatches(toRel, allowed)))
55
+ return null;
56
+ const why = rule.reason ? ` — ${rule.reason}` : '';
57
+ return `"${rule.module}" may depend only on [${rule.mayDependOn.join(', ')}]${why}`;
58
+ }
59
+ }
60
+ }
61
+ /** Build an absolute→relative path map from dependency-graph nodes. */
62
+ function relMap(depGraph) {
63
+ const m = new Map();
64
+ for (const n of depGraph.nodes) {
65
+ if (n.file?.absolutePath && n.file?.path)
66
+ m.set(n.file.absolutePath, norm(n.file.path));
67
+ }
68
+ return m;
69
+ }
70
+ /** Resolve an edge endpoint (absolute id or already-relative) to a relative path. */
71
+ function toRel(id, rels) {
72
+ return rels.get(id) ?? norm(id);
73
+ }
74
+ /** Every rule prefix, for the non-existent-path warning pass. */
75
+ function rulePrefixes(rule) {
76
+ switch (rule.kind) {
77
+ case 'layers': return Object.values(rule.layers).flat();
78
+ case 'forbidden': return [rule.from, rule.to];
79
+ case 'allowedOnly': return [rule.module, ...rule.mayDependOn];
80
+ }
81
+ }
82
+ /**
83
+ * Full violation scan over the dependency graph. Reports every edge that breaks a
84
+ * declared rule, plus warnings for rule prefixes that match no file in the repo
85
+ * (likely typos) — never a throw.
86
+ */
87
+ export function scanViolations(depGraph, rules) {
88
+ const warnings = [...rules.warnings];
89
+ if (rules.rules.length === 0) {
90
+ return { violations: [], warnings, checkedEdges: 0, rulesApplied: 0 };
91
+ }
92
+ const rels = relMap(depGraph);
93
+ const allRel = [...rels.values()];
94
+ // Warn on prefixes that match nothing (typos / stale rules).
95
+ const seenPrefix = new Set();
96
+ for (const rule of rules.rules) {
97
+ for (const prefix of rulePrefixes(rule)) {
98
+ if (seenPrefix.has(prefix))
99
+ continue;
100
+ seenPrefix.add(prefix);
101
+ if (!allRel.some(f => pathMatches(f, prefix))) {
102
+ warnings.push(`rule path "${prefix}" matches no file in the repository — check for a typo`);
103
+ }
104
+ }
105
+ }
106
+ const violations = [];
107
+ const seen = new Set();
108
+ for (const edge of depGraph.edges) {
109
+ const fromRel = toRel(edge.source, rels);
110
+ const toRelPath = toRel(edge.target, rels);
111
+ if (fromRel === toRelPath)
112
+ continue;
113
+ for (const rule of rules.rules) {
114
+ const reason = edgeViolation(fromRel, toRelPath, rule);
115
+ if (reason) {
116
+ const key = `${rule.kind}|${fromRel}|${toRelPath}|${reason}`;
117
+ if (seen.has(key))
118
+ continue;
119
+ seen.add(key);
120
+ violations.push({ kind: rule.kind, from: fromRel, to: toRelPath, reason, source: rule.source });
121
+ }
122
+ }
123
+ }
124
+ // Deterministic ordering.
125
+ violations.sort((a, b) => a.from !== b.from ? (a.from < b.from ? -1 : 1)
126
+ : a.to !== b.to ? (a.to < b.to ? -1 : 1)
127
+ : a.kind < b.kind ? -1 : a.kind > b.kind ? 1 : 0);
128
+ return { violations, warnings, checkedEdges: depGraph.edges.length, rulesApplied: rules.rules.length };
129
+ }
130
+ /**
131
+ * Pre-edit query: would importing `to` from `fromFile` be allowed under the rules?
132
+ * `to` may be a file path (relative or absolute) or a bare exported symbol — in the
133
+ * latter case it is resolved to its declaring file via the dependency graph. When
134
+ * the target cannot be resolved to a file, the verdict is permissive (`allowed:
135
+ * true`) with an `unresolved` note: the checker only decides what it can ground.
136
+ */
137
+ export function canImport(fromFile, to, rules, depGraph) {
138
+ if (rules.rules.length === 0) {
139
+ return { allowed: true, reason: 'no architecture rules declared — inert' };
140
+ }
141
+ const rels = depGraph ? relMap(depGraph) : new Map();
142
+ const fromRel = toRel(fromFile, rels);
143
+ // Resolve the target to a relative file path.
144
+ const looksLikePath = to.includes('/') || /\.[a-z]{1,5}$/i.test(to);
145
+ let targets;
146
+ if (looksLikePath) {
147
+ targets = [toRel(to, rels)];
148
+ }
149
+ else if (depGraph) {
150
+ // Bare symbol → declaring file(s).
151
+ targets = depGraph.nodes
152
+ .filter(n => (n.exports ?? []).some(e => e.name === to))
153
+ .map(n => norm(n.file.path))
154
+ .sort();
155
+ if (targets.length === 0) {
156
+ return {
157
+ allowed: true,
158
+ rule: { kind: 'unresolved', reason: `symbol "${to}" not found among exports` },
159
+ reason: `could not resolve "${to}" to a file; no rule could be evaluated`,
160
+ };
161
+ }
162
+ }
163
+ else {
164
+ return {
165
+ allowed: true,
166
+ rule: { kind: 'unresolved', reason: 'no dependency graph available to resolve symbol' },
167
+ reason: `could not resolve "${to}" without a dependency graph; no rule could be evaluated`,
168
+ };
169
+ }
170
+ // Disallow if ANY candidate target breaks ANY rule (conservative pre-edit guard).
171
+ for (const target of targets) {
172
+ if (fromRel === target)
173
+ continue;
174
+ for (const rule of rules.rules) {
175
+ const reason = edgeViolation(fromRel, target, rule);
176
+ if (reason) {
177
+ return {
178
+ allowed: false,
179
+ rule: { kind: rule.kind, source: rule.source, reason },
180
+ resolvedTo: target,
181
+ reason: `importing "${target}" from "${fromRel}" violates a ${rule.kind} rule: ${reason}`,
182
+ };
183
+ }
184
+ }
185
+ }
186
+ return {
187
+ allowed: true,
188
+ resolvedTo: looksLikePath ? undefined : targets[0],
189
+ reason: `no rule forbids importing ${looksLikePath ? `"${targets[0]}"` : `"${to}"`} from "${fromRel}"`,
190
+ };
191
+ }
192
+ //# sourceMappingURL=check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check.js","sourceRoot":"","sources":["../../../src/core/architecture/check.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,73 @@
1
+ /**
2
+ * Architecture invariant rules (spec-23).
3
+ *
4
+ * A small, opt-in, fully declarative rule format for dependency / layer /
5
+ * module-boundary constraints. Rules are author-declared in
6
+ * `.openlore/architecture.json` (and optionally sourced from synced ADR files),
7
+ * NEVER inferred by an LLM. Parsing is total: malformed entries become warnings
8
+ * and are skipped — loading rules never throws.
9
+ *
10
+ * The checker ([check.ts](./check.ts)) compiles these down to deterministic
11
+ * passes over the file-level dependency graph, reusing the canonical
12
+ * `classifyLayerEdge` primitive from the call-graph analyzer for the `layers` kind.
13
+ */
14
+ /** Where a rule came from — an author's config file, or a recorded decision (spec-16). */
15
+ export type RuleSource = 'config' | 'decision';
16
+ /**
17
+ * Ordered layering: key order is top → bottom, so a lower layer depending on an
18
+ * upper layer is a violation. Each layer maps to one or more path prefixes.
19
+ */
20
+ export interface LayersRule {
21
+ kind: 'layers';
22
+ layers: Record<string, string[]>;
23
+ source: RuleSource;
24
+ }
25
+ /** "Files under `from` must not depend on files under `to`." */
26
+ export interface ForbiddenRule {
27
+ kind: 'forbidden';
28
+ from: string;
29
+ to: string;
30
+ reason?: string;
31
+ source: RuleSource;
32
+ }
33
+ /** Module boundary: "files under `module` may depend ONLY on `mayDependOn` (plus themselves)." */
34
+ export interface AllowedOnlyRule {
35
+ kind: 'allowedOnly';
36
+ module: string;
37
+ mayDependOn: string[];
38
+ reason?: string;
39
+ source: RuleSource;
40
+ }
41
+ export type ArchitectureRule = LayersRule | ForbiddenRule | AllowedOnlyRule;
42
+ /** The parsed rule set plus any non-fatal warnings collected while loading. */
43
+ export interface ArchitectureRules {
44
+ rules: ArchitectureRule[];
45
+ warnings: string[];
46
+ }
47
+ /**
48
+ * Parse a raw config object into validated rules. Total: every malformed entry is
49
+ * recorded as a warning and skipped; this never throws. `source` tags provenance.
50
+ */
51
+ export declare function parseArchitectureRules(raw: unknown, source: RuleSource): ArchitectureRules;
52
+ /**
53
+ * Parse `Invariant:` markers out of synced ADR files. We read SYNCED files only —
54
+ * never `pending.json` fields, which are purged on sync (spec-16 edge case).
55
+ * Supported single-line grammar (deterministic, no LLM):
56
+ *
57
+ * Invariant: forbidden <fromPrefix> -> <toPrefix> [(reason)]
58
+ * Invariant: allowedOnly <modulePrefix> -> <prefixA>, <prefixB> [(reason)]
59
+ *
60
+ * Anything else is ignored. Returns rules tagged `source: 'decision'`.
61
+ */
62
+ export declare function parseInvariantMarkers(adrText: string): ArchitectureRule[];
63
+ /**
64
+ * Load the effective architecture rules for a project: the opt-in config file
65
+ * merged with any decision-sourced invariants. Absent config is NOT an error —
66
+ * returns an empty, inert rule set. Never throws.
67
+ */
68
+ export declare function loadArchitectureRules(absDir: string, opts?: {
69
+ includeDecisions?: boolean;
70
+ }): Promise<ArchitectureRules>;
71
+ /** True when no rules are declared — the instrument is fully inert. */
72
+ export declare function rulesAreInert(rules: ArchitectureRules): boolean;
73
+ //# sourceMappingURL=rules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../../../src/core/architecture/rules.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAMH,0FAA0F;AAC1F,MAAM,MAAM,UAAU,GAAG,QAAQ,GAAG,UAAU,CAAC;AAE/C;;;GAGG;AACH,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,EAAE,CAAC,CAAC;IACjC,MAAM,EAAE,UAAU,CAAC;CACpB;AAED,gEAAgE;AAChE,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,WAAW,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,UAAU,CAAC;CACpB;AAED,kGAAkG;AAClG,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,aAAa,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,EAAE,CAAC;IACtB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,EAAE,UAAU,CAAC;CACpB;AAED,MAAM,MAAM,gBAAgB,GAAG,UAAU,GAAG,aAAa,GAAG,eAAe,CAAC;AAE5E,+EAA+E;AAC/E,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,gBAAgB,EAAE,CAAC;IAC1B,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAeD;;;GAGG;AACH,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,OAAO,EAAE,MAAM,EAAE,UAAU,GAAG,iBAAiB,CAwE1F;AAED;;;;;;;;;GASG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,gBAAgB,EAAE,CA0BzE;AAyBD;;;;GAIG;AACH,wBAAsB,qBAAqB,CACzC,MAAM,EAAE,MAAM,EACd,IAAI,GAAE;IAAE,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAAO,GACxC,OAAO,CAAC,iBAAiB,CAAC,CA4B5B;AAED,uEAAuE;AACvE,wBAAgB,aAAa,CAAC,KAAK,EAAE,iBAAiB,GAAG,OAAO,CAE/D"}
@@ -0,0 +1,201 @@
1
+ /**
2
+ * Architecture invariant rules (spec-23).
3
+ *
4
+ * A small, opt-in, fully declarative rule format for dependency / layer /
5
+ * module-boundary constraints. Rules are author-declared in
6
+ * `.openlore/architecture.json` (and optionally sourced from synced ADR files),
7
+ * NEVER inferred by an LLM. Parsing is total: malformed entries become warnings
8
+ * and are skipped — loading rules never throws.
9
+ *
10
+ * The checker ([check.ts](./check.ts)) compiles these down to deterministic
11
+ * passes over the file-level dependency graph, reusing the canonical
12
+ * `classifyLayerEdge` primitive from the call-graph analyzer for the `layers` kind.
13
+ */
14
+ import { readFile, readdir } from 'node:fs/promises';
15
+ import { join } from 'node:path';
16
+ import { OPENLORE_DIR } from '../../constants.js';
17
+ const ARCHITECTURE_CONFIG_FILE = 'architecture.json';
18
+ function isStringArray(v) {
19
+ return Array.isArray(v) && v.every(x => typeof x === 'string');
20
+ }
21
+ /**
22
+ * Parse a raw config object into validated rules. Total: every malformed entry is
23
+ * recorded as a warning and skipped; this never throws. `source` tags provenance.
24
+ */
25
+ export function parseArchitectureRules(raw, source) {
26
+ const rules = [];
27
+ const warnings = [];
28
+ if (!raw || typeof raw !== 'object') {
29
+ return { rules, warnings: ['architecture rules: expected a JSON object'] };
30
+ }
31
+ const cfg = raw;
32
+ // layers
33
+ if (cfg.layers !== undefined) {
34
+ if (cfg.layers && typeof cfg.layers === 'object' && !Array.isArray(cfg.layers)) {
35
+ const layers = {};
36
+ for (const [name, prefixes] of Object.entries(cfg.layers)) {
37
+ if (isStringArray(prefixes) && prefixes.length > 0) {
38
+ layers[name] = prefixes;
39
+ }
40
+ else {
41
+ warnings.push(`layers.${name}: expected a non-empty array of path prefixes — skipped`);
42
+ }
43
+ }
44
+ if (Object.keys(layers).length >= 2) {
45
+ rules.push({ kind: 'layers', layers, source });
46
+ }
47
+ else if (Object.keys(layers).length > 0) {
48
+ warnings.push('layers: need at least 2 layers to define a direction — skipped');
49
+ }
50
+ }
51
+ else {
52
+ warnings.push('layers: expected an object mapping layer name → path prefixes — skipped');
53
+ }
54
+ }
55
+ // forbidden
56
+ if (cfg.forbidden !== undefined) {
57
+ if (Array.isArray(cfg.forbidden)) {
58
+ cfg.forbidden.forEach((r, i) => {
59
+ if (r && typeof r.from === 'string' && typeof r.to === 'string') {
60
+ rules.push({
61
+ kind: 'forbidden',
62
+ from: r.from,
63
+ to: r.to,
64
+ reason: typeof r.reason === 'string' ? r.reason : undefined,
65
+ source,
66
+ });
67
+ }
68
+ else {
69
+ warnings.push(`forbidden[${i}]: requires string "from" and "to" — skipped`);
70
+ }
71
+ });
72
+ }
73
+ else {
74
+ warnings.push('forbidden: expected an array — skipped');
75
+ }
76
+ }
77
+ // allowedOnly
78
+ if (cfg.allowedOnly !== undefined) {
79
+ if (Array.isArray(cfg.allowedOnly)) {
80
+ cfg.allowedOnly.forEach((r, i) => {
81
+ if (r && typeof r.module === 'string' && isStringArray(r.mayDependOn)) {
82
+ rules.push({
83
+ kind: 'allowedOnly',
84
+ module: r.module,
85
+ mayDependOn: r.mayDependOn,
86
+ reason: typeof r.reason === 'string' ? r.reason : undefined,
87
+ source,
88
+ });
89
+ }
90
+ else {
91
+ warnings.push(`allowedOnly[${i}]: requires string "module" and string[] "mayDependOn" — skipped`);
92
+ }
93
+ });
94
+ }
95
+ else {
96
+ warnings.push('allowedOnly: expected an array — skipped');
97
+ }
98
+ }
99
+ return { rules, warnings };
100
+ }
101
+ /**
102
+ * Parse `Invariant:` markers out of synced ADR files. We read SYNCED files only —
103
+ * never `pending.json` fields, which are purged on sync (spec-16 edge case).
104
+ * Supported single-line grammar (deterministic, no LLM):
105
+ *
106
+ * Invariant: forbidden <fromPrefix> -> <toPrefix> [(reason)]
107
+ * Invariant: allowedOnly <modulePrefix> -> <prefixA>, <prefixB> [(reason)]
108
+ *
109
+ * Anything else is ignored. Returns rules tagged `source: 'decision'`.
110
+ */
111
+ export function parseInvariantMarkers(adrText) {
112
+ const rules = [];
113
+ for (const line of adrText.split(/\r?\n/)) {
114
+ const m = line.match(/^\s*(?:[-*>]\s*)*Invariant:\s*(.+)$/i);
115
+ if (!m)
116
+ continue;
117
+ let body = m[1].trim();
118
+ let reason;
119
+ const reasonMatch = body.match(/\(([^)]*)\)\s*$/);
120
+ if (reasonMatch) {
121
+ reason = reasonMatch[1].trim() || undefined;
122
+ body = body.slice(0, reasonMatch.index).trim();
123
+ }
124
+ const forbidden = body.match(/^forbidden\s+(\S+)\s*->\s*(\S+)$/i);
125
+ if (forbidden) {
126
+ rules.push({ kind: 'forbidden', from: forbidden[1], to: forbidden[2], reason, source: 'decision' });
127
+ continue;
128
+ }
129
+ const allowed = body.match(/^allowedOnly\s+(\S+)\s*->\s*(.+)$/i);
130
+ if (allowed) {
131
+ const mayDependOn = allowed[2].split(',').map(s => s.trim()).filter(Boolean);
132
+ if (mayDependOn.length > 0) {
133
+ rules.push({ kind: 'allowedOnly', module: allowed[1], mayDependOn, reason, source: 'decision' });
134
+ }
135
+ }
136
+ }
137
+ return rules;
138
+ }
139
+ /** Read invariants from synced ADR files under `openspec/decisions/adr-*.md`. */
140
+ async function loadDecisionInvariants(absDir) {
141
+ const rules = [];
142
+ const warnings = [];
143
+ const decisionsDir = join(absDir, 'openspec', 'decisions');
144
+ let entries;
145
+ try {
146
+ entries = await readdir(decisionsDir);
147
+ }
148
+ catch {
149
+ return { rules, warnings }; // no decisions dir — fine
150
+ }
151
+ for (const name of entries.sort()) {
152
+ if (!/^adr-.*\.md$/i.test(name))
153
+ continue;
154
+ try {
155
+ const text = await readFile(join(decisionsDir, name), 'utf-8');
156
+ rules.push(...parseInvariantMarkers(text));
157
+ }
158
+ catch {
159
+ warnings.push(`could not read decision file ${name}`);
160
+ }
161
+ }
162
+ return { rules, warnings };
163
+ }
164
+ /**
165
+ * Load the effective architecture rules for a project: the opt-in config file
166
+ * merged with any decision-sourced invariants. Absent config is NOT an error —
167
+ * returns an empty, inert rule set. Never throws.
168
+ */
169
+ export async function loadArchitectureRules(absDir, opts = {}) {
170
+ const rules = [];
171
+ const warnings = [];
172
+ // Config file (opt-in).
173
+ try {
174
+ const raw = await readFile(join(absDir, OPENLORE_DIR, ARCHITECTURE_CONFIG_FILE), 'utf-8');
175
+ let parsed;
176
+ try {
177
+ parsed = JSON.parse(raw);
178
+ }
179
+ catch {
180
+ return { rules, warnings: [`${OPENLORE_DIR}/${ARCHITECTURE_CONFIG_FILE}: invalid JSON — ignored`] };
181
+ }
182
+ const fromConfig = parseArchitectureRules(parsed, 'config');
183
+ rules.push(...fromConfig.rules);
184
+ warnings.push(...fromConfig.warnings);
185
+ }
186
+ catch {
187
+ /* no config file — inert */
188
+ }
189
+ // Decision-sourced invariants (spec-16 tie), opt-in via flag.
190
+ if (opts.includeDecisions !== false) {
191
+ const fromDecisions = await loadDecisionInvariants(absDir);
192
+ rules.push(...fromDecisions.rules);
193
+ warnings.push(...fromDecisions.warnings);
194
+ }
195
+ return { rules, warnings };
196
+ }
197
+ /** True when no rules are declared — the instrument is fully inert. */
198
+ export function rulesAreInert(rules) {
199
+ return rules.rules.length === 0;
200
+ }
201
+ //# sourceMappingURL=rules.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules.js","sourceRoot":"","sources":["../../../src/core/architecture/rules.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Acquire the consolidation lock. Returns an idempotent release function.
3
+ * Waits (polling) while another process holds it; steals a stale lock left by a
4
+ * crashed holder. On the rare MAX_WAIT timeout it proceeds without the lock
5
+ * rather than block a background process forever.
6
+ */
7
+ export declare function acquireDecisionsLock(rootPath: string): Promise<() => Promise<void>>;
8
+ /** Run `fn` while holding the consolidation lock; always releases. */
9
+ export declare function withDecisionsLock<T>(rootPath: string, fn: () => Promise<T>): Promise<T>;
10
+ //# sourceMappingURL=lock.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lock.d.ts","sourceRoot":"","sources":["../../../src/core/decisions/lock.ts"],"names":[],"mappings":"AAwBA;;;;;GAKG;AACH,wBAAsB,oBAAoB,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,CAmCzF;AAED,sEAAsE;AACtE,wBAAsB,iBAAiB,CAAC,CAAC,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAO7F"}
@@ -0,0 +1,77 @@
1
+ /**
2
+ * Cross-process advisory lock for the decision store (Spec 15 dogfood fix).
3
+ *
4
+ * `record_decision` spawns a detached `decisions --consolidate` per call. Under
5
+ * rapid recording, several consolidations run at once; each does a
6
+ * load → mutate → save of `pending.json`, and the later save clobbers the
7
+ * earlier one — silently losing decisions (observed during the spec-15 dogfood:
8
+ * 5 rapid records produced 3 stored decisions).
9
+ *
10
+ * Serializing consolidation behind this lock — and reloading the store *inside*
11
+ * it — makes the read-modify-write safe: whoever holds the lock sees every draft
12
+ * written so far, so nothing is lost.
13
+ */
14
+ import { open, stat, unlink, mkdir } from 'node:fs/promises';
15
+ import { join } from 'node:path';
16
+ import { decisionsDir } from './store.js';
17
+ const LOCK_FILE = '.consolidate.lock';
18
+ const STALE_MS = 120_000; // steal a lock older than this (crashed/killed holder)
19
+ const POLL_MS = 150;
20
+ const MAX_WAIT_MS = 180_000; // give up waiting after this and proceed best-effort
21
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
22
+ /**
23
+ * Acquire the consolidation lock. Returns an idempotent release function.
24
+ * Waits (polling) while another process holds it; steals a stale lock left by a
25
+ * crashed holder. On the rare MAX_WAIT timeout it proceeds without the lock
26
+ * rather than block a background process forever.
27
+ */
28
+ export async function acquireDecisionsLock(rootPath) {
29
+ const dir = decisionsDir(rootPath);
30
+ await mkdir(dir, { recursive: true });
31
+ const lockPath = join(dir, LOCK_FILE);
32
+ const start = Date.now();
33
+ for (;;) {
34
+ try {
35
+ const fh = await open(lockPath, 'wx'); // exclusive create — fails if held
36
+ await fh.writeFile(`${process.pid} ${new Date().toISOString()}`);
37
+ await fh.close();
38
+ let released = false;
39
+ return async () => {
40
+ if (released)
41
+ return;
42
+ released = true;
43
+ await unlink(lockPath).catch(() => { });
44
+ };
45
+ }
46
+ catch (err) {
47
+ if (err.code !== 'EEXIST')
48
+ throw err;
49
+ // Held by someone else — steal if stale, else wait.
50
+ try {
51
+ const s = await stat(lockPath);
52
+ if (Date.now() - s.mtimeMs > STALE_MS) {
53
+ await unlink(lockPath).catch(() => { });
54
+ continue; // retry acquire immediately
55
+ }
56
+ }
57
+ catch {
58
+ continue; // lock vanished between open and stat — retry acquire
59
+ }
60
+ if (Date.now() - start > MAX_WAIT_MS) {
61
+ return async () => { }; // best-effort: proceed without the lock
62
+ }
63
+ await sleep(POLL_MS);
64
+ }
65
+ }
66
+ }
67
+ /** Run `fn` while holding the consolidation lock; always releases. */
68
+ export async function withDecisionsLock(rootPath, fn) {
69
+ const release = await acquireDecisionsLock(rootPath);
70
+ try {
71
+ return await fn();
72
+ }
73
+ finally {
74
+ await release();
75
+ }
76
+ }
77
+ //# sourceMappingURL=lock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lock.js","sourceRoot":"","sources":["../../../src/core/decisions/lock.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Projection: decision store → first-class graph nodes + `affects` edges (spec-16).
3
+ *
4
+ * This is the decisions analogue of src/core/analyzer/iac/project.ts. The JSON
5
+ * store (.openlore/decisions/pending.json) remains the authored source of truth;
6
+ * this projection is derived and regenerable. Promoting a Decision to a graph node
7
+ * with `affects` edges to the files it governs turns orient's runtime
8
+ * set-membership filter into a deterministic graph join, and lets
9
+ * analyze_impact / get_subgraph answer "what decisions govern this code?" with the
10
+ * same machinery they use for code edges.
11
+ *
12
+ * Edge direction is decision → governed file, mirroring the IaC convention
13
+ * (dependent/owner → dependency): a decision "affects" the files it governs.
14
+ */
15
+ import type { DecisionStore, DecisionStatus } from '../../types/index.js';
16
+ /** Graph node-id namespace for projected decisions (keeps them distinct from code ids). */
17
+ export declare const DECISION_NODE_PREFIX = "decision::";
18
+ /** Stable graph node id for a decision's 8-char store id. */
19
+ export declare function decisionNodeId(decisionId: string): string;
20
+ /** A projected decision — a first-class, clearly-typed graph node (not a FunctionNode). */
21
+ export interface DecisionNode {
22
+ /** Graph node id, e.g. "decision::c6d1ad07". */
23
+ id: string;
24
+ /** Original 8-char store id. */
25
+ decisionId: string;
26
+ /** Discriminator so callers never confuse this with a code node. */
27
+ kind: 'decision';
28
+ title: string;
29
+ status: DecisionStatus;
30
+ rationale: string;
31
+ consequences: string;
32
+ affectedDomains: string[];
33
+ affectedFiles: string[];
34
+ confidence: 'high' | 'medium' | 'low';
35
+ /** 8-char id of a prior decision this one reverses, if any. */
36
+ supersedes?: string;
37
+ }
38
+ /** An `affects` edge: decision node → a governed file path. */
39
+ export interface DecisionAffectsEdge {
40
+ /** Graph node id of the decision ("decision::<id>"). */
41
+ decisionNodeId: string;
42
+ /** Repo-relative, POSIX path of the governed file. */
43
+ filePath: string;
44
+ kind: 'affects';
45
+ }
46
+ export interface ProjectedDecisions {
47
+ nodes: DecisionNode[];
48
+ edges: DecisionAffectsEdge[];
49
+ }
50
+ /**
51
+ * Project the active decisions in a store onto decision nodes + `affects` edges.
52
+ *
53
+ * - Inactive decisions (synced / rejected / phantom) are excluded, matching
54
+ * orient's INACTIVE_STATUSES — their content already lives in ADRs / spec.md.
55
+ * - An empty or legacy store projects to zero nodes/edges.
56
+ * - Output is fully sorted for deterministic, regenerable persistence.
57
+ */
58
+ export declare function projectDecisions(store: DecisionStore): ProjectedDecisions;
59
+ //# sourceMappingURL=project.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project.d.ts","sourceRoot":"","sources":["../../../src/core/decisions/project.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAmB,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAG3F,2FAA2F;AAC3F,eAAO,MAAM,oBAAoB,eAAe,CAAC;AAEjD,6DAA6D;AAC7D,wBAAgB,cAAc,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,2FAA2F;AAC3F,MAAM,WAAW,YAAY;IAC3B,gDAAgD;IAChD,EAAE,EAAE,MAAM,CAAC;IACX,gCAAgC;IAChC,UAAU,EAAE,MAAM,CAAC;IACnB,oEAAoE;IACpE,IAAI,EAAE,UAAU,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,cAAc,CAAC;IACvB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;IACrB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,KAAK,CAAC;IACtC,+DAA+D;IAC/D,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,+DAA+D;AAC/D,MAAM,WAAW,mBAAmB;IAClC,wDAAwD;IACxD,cAAc,EAAE,MAAM,CAAC;IACvB,sDAAsD;IACtD,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,SAAS,CAAC;CACjB;AAED,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,KAAK,EAAE,mBAAmB,EAAE,CAAC;CAC9B;AAOD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,aAAa,GAAG,kBAAkB,CAmCzE"}