vigiles 2.0.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.
Files changed (156) hide show
  1. package/.claude/settings.json +46 -0
  2. package/.claude/settings.local.json +8 -0
  3. package/.claude-plugin/hooks/post-edit.sh +34 -0
  4. package/.claude-plugin/hooks/pre-edit.sh +40 -0
  5. package/.claude-plugin/hooks/session-start.sh +38 -0
  6. package/.claude-plugin/marketplace.json +14 -0
  7. package/.claude-plugin/plugin.json +47 -0
  8. package/.github/workflows/ci.yml +81 -0
  9. package/.prettierignore +1 -0
  10. package/.vigiles/generated.d.ts +205 -0
  11. package/CLAUDE.md +95 -0
  12. package/CLAUDE.md.spec.ts +142 -0
  13. package/CONTRIBUTING.md +121 -0
  14. package/LICENSE +21 -0
  15. package/README.md +377 -0
  16. package/action.yml +25 -0
  17. package/dist/action.d.ts +7 -0
  18. package/dist/action.d.ts.map +1 -0
  19. package/dist/action.js +180 -0
  20. package/dist/action.js.map +1 -0
  21. package/dist/cli.d.ts +12 -0
  22. package/dist/cli.d.ts.map +1 -0
  23. package/dist/cli.js +1267 -0
  24. package/dist/cli.js.map +1 -0
  25. package/dist/cli.test.d.ts +2 -0
  26. package/dist/cli.test.d.ts.map +1 -0
  27. package/dist/cli.test.js +650 -0
  28. package/dist/cli.test.js.map +1 -0
  29. package/dist/compile.d.ts +101 -0
  30. package/dist/compile.d.ts.map +1 -0
  31. package/dist/compile.js +503 -0
  32. package/dist/compile.js.map +1 -0
  33. package/dist/evolve.d.ts +132 -0
  34. package/dist/evolve.d.ts.map +1 -0
  35. package/dist/evolve.js +599 -0
  36. package/dist/evolve.js.map +1 -0
  37. package/dist/freshness.d.ts +67 -0
  38. package/dist/freshness.d.ts.map +1 -0
  39. package/dist/freshness.js +244 -0
  40. package/dist/freshness.js.map +1 -0
  41. package/dist/freshness.test.d.ts +2 -0
  42. package/dist/freshness.test.d.ts.map +1 -0
  43. package/dist/freshness.test.js +356 -0
  44. package/dist/freshness.test.js.map +1 -0
  45. package/dist/generate-types.d.ts +34 -0
  46. package/dist/generate-types.d.ts.map +1 -0
  47. package/dist/generate-types.js +381 -0
  48. package/dist/generate-types.js.map +1 -0
  49. package/dist/inline.d.ts +58 -0
  50. package/dist/inline.d.ts.map +1 -0
  51. package/dist/inline.js +142 -0
  52. package/dist/inline.js.map +1 -0
  53. package/dist/inline.test.d.ts +5 -0
  54. package/dist/inline.test.d.ts.map +1 -0
  55. package/dist/inline.test.js +152 -0
  56. package/dist/inline.test.js.map +1 -0
  57. package/dist/linters.d.ts +38 -0
  58. package/dist/linters.d.ts.map +1 -0
  59. package/dist/linters.js +588 -0
  60. package/dist/linters.js.map +1 -0
  61. package/dist/proofs.d.ts +272 -0
  62. package/dist/proofs.d.ts.map +1 -0
  63. package/dist/proofs.js +622 -0
  64. package/dist/proofs.js.map +1 -0
  65. package/dist/proofs.test.d.ts +9 -0
  66. package/dist/proofs.test.d.ts.map +1 -0
  67. package/dist/proofs.test.js +952 -0
  68. package/dist/proofs.test.js.map +1 -0
  69. package/dist/spec.d.ts +258 -0
  70. package/dist/spec.d.ts.map +1 -0
  71. package/dist/spec.js +113 -0
  72. package/dist/spec.js.map +1 -0
  73. package/dist/spec.test.d.ts +2 -0
  74. package/dist/spec.test.d.ts.map +1 -0
  75. package/dist/spec.test.js +1222 -0
  76. package/dist/spec.test.js.map +1 -0
  77. package/dist/types.d.ts +101 -0
  78. package/dist/types.d.ts.map +1 -0
  79. package/dist/types.js +3 -0
  80. package/dist/types.js.map +1 -0
  81. package/dist/validate.d.ts +10 -0
  82. package/dist/validate.d.ts.map +1 -0
  83. package/dist/validate.js +286 -0
  84. package/dist/validate.js.map +1 -0
  85. package/dist/validate.test.d.ts +2 -0
  86. package/dist/validate.test.d.ts.map +1 -0
  87. package/dist/validate.test.js +531 -0
  88. package/dist/validate.test.js.map +1 -0
  89. package/docs/agent-setup.md +85 -0
  90. package/docs/agent-workflows.md +103 -0
  91. package/docs/comparison.md +71 -0
  92. package/docs/freshness.md +124 -0
  93. package/docs/inline-mode.md +119 -0
  94. package/docs/linter-support.md +166 -0
  95. package/docs/spec-format.md +194 -0
  96. package/eslint.config.mjs +79 -0
  97. package/examples/CLAUDE.md +54 -0
  98. package/examples/CLAUDE.md.spec.ts +65 -0
  99. package/examples/SKILL.md +50 -0
  100. package/examples/SKILL.md.spec.ts +57 -0
  101. package/fixtures/example-project/CLAUDE.md +11 -0
  102. package/fixtures/example-project/package.json +9 -0
  103. package/fixtures/example-project/src/index.ts +3 -0
  104. package/fixtures/example-project/src/utils.test.ts +2 -0
  105. package/fixtures/example-project/src/utils.ts +3 -0
  106. package/logo.png +0 -0
  107. package/package.json +42 -0
  108. package/research/adoption-strategy.md +111 -0
  109. package/research/agent-integration.md +145 -0
  110. package/research/ai-code-quality.md +197 -0
  111. package/research/code-search-for-agents.md +313 -0
  112. package/research/competitive-landscape.md +163 -0
  113. package/research/doc-freshness.md +516 -0
  114. package/research/executable-specs.md +368 -0
  115. package/research/feature-ideas.md +464 -0
  116. package/research/formal-proofs-for-agents.md +338 -0
  117. package/research/fp-for-agent-harness.md +150 -0
  118. package/research/fp-for-deterministic-ai.md +131 -0
  119. package/research/self-evolving-specs.md +298 -0
  120. package/schemas/claude-md-strict.yml +18 -0
  121. package/schemas/claude-md.yml +6 -0
  122. package/schemas/skill-strict.yml +12 -0
  123. package/schemas/skill.yml +5 -0
  124. package/skills/audit-feedback-loop/SKILL.md +76 -0
  125. package/skills/edit-spec/SKILL.md +131 -0
  126. package/skills/enforce-rules-format/SKILL.md +71 -0
  127. package/skills/generate-logo/SKILL.md +102 -0
  128. package/skills/generate-rule/SKILL.md +90 -0
  129. package/skills/linter-docs/clippy.md +241 -0
  130. package/skills/linter-docs/eslint.md +384 -0
  131. package/skills/linter-docs/pylint.md +288 -0
  132. package/skills/linter-docs/rubocop.md +277 -0
  133. package/skills/linter-docs/ruff.md +187 -0
  134. package/skills/linter-docs/stylelint.md +247 -0
  135. package/skills/migrate-to-spec/SKILL.md +124 -0
  136. package/skills/pr-to-lint-rule/SKILL.md +97 -0
  137. package/skills/strengthen/SKILL.md +168 -0
  138. package/src/action.ts +214 -0
  139. package/src/cli.test.ts +914 -0
  140. package/src/cli.ts +1631 -0
  141. package/src/compile.ts +691 -0
  142. package/src/evolve.ts +781 -0
  143. package/src/freshness.test.ts +449 -0
  144. package/src/freshness.ts +299 -0
  145. package/src/generate-types.ts +448 -0
  146. package/src/inline.test.ts +206 -0
  147. package/src/inline.ts +164 -0
  148. package/src/linters.ts +739 -0
  149. package/src/proofs.test.ts +1314 -0
  150. package/src/proofs.ts +849 -0
  151. package/src/spec.test.ts +1471 -0
  152. package/src/spec.ts +427 -0
  153. package/src/types.ts +117 -0
  154. package/src/validate.test.ts +701 -0
  155. package/src/validate.ts +381 -0
  156. package/tsconfig.json +23 -0
