woods 1.6.0 → 2.0.0.beta1

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 (273) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1908 -0
  3. data/CONTRIBUTING.md +202 -87
  4. data/README.md +161 -513
  5. data/SECURITY.md +92 -0
  6. data/assets/woods-wordmark-white-with-bg.png +0 -0
  7. data/docs/AGENT_GUIDE.md +204 -0
  8. data/docs/AGENT_SETUP.md +205 -0
  9. data/docs/BACKEND_MATRIX.md +470 -0
  10. data/docs/CONFIGURATION_REFERENCE.md +620 -0
  11. data/docs/CONSOLE_MCP_SETUP.md +829 -0
  12. data/docs/DOCKER_SETUP.md +454 -0
  13. data/docs/EMBEDDING_MODELS.md +136 -0
  14. data/docs/EVALUATION.md +91 -0
  15. data/docs/EXTRACTOR_REFERENCE.md +765 -0
  16. data/docs/FAQ.md +544 -0
  17. data/docs/GETTING_STARTED.md +183 -0
  18. data/docs/INCREMENTAL_EXTRACTION.md +415 -0
  19. data/docs/INTERNALS.md +415 -0
  20. data/docs/MCP_HTTP_TRANSPORT.md +144 -0
  21. data/docs/MCP_SERVERS.md +231 -0
  22. data/docs/MCP_TOOL_COOKBOOK.md +987 -0
  23. data/docs/MCP_WORKTREE_SETUP.md +127 -0
  24. data/docs/NOTION_INTEGRATION.md +283 -0
  25. data/docs/OBSIDIAN_INTEGRATION.md +170 -0
  26. data/docs/PUBLISHED_INDEX.md +197 -0
  27. data/docs/README.md +94 -0
  28. data/docs/RETRIEVAL_GUIDE.md +267 -0
  29. data/docs/TOKEN_BENCHMARK.md +68 -0
  30. data/docs/TROUBLESHOOTING.md +841 -0
  31. data/docs/UNBLOCKED_INTEGRATION.md +279 -0
  32. data/docs/UPGRADING_TO_2.md +321 -0
  33. data/docs/WATCH_DAEMON.md +667 -0
  34. data/docs/WHY_WOODS.md +219 -0
  35. data/exe/woods-console +40 -4
  36. data/exe/woods-console-mcp +21 -35
  37. data/exe/woods-mcp +20 -7
  38. data/exe/woods-mcp-http +80 -11
  39. data/exe/woods-mcp-start +57 -52
  40. data/lib/generators/woods/install_generator.rb +6 -5
  41. data/lib/generators/woods/pgvector_generator.rb +6 -3
  42. data/lib/generators/woods/templates/add_pgvector_to_woods.rb.erb +29 -9
  43. data/lib/generators/woods/templates/create_woods_tables.rb.erb +5 -1
  44. data/lib/generators/woods/templates/woods.rb.tt +49 -28
  45. data/lib/tasks/woods.rake +622 -168
  46. data/lib/tasks/woods_checks.rake +107 -0
  47. data/lib/tasks/woods_evaluation.rake +164 -80
  48. data/lib/woods/ast/call_site_extractor.rb +6 -15
  49. data/lib/woods/ast/method_extractor.rb +19 -9
  50. data/lib/woods/ast/parser.rb +54 -8
  51. data/lib/woods/atomic_file.rb +40 -1
  52. data/lib/woods/builder.rb +310 -22
  53. data/lib/woods/cache/cache_middleware.rb +7 -2
  54. data/lib/woods/cache/cache_store.rb +9 -1
  55. data/lib/woods/cache/solid_cache_store.rb +6 -4
  56. data/lib/woods/change_set.rb +88 -0
  57. data/lib/woods/checks/generation_resolution.rb +34 -0
  58. data/lib/woods/checks/moved_messages.rb +186 -0
  59. data/lib/woods/chunking/semantic_chunker.rb +160 -18
  60. data/lib/woods/console/audit_logger.rb +12 -3
  61. data/lib/woods/console/bridge_protocol.rb +3 -16
  62. data/lib/woods/console/connection_manager.rb +51 -136
  63. data/lib/woods/console/dispatch_pipeline.rb +42 -12
  64. data/lib/woods/console/embedded_executor.rb +806 -149
  65. data/lib/woods/console/eval_guard.rb +27 -20
  66. data/lib/woods/console/input_contract.rb +78 -0
  67. data/lib/woods/console/model_validator.rb +29 -1
  68. data/lib/woods/console/rack_middleware.rb +65 -42
  69. data/lib/woods/console/redactor.rb +26 -8
  70. data/lib/woods/console/safe_context.rb +58 -10
  71. data/lib/woods/console/scope_predicate_parser.rb +41 -0
  72. data/lib/woods/console/server.rb +119 -247
  73. data/lib/woods/console/sql_noise_stripper.rb +125 -16
  74. data/lib/woods/console/sql_table_scanner.rb +82 -22
  75. data/lib/woods/console/sql_validator.rb +459 -29
  76. data/lib/woods/console/table_gate.rb +2 -2
  77. data/lib/woods/console/tool_specs.rb +463 -90
  78. data/lib/woods/console/tools/tier1.rb +1 -5
  79. data/lib/woods/console/tools/tier4.rb +18 -9
  80. data/lib/woods/coordination/lock_heartbeat.rb +103 -0
  81. data/lib/woods/coordination/pipeline_lock.rb +263 -53
  82. data/lib/woods/db/migrations/007_typed_snapshot_units.rb +45 -0
  83. data/lib/woods/db/migrator.rb +3 -9
  84. data/lib/woods/db/schema_version.rb +47 -2
  85. data/lib/woods/dependency_graph.rb +898 -64
  86. data/lib/woods/embedding/fake.rb +138 -0
  87. data/lib/woods/embedding/indexer.rb +832 -40
  88. data/lib/woods/embedding/openai.rb +77 -19
  89. data/lib/woods/embedding/provider.rb +189 -11
  90. data/lib/woods/embedding/text_preparer.rb +1 -1
  91. data/lib/woods/embedding/token_counter.rb +0 -7
  92. data/lib/woods/evaluation/ablation_agent_payload.rb +38 -0
  93. data/lib/woods/evaluation/ablation_executor.rb +67 -0
  94. data/lib/woods/evaluation/ablation_provenance.rb +38 -0
  95. data/lib/woods/evaluation/ablation_report_writer.rb +43 -0
  96. data/lib/woods/evaluation/ablation_runner.rb +173 -0
  97. data/lib/woods/evaluation/ablation_summary.rb +65 -0
  98. data/lib/woods/evaluation/ablation_task.rb +66 -0
  99. data/lib/woods/evaluation/ablation_task_set.rb +77 -0
  100. data/lib/woods/evaluation/ablation_timed_executor.rb +91 -0
  101. data/lib/woods/evaluation/ablation_worktree.rb +71 -0
  102. data/lib/woods/evaluation/baseline.rb +60 -0
  103. data/lib/woods/evaluation/baseline_runner.rb +11 -3
  104. data/lib/woods/evaluation/evaluator.rb +41 -8
  105. data/lib/woods/evaluation/query_set.rb +79 -13
  106. data/lib/woods/evaluation/report_generator.rb +20 -1
  107. data/lib/woods/export/unit_facts.rb +0 -11
  108. data/lib/woods/extracted_unit.rb +22 -63
  109. data/lib/woods/extractor.rb +2509 -189
  110. data/lib/woods/extractors/action_cable_extractor.rb +9 -4
  111. data/lib/woods/extractors/ast_source_extraction.rb +20 -2
  112. data/lib/woods/extractors/caching_extractor.rb +46 -12
  113. data/lib/woods/extractors/callback_analyzer.rb +39 -9
  114. data/lib/woods/extractors/component_discovery.rb +123 -0
  115. data/lib/woods/extractors/concern_extractor.rb +17 -3
  116. data/lib/woods/extractors/controller_extractor.rb +389 -29
  117. data/lib/woods/extractors/decorator_extractor.rb +7 -14
  118. data/lib/woods/extractors/engine_extractor.rb +53 -8
  119. data/lib/woods/extractors/event_extractor.rb +55 -4
  120. data/lib/woods/extractors/factory_extractor.rb +49 -11
  121. data/lib/woods/extractors/graphql_extractor.rb +162 -66
  122. data/lib/woods/extractors/i18n_extractor.rb +6 -1
  123. data/lib/woods/extractors/job_extractor.rb +51 -21
  124. data/lib/woods/extractors/lib_extractor.rb +23 -17
  125. data/lib/woods/extractors/line_neutralizer.rb +171 -0
  126. data/lib/woods/extractors/mailer_extractor.rb +9 -1
  127. data/lib/woods/extractors/manager_extractor.rb +19 -2
  128. data/lib/woods/extractors/migration_extractor.rb +22 -11
  129. data/lib/woods/extractors/model_extractor.rb +292 -57
  130. data/lib/woods/extractors/package_extractor.rb +154 -0
  131. data/lib/woods/extractors/phlex_extractor.rb +18 -3
  132. data/lib/woods/extractors/policy_extractor.rb +6 -5
  133. data/lib/woods/extractors/poro_extractor.rb +13 -14
  134. data/lib/woods/extractors/pundit_extractor.rb +3 -3
  135. data/lib/woods/extractors/rails_source_extractor.rb +24 -7
  136. data/lib/woods/extractors/rake_task_extractor.rb +158 -30
  137. data/lib/woods/extractors/reference_patterns.rb +38 -0
  138. data/lib/woods/extractors/route_extractor.rb +58 -2
  139. data/lib/woods/extractors/scheduled_job_extractor.rb +51 -35
  140. data/lib/woods/extractors/serializer_extractor.rb +3 -4
  141. data/lib/woods/extractors/service_extractor.rb +11 -1
  142. data/lib/woods/extractors/shared_dependency_scanner.rb +24 -34
  143. data/lib/woods/extractors/shared_utility_methods.rb +36 -6
  144. data/lib/woods/extractors/source_nesting.rb +560 -0
  145. data/lib/woods/extractors/state_machine_extractor.rb +30 -18
  146. data/lib/woods/extractors/test_mapping_extractor.rb +26 -9
  147. data/lib/woods/extractors/view_component_extractor.rb +28 -3
  148. data/lib/woods/extractors/view_engines/erb.rb +17 -3
  149. data/lib/woods/feedback/gap_detector.rb +9 -3
  150. data/lib/woods/feedback/store.rb +7 -1
  151. data/lib/woods/filename_utils.rb +29 -1
  152. data/lib/woods/flow_analysis/operation_extractor.rb +22 -10
  153. data/lib/woods/flow_assembler.rb +63 -21
  154. data/lib/woods/flow_document.rb +1 -0
  155. data/lib/woods/flow_precomputer.rb +138 -22
  156. data/lib/woods/gem_mapper.rb +285 -0
  157. data/lib/woods/generation.rb +185 -0
  158. data/lib/woods/git_command.rb +38 -0
  159. data/lib/woods/git_provenance.rb +16 -2
  160. data/lib/woods/graph_analyzer.rb +408 -34
  161. data/lib/woods/index_artifact.rb +93 -23
  162. data/lib/woods/mcp/bearer_auth.rb +102 -13
  163. data/lib/woods/mcp/bootstrap_state.rb +77 -0
  164. data/lib/woods/mcp/bootstrapper.rb +582 -77
  165. data/lib/woods/mcp/config_resolver.rb +66 -6
  166. data/lib/woods/mcp/errors.rb +60 -0
  167. data/lib/woods/mcp/index_reader.rb +836 -117
  168. data/lib/woods/mcp/index_reader_pinning.rb +78 -0
  169. data/lib/woods/mcp/origin_guard.rb +66 -7
  170. data/lib/woods/mcp/protocol_policy.rb +98 -0
  171. data/lib/woods/mcp/provider_probe.rb +45 -6
  172. data/lib/woods/mcp/renderers/markdown_renderer.rb +72 -4
  173. data/lib/woods/mcp/renderers/plain_renderer.rb +54 -6
  174. data/lib/woods/mcp/server.rb +898 -152
  175. data/lib/woods/mcp/tasks/extension.rb +196 -0
  176. data/lib/woods/mcp/tasks/request_capture.rb +45 -0
  177. data/lib/woods/mcp/tasks/store.rb +518 -0
  178. data/lib/woods/mcp/tool_contract.rb +171 -0
  179. data/lib/woods/mcp/tool_response_renderer.rb +7 -0
  180. data/lib/woods/model_name_cache.rb +19 -1
  181. data/lib/woods/notion/client.rb +132 -36
  182. data/lib/woods/notion/exporter.rb +456 -61
  183. data/lib/woods/notion/mappers/column_mapper.rb +34 -5
  184. data/lib/woods/notion/mappers/migration_mapper.rb +32 -8
  185. data/lib/woods/notion/mappers/model_mapper.rb +21 -6
  186. data/lib/woods/notion/mappers/shared.rb +45 -3
  187. data/lib/woods/notion/sync_manifest.rb +258 -0
  188. data/lib/woods/obsidian/errors.rb +6 -0
  189. data/lib/woods/obsidian/name_mapper.rb +40 -24
  190. data/lib/woods/obsidian/vault_exporter.rb +103 -36
  191. data/lib/woods/operator/pipeline_guard.rb +118 -21
  192. data/lib/woods/operator/status_reporter.rb +20 -3
  193. data/lib/woods/path_dispatcher.rb +276 -0
  194. data/lib/woods/payload_store.rb +223 -0
  195. data/lib/woods/published_index/edge_shaper.rb +61 -0
  196. data/lib/woods/published_index/generation_catalog.rb +72 -0
  197. data/lib/woods/published_index/typed_unit_reader.rb +48 -0
  198. data/lib/woods/published_index.rb +287 -0
  199. data/lib/woods/railtie.rb +69 -30
  200. data/lib/woods/railtie_support.rb +167 -0
  201. data/lib/woods/release.rb +12 -0
  202. data/lib/woods/reload_policy.rb +206 -0
  203. data/lib/woods/resilience/circuit_breaker.rb +47 -8
  204. data/lib/woods/resilience/index_validator.rb +296 -10
  205. data/lib/woods/resilience/retryable_provider.rb +71 -6
  206. data/lib/woods/resolved_config.rb +55 -11
  207. data/lib/woods/retrieval/context_assembler.rb +132 -40
  208. data/lib/woods/retrieval/query_classifier.rb +26 -8
  209. data/lib/woods/retrieval/ranker.rb +193 -28
  210. data/lib/woods/retrieval/search_executor.rb +206 -39
  211. data/lib/woods/retriever.rb +317 -71
  212. data/lib/woods/retry_after.rb +22 -2
  213. data/lib/woods/ruby_analyzer/class_analyzer.rb +10 -14
  214. data/lib/woods/ruby_analyzer/fqn_builder.rb +2 -0
  215. data/lib/woods/ruby_analyzer/mermaid_renderer.rb +14 -4
  216. data/lib/woods/ruby_analyzer/method_analyzer.rb +1 -1
  217. data/lib/woods/ruby_analyzer.rb +21 -5
  218. data/lib/woods/session_tracer/file_store.rb +138 -19
  219. data/lib/woods/session_tracer/middleware.rb +1 -2
  220. data/lib/woods/session_tracer/redis_store.rb +122 -12
  221. data/lib/woods/session_tracer/session_flow_assembler.rb +57 -17
  222. data/lib/woods/session_tracer/session_flow_document.rb +56 -14
  223. data/lib/woods/session_tracer/solid_cache_coordination.rb +192 -0
  224. data/lib/woods/session_tracer/solid_cache_store.rb +560 -91
  225. data/lib/woods/session_tracer/store.rb +14 -1
  226. data/lib/woods/storage/metadata_store.rb +230 -26
  227. data/lib/woods/storage/pgvector.rb +180 -22
  228. data/lib/woods/storage/qdrant.rb +367 -41
  229. data/lib/woods/storage/snapshotter/metadata.rb +79 -16
  230. data/lib/woods/storage/snapshotter/vector.rb +128 -17
  231. data/lib/woods/storage/snapshotter.rb +23 -5
  232. data/lib/woods/storage/vector_store.rb +49 -8
  233. data/lib/woods/storage_identity.rb +28 -0
  234. data/lib/woods/tasks.rb +53 -2
  235. data/lib/woods/temporal/json_snapshot_store.rb +112 -42
  236. data/lib/woods/temporal/snapshot_store.rb +139 -42
  237. data/lib/woods/unblocked/client.rb +119 -17
  238. data/lib/woods/unblocked/document_builder.rb +34 -2
  239. data/lib/woods/unblocked/exporter.rb +63 -27
  240. data/lib/woods/unblocked/rate_limiter.rb +23 -9
  241. data/lib/woods/unblocked/sync_manifest.rb +16 -8
  242. data/lib/woods/update_check.rb +24 -1
  243. data/lib/woods/util/uuid5.rb +124 -0
  244. data/lib/woods/version.rb +1 -1
  245. data/lib/woods/watch/daemon.rb +1345 -0
  246. data/lib/woods/watch/listen_watcher.rb +81 -0
  247. data/lib/woods/watch/polling_watcher.rb +137 -0
  248. data/lib/woods/watch/status.rb +169 -0
  249. data/lib/woods/watch/tree_scan.rb +163 -0
  250. data/lib/woods/watch/watcher.rb +100 -0
  251. data/lib/woods.rb +53 -9
  252. data/plugin/.claude-plugin/plugin.json +18 -0
  253. data/plugin/hooks/hooks.json +29 -0
  254. data/plugin/hooks/woods-post-edit.sh +226 -0
  255. data/plugin/hooks/woods-session-start.sh +77 -0
  256. data/plugin/skills/woods-agent-enable/SKILL.md +51 -0
  257. data/plugin/skills/woods-diagnose/SKILL.md +75 -0
  258. data/plugin/skills/woods-investigate/SKILL.md +39 -0
  259. data/plugin/skills/woods-mcp-config/SKILL.md +101 -0
  260. data/plugin/skills/woods-setup/SKILL.md +99 -0
  261. metadata +134 -23
  262. data/lib/woods/console/adapters/cache_adapter.rb +0 -58
  263. data/lib/woods/console/adapters/good_job_adapter.rb +0 -33
  264. data/lib/woods/console/adapters/job_adapter.rb +0 -74
  265. data/lib/woods/console/adapters/sidekiq_adapter.rb +0 -33
  266. data/lib/woods/console/adapters/solid_queue_adapter.rb +0 -33
  267. data/lib/woods/console/bridge.rb +0 -210
  268. data/lib/woods/formatting/claude_adapter.rb +0 -98
  269. data/lib/woods/formatting/generic_adapter.rb +0 -56
  270. data/lib/woods/formatting/gpt_adapter.rb +0 -64
  271. data/lib/woods/notion/mapper.rb +0 -40
  272. data/lib/woods/observability/health_check.rb +0 -79
  273. data/lib/woods/observability/instrumentation.rb +0 -34
data/CONTRIBUTING.md CHANGED
@@ -1,127 +1,242 @@
1
1
  # Contributing to Woods
2
2
 
3
- Thank you for your interest in contributing to Woods!
3
+ <!-- release-state:contributing-intro -->
4
+ Woods welcomes bug fixes, extractor coverage, storage and retrieval improvements, MCP compatibility work, documentation, and focused performance changes. This guide covers the shared contribution contract. Coding agents working from a source checkout should also read the repository's [AGENTS.md](https://github.com/lost-in-the/woods/blob/v2.0.0.beta1/AGENTS.md).
5
+ <!-- release-state:end -->
4
6
 
5
- ## Bug Reports
7
+ ## Choose the right channel
6
8
 
7
- Please open an issue on GitHub with:
9
+ - **Bug:** open an issue with reproduction steps, expected and actual behavior, Woods/Ruby/Rails versions, database adapter, and the smallest useful log or stack trace.
10
+ - **Feature:** describe the user problem, intended outcome, alternatives considered, and affected extraction/MCP/storage surfaces.
11
+ - **Security issue:** do not open a public issue. Follow [SECURITY.md](SECURITY.md).
12
+ - **Question or documentation gap:** open an issue and point to the page or workflow that was unclear.
8
13
 
9
- - A clear description of the bug
10
- - Steps to reproduce
11
- - Expected vs. actual behavior
12
- - Your Ruby version, Rails version, and database adapter
14
+ Search existing issues and pull requests first. A minimal reproduction in a small Rails app is more useful than a large application dump; never attach secrets or production data.
13
15
 