@@ -0,0 +1,368 @@
1
+ # Executable Specification Files
2
+
3
+ Design document for vigiles v2's spec-driven compilation model. The spec is the source of truth. The markdown is a build artifact.
4
+
5
+ ---
6
+
7
+ ## Summary
8
+
9
+ vigiles v2 introduces `.spec.ts` files — TypeScript source files that compile to instruction files (CLAUDE.md, SKILL.md). TypeScript's type system catches errors at authoring time. vigiles validates everything else at build time. The annotation model from v1 becomes a compile-time guarantee: you can't create a rule without calling `enforce()`, `check()`, or `guidance()`.
10
+
11
+ ---
12
+
13
+ ## Core Concept
14
+
15
+ ```
16
+ CLAUDE.md.spec.ts → compiles to → CLAUDE.md
17
+ SKILL.md.spec.ts → compiles to → SKILL.md
18
+ ```
19
+
20
+ - `.claude/settings.json` — validated in place (no spec needed)
21
+ - `vigiles.config.ts` — optional project-level settings
22
+ - Generated files carry a hash comment for tamper detection
23
+
24
+ ---
25
+
26
+ ## Three Rule Types
27
+
28
+ | Type | Builder | What it means | vigiles responsibility |
29
+ | ------------- | ------------ | ----------------------------------------------------------------- | --------------------------------------------------- |
30
+ | **Delegated** | `enforce()` | Backed by external linter (ESLint, Ruff, Clippy, Pylint, RuboCop) | Verify rule exists and is enabled in project config |
31
+ | **Checked** | `check()` | vigiles-owned filesystem assertion (e.g., test file pairing) | Execute the assertion, fail build on violation |
32
+ | **Guidance** | `guidance()` | Prose-only, not mechanically enforced | Compile to `**Guidance only**` annotation |
33
+
34
+ Delegated rules reuse the existing linter cross-referencing engine and extend to additional tools (ast-grep, Dependency Cruiser, Steiger). Checked rules are scoped to filesystem assertions only — vigiles does NOT reimplement architectural linting. Guidance rules are pass-through.
35
+
36
+ ---
37
+
38
+ ## Spec API
39
+
40
+ ### `claude()` — CLAUDE.md spec
41
+
42
+ ```ts
43
+ import { claude, enforce, check, guidance, every } from "vigiles/spec";
44
+
45
+ export default claude({
46
+ commands: {
47
+ "npm run build": "Compile TypeScript to dist/",
48
+ "npm test": "Build and run all tests",
49
+ "npm run fmt": "Format with prettier",
50
+ },
51
+
52
+ keyFiles: {
53
+ "src/validate.ts": "Core validation engine",
54
+ "src/cli.ts": "CLI entry point",
55
+ "src/types.ts": "TypeScript type definitions",
56
+ },
57
+
58
+ sections: {
59
+ Architecture: `
60
+ TypeScript strict-mode codebase. Core engine in src/validate.ts.
61
+ `,
62
+ },
63
+
64
+ rules: {
65
+ "no-console-log-in-production": enforce(
66
+ "eslint/no-console",
67
+ "Use the structured logger which routes to Datadog.",
68
+ ),
69
+
70
+ "use-barrel-imports": enforce(
71
+ "eslint/no-restricted-imports",
72
+ "Prevents import path drift during refactoring.",
73
+ ),
74
+
75
+ "use-tailwind-spacing": guidance(
76
+ "Use spacing scale values (p-4, m-8) instead of arbitrary values.",
77
+ ),
78
+
79
+ "controllers-have-tests": check(
80
+ every("src/**/*.controller.ts").has("{name}.test.ts"),
81
+ "Every controller must have a co-located test file.",
82
+ ),
83
+ },
84
+ });
85
+ ```
86
+
87
+ Fields:
88
+
89
+ - `commands` — map of npm scripts/commands to descriptions. Verified against `package.json` at compile time.
90
+ - `keyFiles` — map of file paths to descriptions. Verified via `existsSync` at compile time.
91
+ - `sections` — map of section names to markdown prose. Pass-through.
92
+ - `rules` — map of rule IDs to `enforce()`, `check()`, or `guidance()` calls.
93
+
94
+ ### `skill()` — SKILL.md spec
95
+
96
+ ```ts
97
+ import { skill, file, cmd, ref } from "vigiles/spec";
98
+
99
+ export default skill({
100
+ name: "run-tests",
101
+ description: "Run the project test suite and report results",
102
+ argumentHint: "--filter <pattern>",
103
+ instructions: `
104
+ 1. Read ${file("jest.config.ts")} to understand the test setup
105
+ 2. Run ${cmd("npm test")} to execute the suite
106
+ 3. If tests fail, check ${ref("skills/debug/SKILL.md")} for debugging steps
107
+ `,
108
+ });
109
+ ```
110
+
111
+ Tagged template helpers:
112
+
113
+ - `file("path")` — verified file reference, compiles to backtick path. Fails if file doesn't exist.
114
+ - `cmd("npm test")` — verified command, compiles to backtick command. Checks `package.json` scripts.
115
+ - `ref("skills/other/SKILL.md")` — verified cross-reference. Fails if target doesn't exist.
116
+
117
+ ### Rule builders
118
+
119
+ ```ts
120
+ // Delegated — backed by external linter
121
+ enforce(linterRule: `${LinterName}/${string}`, why: string)
122
+
123
+ // Guidance — prose only
124
+ guidance(text: string)
125
+
126
+ // Proven — vigiles executes the check
127
+ check(assertion: Assertion, why: string)
128
+ ```
129
+
130
+ ### Proof assertions (future — ast-grep powered)
131
+
132
+ ```ts
133
+ // Every file matching glob has a corresponding file matching pattern
134
+ every("src/**/*.controller.ts").has("{name}.test.ts");
135
+
136
+ // No file matching glob contains the AST pattern
137
+ no("src/**/*.ts").matches("console.log($$$)");
138
+
139
+ // Import boundaries between layers
140
+ layers({
141
+ domain: { allow: [] },
142
+ service: { allow: ["domain"] },
143
+ handler: { allow: ["service", "domain"] },
144
+ });
145
+ ```
146
+
147
+ - `every(glob).has(pattern)` — structural file pairing
148
+ - `no(glob).matches(astPattern)` — absence proof via AST
149
+ - `layers({...})` — import boundary verification
150
+
151
+ ---
152
+
153
+ ## Template Literal Types
154
+
155
+ TypeScript template literal types provide compile-time safety:
156
+
157
+ ```ts
158
+ type LinterName =
159
+ | "eslint"
160
+ | "stylelint"
161
+ | "ruff"
162
+ | "clippy"
163
+ | "pylint"
164
+ | "rubocop";
165
+ type LinterRule = `${LinterName}/${string}`;
166
+
167
+ // Catches typos at authoring time
168
+ enforce("eslit/no-console", "...");
169
+ // ^^^^^ Type error: "eslit" is not assignable to LinterName
170
+
171
+ type HookEvent =
172
+ | "PreToolUse"
173
+ | "PostToolUse"
174
+ | "Notification"
175
+ | "Stop"
176
+ | "SubagentStop";
177
+
178
+ type ClaudeTool =
179
+ | "Read"
180
+ | "Write"
181
+ | "Edit"
182
+ | "Bash"
183
+ | "Glob"
184
+ | "Grep"
185
+ | "WebSearch"
186
+ | "WebFetch"
187
+ | "TodoWrite"
188
+ | "NotebookEdit";
189
+
190
+ // Catches tool name typos in hook matchers
191
+ const hook: Hook = {
192
+ event: "PostToolUse",
193
+ matcher: ["Edti"], // Type error
194
+ command: "npx prettier --check .",
195
+ };
196
+ ```
197
+
198
+ ---
199
+
200
+ ## Compilation
201
+
202
+ `vigiles compile` reads spec files and generates markdown.
203
+
204
+ ### Output format
205
+
206
+ Generated CLAUDE.md includes:
207
+
208
+ - Hash comment at top: `<!-- vigiles:sha256:HASH compiled from CLAUDE.md.spec.ts -->`
209
+ - `## Commands` section from `commands` map
210
+ - `## Key Files` section from `keyFiles` map
211
+ - Custom `## {name}` sections from `sections` map
212
+ - `## Rules` section with properly annotated rules under `###` headings
213
+
214
+ Generated SKILL.md includes:
215
+
216
+ - Hash comment at top
217
+ - YAML frontmatter (`name`, `description`, `argument_hint`)
218
+ - Instruction body with resolved `file()`, `cmd()`, `ref()` references
219
+
220
+ ### Compile-time checks
221
+
222
+ | Check | What fails |
223
+ | -------------------------------------------- | ---------------------- |
224
+ | `commands` key not in `package.json` scripts | Unknown command |
225
+ | `keyFiles` key doesn't exist on disk | Stale file reference |
226
+ | `file()` path doesn't exist | Broken file reference |
227
+ | `cmd()` script not in `package.json` | Unknown script |
228
+ | `ref()` target doesn't exist | Broken cross-reference |
229
+ | `enforce()` rule not found in linter catalog | Unknown linter rule |
230
+ | `enforce()` rule disabled in project config | Dead enforcement |
231
+ | `check()` assertion fails | Proof violation |
232
+ | Compiled output exceeds `max-lines` | File too long |
233
+
234
+ ---
235
+
236
+ ## Hash Verification
237
+
238
+ Generated files carry a SHA-256 hash of their content (excluding the hash line itself).
239
+
240
+ On `vigiles check`:
241
+
242
+ 1. Read the hash comment from the generated file
243
+ 2. Recompute hash of content (excluding hash line)
244
+ 3. If mismatch — file was manually edited after compilation
245
+ 4. Report: `CLAUDE.md was modified after compilation. Run vigiles compile to regenerate, or vigiles adopt to pull changes back.`
246
+
247
+ This closes the loop: specs are the source of truth, but manual edits are detected rather than silently lost.
248
+
249
+ ---
250
+
251
+ ## Commands
252
+
253
+ | Command | Purpose | Status |
254
+ | ------------------ | ------------------------------------------------------------- | ------- |
255
+ | `vigiles compile` | spec.ts files to .md files (with hash) | Phase 1 |
256
+ | `vigiles check` | Validate everything: specs, hashes, linter cross-refs, proofs | Phase 1 |
257
+ | `vigiles adopt` | Detect manual .md edits, help merge back into spec | Phase 4 |
258
+ | `vigiles discover` | Scan linter configs, report coverage gaps | Phase 3 |
259
+
260
+ Existing v1 commands (`vigiles validate`, `vigiles` with file args) continue to work unchanged.
261
+
262
+ ---
263
+
264
+ ## Integration with Existing Linters
265
+
266
+ vigiles does NOT replace linters. Three relationships:
267
+
268
+ 1. **Verify** — `enforce("eslint/no-console")` checks that the rule exists and is enabled. This is the existing `require-rule-file` behavior, moved to compile time.
269
+ 2. **Fill gaps** — `check()` handles properties no single linter can express: cross-file pairing, import boundaries, AST absence across a codebase. Powered by ast-grep.
270
+ 3. **Recommend** — if a `check()` assertion could be expressed as an existing linter rule, suggest delegating. Example: `no("src/**/*.ts").matches("console.log($$$)")` could be `enforce("eslint/no-console")`.
271
+
272
+ ---
273
+
274
+ ## What Replaces What
275
+
276
+ | v1 Rule | v2 Equivalent | Why |
277
+ | --------------------- | -------------------------------------------------------------------- | --------------------------------------------- |
278
+ | `require-annotations` | Type system — can't create rule without `enforce`/`guidance`/`prove` | Compile-time guarantee replaces runtime check |
279
+ | `max-lines` | Build constraint on compiled output | Constrain the artifact, not the source |
280
+ | `require-rule-file` | `enforce()` verification during compile | Same check, earlier in the pipeline |
281
+ | `require-structure` | Compiler generates correct structure | We control the output format |
282
+ | `no-broken-links` | `file()` and `ref()` verified at compile | Typed references replace regex scanning |
283
+
284
+ v1 validation remains available for projects that don't adopt specs. No breaking changes.
285
+
286
+ ---
287
+
288
+ ## Migration from v1
289
+
290
+ - `vigiles init --from-claude-md` parses an existing CLAUDE.md and generates a `.spec.ts` file
291
+ - `**Enforced by:** eslint/X` lines become `enforce("eslint/X", "...")`
292
+ - `**Guidance only**` lines become `guidance("...")`
293
+ - Commands, key files, and sections are extracted into the appropriate fields
294
+ - Existing `.vigilesrc.json` still works — v1 validation is unchanged
295
+ - Specs are opt-in — adoption is incremental, not a flag day
296
+
297
+ ---
298
+
299
+ ## Implementation Strategy
300
+
301
+ ### Phase 1: Foundation
302
+
303
+ - Type definitions for spec system (`claude()`, `skill()`, `enforce()`, `guidance()`, `check()`)
304
+ - Builder functions with TypeScript template literal types
305
+ - Compiler: spec to markdown with hash
306
+ - `vigiles compile` and `vigiles check` commands
307
+ - `vigiles init --from-claude-md` migration tool
308
+
309
+ ### Phase 2: Proofs
310
+
311
+ - ast-grep integration via `@ast-grep/napi`
312
+ - `every(glob).has(pattern)` — filesystem assertions
313
+ - `no(glob).matches(astPattern)` — AST pattern absence
314
+ - `layers({...})` — import graph analysis
315
+
316
+ ### Phase 3: Discovery
317
+
318
+ - `vigiles discover` — scan linter configs, report coverage gaps
319
+ - Suggest undocumented rules that agents trip on frequently
320
+ - Reverse coverage: which enabled linter rules have no corresponding spec entry
321
+
322
+ ### Phase 4: Adopt
323
+
324
+ - `vigiles adopt` — detect manual edits via hash mismatch
325
+ - Diff analysis to suggest spec updates
326
+ - Interactive merge workflow
327
+
328
+ ---
329
+
330
+ ## Technology Choices
331
+
332
+ | Choice | Why | Alternatives considered |
333
+ | ------------------------------------ | ----------------------------------------------------------------------------- | --------------------------------------------------------------------------- |
334
+ | **ast-grep** (`@ast-grep/napi`) | 26 languages, Rust-fast, Node.js API, pattern syntax is readable | tree-sitter raw (too low-level), Semgrep (can't embed, restrictive license) |
335
+ | **TypeScript specs** (not YAML/JSON) | Full type system, template literal types, IDE autocomplete, conditional logic | YAML (no type safety), JSON (no comments, no logic) |
336
+ | **SHA-256 hash** | Fast, built into Node.js crypto, collision-resistant | MD5 (deprecated), content-addressed CAS (overengineered) |
337
+ | **Compile-to-markdown** | Agents read markdown natively, no runtime dependency on vigiles | Custom binary format (agents can't read), JSON (poor agent UX) |
338
+
339
+ ### Future considerations
340
+
341
+ - **ESLint v10 inline plugins** — potential target for generating ESLint rules from `check()` assertions
342
+ - **TypeSpec emitter pattern** — one source, multiple outputs (CLAUDE.md + .cursorrules + AGENTS.md from single spec)
343
+ - **Pulumi model** — TypeScript as the authoring language for declarative output is a proven pattern
344
+
345
+ ---
346
+
347
+ ## Research Sources
348
+
349
+ | Source | Key insight |
350
+ | ---------------------------------------------------------- | ---------------------------------------------------------------------------------- |
351
+ | AST hallucination detection (arxiv 2601.19106) | ast-grep achieves 100% precision, 87.6% recall for detecting AST-inconsistent code |
352
+ | Factory.ai: "Agents write the code; linters write the law" | Linters direct agents, not the reverse. vigiles is the bridge |
353
+ | Martin Fowler: "Harness Engineering" (Feb 2026) | The harness (hooks, rules, CI) is as important as the agent itself |
354
+ | ETH Zurich: LLM-generated instruction files | Reduce success by 3%, increase cost by 20%. Over 50% of rules were noise |
355
+ | Spotify Honk | 650+ agent PRs/month with strong feedback loops. Linter enforcement is critical |
356
+ | CodeRabbit | AI-generated code has 1.7x more issues than human code. More enforcement, not less |
357
+ | TypeSpec emitter pattern | One source of truth, multiple output formats. Proven at Azure scale |
358
+ | Pulumi | TypeScript to declarative output. Developer ergonomics matter |
359
+
360
+ ---
361
+
362
+ ## Open Questions
363
+
364
+ 1. **Spec file discovery** — should `vigiles compile` scan for `*.spec.ts` recursively, or require explicit listing in `vigiles.config.ts`?
365
+ 2. **Proof caching** — ast-grep scans can be expensive. Cache proof results and invalidate on file change? Or always re-run?
366
+ 3. **Multi-file specs** — can one spec import from another? Shared rule sets across teams?
367
+ 4. **Watch mode** — `vigiles compile --watch` for development? Or rely on editor save hooks?
368
+ 5. **Error recovery** — if one `enforce()` rule is unknown, should the whole compile fail or emit a warning annotation?