14
- ## Feature Requests
16
+ ## Development setup
15
17
 
16
- Open an issue describing:
18
+ Prerequisites are Git, Ruby 3.0 or later, and a Bundler version compatible with that Ruby. The repository tests several Ruby versions and intentionally does not pin one local version; select a supported Ruby with your normal version manager, then confirm `ruby --version` and `bundle --version`. `bin/setup` installs the bundle but does not install or select Ruby.
17
19
 
18
- - The problem you're trying to solve
19
- - Your proposed solution
20
- - Any alternatives you've considered
20
+ ```bash
21
+ git clone https://github.com/lost-in-the/woods.git
22
+ cd woods
23
+ bin/setup
24
+ bin/rake spec
25
+ bin/rubocop
26
+ ```
27
+
28
+ Create a branch from current `main`. Keep each pull request to one logical change and preserve unrelated formatting and refactors for separate work.
21
29
 
22
- ## Pull Requests
30
+ `main` is the development branch: it holds work for the next release and can run ahead of the latest published gem. Releases are cut from version tags by the guarded workflow in the [release section below](#release-flow); documentation matching a published gem lives on that release's tag.
23
31
 
24
- 1. Fork the repo and create your branch from `main`
25
- 2. Install dependencies: `bin/setup`
26
- 3. Make your changes
27
- 4. Add tests for new functionality
28
- 5. Ensure the test suite passes: `bundle exec rake spec`
29
- 6. Ensure code style passes: `bundle exec rubocop`
30
- 7. Update CHANGELOG.md with your changes
31
- 8. Complete the **Pre-PR requirements** below
32
- 9. Open a pull request
32
+ ## Understand the repository
33
33
 
34
- ### Pre-PR requirements
34
+ | Path | Responsibility |
35
+ |---|---|
36
+ | `lib/woods/extractor.rb`, `lib/woods/extractors/` | Runtime Rails extraction pipeline and extractors |
37
+ | `lib/woods/mcp/` | Read-only Index MCP server and protocol behavior |
38
+ | `lib/woods/console/` | Live Rails Console MCP and safeguards |
39
+ | `lib/woods/storage/`, `lib/woods/embedding/`, `lib/woods/retrieval/` | Persistence, vectors, and semantic retrieval |
40
+ | `lib/tasks/` | Rails/Rake operational interface |
41
+ | `spec/` | Unit, contract, and opt-in integration specs |
42
+ | `spec/dummy/` | Booted Rails fixture application |
43
+ | `docs/` | User, agent, operational, and reference documentation |
44
+ | `plugin/skills/` | Distributed Woods skills (setup/upgrade, MCP configuration, investigation, agent enablement, diagnosis) |
35
45
 
36
- These are hard gates — a PR that fails either is incomplete:
46
+ <!-- release-state:contributing-architecture -->
47
+ Read [CLAUDE.md](https://github.com/lost-in-the/woods/blob/v2.0.0.beta1/CLAUDE.md) for architecture and implementation gotchas before changing runtime behavior.
48
+ <!-- release-state:end -->
37
49
 
38
- 1. **Documentation must be current.** Any doc affected by the change — README, `docs/`, the
39
- `plugin/skills/` user guides, `CHANGELOG.md` — must be updated in the *same* PR. Don't ship
40
- behavior the docs still describe the old way.
41
- 2. **Investigate plugin-functionality impact.** If the change touches anything the distributed
42
- user skills rely on — a rake task, MCP tool or its arguments, an executable (`woods-mcp`,
43
- `woods-mcp-start`, `woods-console-mcp`, `woods-mcp-http`), a config key, or setup steps —
44
- investigate whether `plugin/skills/{woods-setup,woods-mcp-config,woods-diagnose}` need to
45
- change.
50
+ ### Agent orientation and static self-map
46
51
 
47
- ### Claude Code plugin changes
52
+ When investigating Woods itself, agents can create a disposable, MCP-queryable
53
+ map of the gem source before planning a broad change or debugging a cross-cutting
54
+ problem:
48
55
 
49
- `plugin/` is distributed as the `woods-plugin` via the
50
- [`lost-in-the/plugins`](https://github.com/lost-in-the/plugins) marketplace (a `git-subdir`
51
- reference to this subtree). Installed users may run an **older** gem than `main`, so:
56
+ ```bash
57
+ output_dir="$(mktemp -d)"
58
+ bin/rake "woods:self_map[$output_dir]"
59
+ bundle exec woods-mcp-start "$output_dir"
60
+ ```
52
61
 
53
- - If a change adds/removes/renames a tool, task, executable, or config key that a skill
54
- documents, **update the skill in the same PR**.
55
- - The skills carry a Version Preflight (operate only against the installed version). **Land the
56
- skill change with the release that ships the capability** — never document a feature in a
57
- skill before the version that provides it is released. Bump `plugin/.claude-plugin/plugin.json`
58
- `version` when the skill content changes.
59
- - If the change requires a new marketplace entry, `ref` pin, or metadata edit, open a **paired
60
- PR against `lost-in-the/plugins`** and link it from this PR.
62
+ This internal developer task publishes an atomic standard index generation.
63
+ Use `woods_status`, `structure`, `search`, `lookup`, `dependencies`, and
64
+ `dependents` to identify ownership and estimate the static blast radius. The
65
+ map is Woods-only, has no embeddings, and must remain out of version control.
66
+ It is not a replacement for booted Rails extraction or evidence of runtime
67
+ Rails behavior; use the normal host-app pipeline for that.
61
68
 
62
- ## Development Setup
69
+ ## Make the change
70
+
71
+ 1. Reproduce a bug or define the expected behavior.
72
+ 2. Add or update the smallest test that can fail for the behavior.
73
+ 3. Make the targeted implementation change.
74
+ 4. Run the narrow test, then the relevant broader suite.
75
+ 5. Update the canonical documentation, plugin skill, and changelog when the public contract changes.
76
+ 6. Review the complete diff before opening a pull request.
77
+
78
+ Woods extracts Rails behavior through a booted runtime. Features that depend on routes, Active Record reflections, descendants, or framework internals must use runtime introspection. Unit tests may isolate collaborators, but version-sensitive behavior also needs the booted-app lane.
79
+
80
+ ## Validate in proportion to the change
81
+
82
+ Start with the smallest command that exercises your work:
63
83
 
64
84
  ```bash
65
- git clone https://github.com/lost-in-the/woods.git
66
- cd woods
67
- bin/setup
68
- bundle exec rake spec # Run tests
69
- bundle exec rubocop # Check style
70
- ```
85
+ # One spec file
86
+ bin/rspec spec/path/to/spec.rb
71
87
 
72
- ## Testing
88
+ # Unit/contract suite (booted-app and live-backend lanes excluded)
89
+ bin/rake spec
73
90
 
74
- Woods has two test suites:
91
+ # Style
92
+ bin/rubocop
93
+ ```
75
94
 
76
- - **Gem unit specs** (`spec/`): Run with `bundle exec rake spec`. No Rails boot required.
77
- - **Integration specs**: Run inside a host Rails app to test real extraction.
95
+ Before requesting review, run the full unit suite and style check unless the PR explains why one cannot run.
78
96
 
79
- All new features need tests. Bug fixes should include a regression test.
97
+ Coverage from the default process excludes opt-in Rails, installed-artifact, and live-backend lanes. Report their results separately; a low percentage for subprocess-driven tasks does not establish that they are untested. CI enforces the aggregate line floor and measures branches, but does not enforce a branch floor. Add behavior-based regressions and real optional-gem fixtures for changed extraction paths before proposing higher thresholds.
80
98
 
81
99
  ### Rails version matrix
82
100
 
83
- The gem supports `railties >= 6.0`. Coverage is split across two CI jobs:
101
+ The gem supports Ruby 3.0 or later and Rails 6.0 through 8.x. CI separates fast unit coverage from real Rails boots:
84
102
 
85
- - The base unit `test` job runs `rake spec` across **Ruby 3.0–4.0** on the
86
- default (newest) Rails. The unit specs stub Rails, so they run once on the base
87
- Gemfile rather than per Rails version.
88
- - The `rails-matrix` job runs the **booted-app extraction test**
89
- (`spec/integration/booted_extraction_spec.rb` against `spec/dummy`) under each
90
- supported Rails — 6.0, 6.1, 7.0, 7.1, 7.2, 8.0 — using per-version gemfiles
91
- under `gemfiles/`. This is the version-sensitive gate: it boots a real Rails
92
- app in-process and runs an extraction. (The booted spec is tagged `:booted_app`
93
- and excluded from the default `rake spec`; `WOODS_RUN_BOOTED_APP=1` opts it in,
94
- and it must run in its own process — it can't share one with the unit suite.)
103
+ - the base test job runs unit specs across supported Ruby versions;
104
+ - the `rails-matrix` job boots `spec/dummy` and performs extraction for each supported Rails line using `gemfiles/rails_*.gemfile`.
95
105
 
96
- The Rails pins live in `Appraisals`; the gemfiles are hand-maintained
97
- (`eval_gemfile`-ing the base `Gemfile` and pinning Rails) because Appraisal can't
98
- generate from the conditional base Gemfile. To run a single Rails row locally:
106
+ Run one Rails row locally:
99
107
 
100
108
  ```bash
101
109
  BUNDLE_GEMFILE=gemfiles/rails_7.2.gemfile bundle install
102
110
  WOODS_RUN_BOOTED_APP=1 BUNDLE_GEMFILE=gemfiles/rails_7.2.gemfile \
103
- bundle exec rspec spec/integration/booted_extraction_spec.rb
111
+ bin/rspec spec/integration/booted_extraction_spec.rb
112
+ ```
113
+
114
+ When adding a Rails line, update `Appraisals`, the corresponding hand-maintained gemfile, and `.github/workflows/ci.yml`. For Rails below 7.1, copy an existing 6.x gemfile so its sqlite3 and concurrent-ruby compatibility pins are preserved.
115
+
116
+ ### Live storage and SQL dialects
117
+
118
+ The opt-in `live-backends` lane verifies behavior against PostgreSQL/pgvector and Qdrant that doubles cannot prove, including batch conflicts, delete addressing, filter translation, and extension setup.
119
+
120
+ ```bash
121
+ BUNDLE_GEMFILE=gemfiles/live_backends.gemfile bundle install
122
+ WOODS_RUN_LIVE_BACKENDS=1 BUNDLE_GEMFILE=gemfiles/live_backends.gemfile \
123
+ bin/rspec spec/integration/live_backends_spec.rb spec/integration/console_sql_dialects_spec.rb
104
124
  ```
105
125
 
106
- When adding a Rails line: add it to both `Appraisals` and `gemfiles/`, and add a
107
- valid Ruby×Rails pair to the `rails-matrix` job in `.github/workflows/ci.yml`.
108
- **For a row below Rails 7.1**, the gemfile must set `ENV['WOODS_SQLITE3_REQ'] =
109
- '~> 1.4'` *before* `eval_gemfile` (those Rails versions pin `sqlite3 ~> 1.4` in
110
- their adapter at load time) and pin `concurrent-ruby '< 1.3.5'` (1.3.5 dropped
111
- the implicit `require "logger"` those releases rely on under Ruby 3.x) — copy an
112
- existing `gemfiles/rails_6.0.gemfile` as the template.
126
+ The lane expects reachable PostgreSQL/pgvector, MySQL, and Qdrant services. Configure endpoints with `WOODS_PG_URL`, `WOODS_MYSQL_URL`, and `WOODS_QDRANT_URL`. The Console contracts exercise blocked-table enforcement and legitimate SQL on both database dialects. New adapter behavior that depends on a real server belongs in this lane.
127
+
128
+ ## Keep public surfaces synchronized
129
+
130
+ A pull request is incomplete when behavior and user guidance disagree.
131
+
132
+ Update the canonical owner for any changed contract:
133
+
134
+ | Change | Documentation owner |
135
+ |---|---|
136
+ | Install or first run | `docs/GETTING_STARTED.md` |
137
+ | Agent-operated installation | `docs/AGENT_SETUP.md` |
138
+ | Configuration key/default | `docs/CONFIGURATION_REFERENCE.md` |
139
+ | MCP setup or registered tools | `docs/MCP_SERVERS.md` |
140
+ | Agent query workflow | `docs/AGENT_GUIDE.md` |
141
+ | Console security/transport | `docs/CONSOLE_MCP_SETUP.md` |
142
+ | Major-version behavior | `docs/UPGRADING_TO_2.md` |
143
+ | Failure diagnosis | `docs/TROUBLESHOOTING.md` |
144
+
145
+ If a rake task, executable, MCP tool/argument, config key, setup step, or diagnosis path changes, inspect all five distributed skills under `plugin/skills/`. Update affected skills in the same Woods PR and bump `plugin/.claude-plugin/plugin.json` when skill content changes.
146
+
147
+ The plugin is published through the [`lost-in-the/plugins`](https://github.com/lost-in-the/plugins) marketplace as a git-subdir reference. Open and cross-link a paired marketplace PR when compatibility metadata, the entry, or its ref must change. Skills must check the installed Woods version and must not document unreleased capabilities as available.
148
+
149
+ Update `CHANGELOG.md` for user-visible changes. Internal refactors and typo-only documentation fixes normally do not need an entry.
150
+
151
+ ## Pull request evidence
152
+
153
+ Include:
154
+
155
+ - the problem and user-visible outcome;
156
+ - implementation scope and important tradeoffs;
157
+ - exact validation commands and results;
158
+ - Rails/storage lanes run or intentionally not run;
159
+ - public docs and plugin impact;
160
+ - migration, compatibility, security, and rollback notes when applicable;
161
+ - screenshots or transcript excerpts only when they materially verify behavior.
162
+
163
+ Do not use empty assertions or output-only tests. A regression test must fail before the fix and exercise the same runtime path as production behavior.
164
+
165
+ ## Code conventions
166
+
167
+ - Add `# frozen_string_literal: true` to Ruby files.
168
+ - Document public APIs with YARD where it improves their contract.
169
+ - Rescue `StandardError` or a narrower class; never use a bare rescue.
170
+ - Extractors return `Array<Woods::ExtractedUnit>`.
171
+ - Keep MCP stdout free of non-protocol output.
172
+ - Prefer explicit structured errors over suppressing a failure.
173
+
174
+ By contributing, you agree that your contribution is licensed under the [MIT License](LICENSE.txt).
175
+
176
+ ## Release flow
177
+
178
+ `main` is the development branch and never claims a released version. Between releases it carries the alpha development marker. Every release, including a beta or a release candidate, is an explicit commit plus a tag, cut by one rake task and published only by the guarded workflow.
179
+
180
+ | State | `Woods::VERSION` | Tagged | On RubyGems | Documentation links point at |
181
+ |---|---|---|---|---|
182
+ | Development | `X.Y.Z.alpha` | never | never | `main` |
183
+ | Beta | `X.Y.Z.betaN` | `vX.Y.Z.betaN` | prerelease | the tag |
184
+ | Release candidate | `X.Y.Z.rcN` | `vX.Y.Z.rcN` | prerelease | the tag |
185
+ | Release | `X.Y.Z` | `vX.Y.Z` | stable | the tag |
186
+
187
+ RubyGems treats any letter in a version as a prerelease, so a `~> 1.6` or `~> 2.0` constraint never resolves a beta or a release candidate. Adopting one is explicit: `gem "woods", "2.0.0.beta1"`.
188
+
189
+ `spec/release_v2/version_state_spec.rb` enforces this table on every commit. VERSION is either an alpha or the changelog carries its dated heading, and the four `release-state` documentation fences match the state VERSION declares.
190
+
191
+ ### During feature work
192
+
193
+ - Do not edit `lib/woods/version.rb` by hand.
194
+ - Put changelog entries under `## [Unreleased]` only, beneath one of its `###` headings. Duplicate headings are merged at release time, in the order they first appear.
195
+ - Leave the `release-state` fences alone. `release:prepare` rewrites them.
196
+
197
+ ### Preparing a release
198
+
199
+ One command per transition. It never commits, tags, pushes, or publishes.
200
+
201
+ | Transition | Command |
202
+ |---|---|
203
+ | Alpha to the first beta | `bin/rake "release:prepare[2.0.0.beta1]"` |
204
+ | Beta to the next beta or a release candidate | `bin/rake "release:prepare[2.0.0.rc1]"` |
205
+ | Release candidate to the release | `bin/rake "release:prepare[2.0.0]"` |
206
+ | After the release publishes, reopen development | `bin/rake "release:reopen[2.1.0.alpha]"` |
207
+
208
+ `release:prepare` refuses a dirty working tree, a version that moves backwards, a version whose base is not the line `main` is developing, and an alpha target. It then bumps VERSION, folds `## [Unreleased]` into `## [<version>] - <date>` with one block per `###` heading, restates the fences, regenerates the surface inventory, and prints the tag and dispatch commands. Every rewrite is computed before any of it is written, so a refusal leaves the working tree untouched.
209
+
210
+ A final release also absorbs every prerelease section of its own base version. Cutting `2.0.0` folds `## [2.0.0.beta1]` and `## [2.0.0.rc1]` into `## [2.0.0] - <date>` and removes their headings, prerelease entries first and anything written after them second, so the notes a user reads for 2.0.0 are the whole story rather than three fragments. An empty `## [Unreleased]` is therefore legitimate for a final release cut straight from a release candidate. A beta or a release candidate has nothing to absorb, so an empty Unreleased section refuses: there is nothing new to publish.
211
+
212
+ Review the diff and run the release contracts:
213
+
214
+ ```bash
215
+ bin/rspec spec/release_v2
216
+ bin/rake release_v2:verify_surface_inventory
217
+ bin/rspec spec/integration/packaged_gem_spec.rb
218
+ ```
219
+
220
+ Then commit and open a pull request. The release commit lands on `main` through review like any other change.
221
+
222
+ ### After the release commit merges
223
+
224
+ A release is pinned by its tag, never by a branch:
225
+
226
+ | Step | Command | What guards it |
227
+ |---|---|---|
228
+ | Tag the merge commit | `git tag v<version> <merge-sha> && git push origin v<version>` (lightweight or annotated both work) | `script/validate-release` requires the tag to sit on `main` history, match `Woods::VERSION`, match the dated `CHANGELOG.md` heading, and not be an alpha |
229
+ | Trigger the release workflow | `gh api --method POST repos/lost-in-the/woods/dispatches -f event_type=release -F 'client_payload[tag]=v<version>' -F 'client_payload[ci_run_id]=<id>'` where `<id>` is the green CI run on the tagged SHA (requires Contents write) | `.github/workflows/release.yml` re-validates the named CI run through the API, verifies the artifact digest, and runs secret-free candidate package tests before publishing |
230
+ | Verify publication | `gem info woods --remote` shows the new version; for a prerelease, `gem info woods --remote --prerelease`. The README gem badge updates on its own | just before pushing, the workflow re-runs `script/verify-release-tag` so a tag that moved since validation aborts the publish |
113
231
 
114
- ## Code Style
232
+ Nothing is published from a laptop: the workflow builds and pushes the gem from the validated CI artifact, so the bytes on RubyGems are the bytes CI tested. `rake release` and `rake release:rubygem_push`, which `bundler/gem_tasks` installs, are blocked for that reason.
115
233
 
116
- - `frozen_string_literal: true` on every file
117
- - YARD documentation on public methods
118
- - `rescue StandardError`, never bare `rescue`
119
- - All extractors return `Array<ExtractedUnit>`
234
+ After a final release publishes, reopen development with `release:reopen` in a follow-up pull request.
120
235
 
121
- ## Runtime Introspection Requirement
236
+ ### Stable branches
122
237
 
123
- Woods uses runtime introspection, not static parsing. If your feature requires access to Rails internals (ActiveRecord reflections, route introspection, etc.), it must run inside a booted Rails environment. Unit tests should use mocks/stubs; integration tests should run in a real Rails app.
238
+ A stable branch is `N-M-stable`, cut from the release tag. Create one only when a released line needs a patch after a newer major has shipped on `main`; until then, `main` is the only branch. There is no stable branch today.
124
239
 
125
- ## License
240
+ ### What coding agents may do
126
241
 
127
- By contributing, you agree that your contributions will be licensed under the MIT License.
242
+ Agents may run `release:prepare` and `release:reopen` when asked, report the commands those tasks print, and prepare the pull request. Agents must not edit `lib/woods/version.rb` or the `release-state` fences by hand, create or push tags, dispatch the release workflow, or run any form of `gem push`. See `.claude/skills/release-flow/SKILL.md`.