woods 1.6.2 → 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 (278) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +1889 -11
  3. data/CONTRIBUTING.md +195 -129
  4. data/README.md +162 -520
  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 +77 -10
  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/credential_index.rb +2 -20
  64. data/lib/woods/console/credential_scanner.rb +14 -14
  65. data/lib/woods/console/dispatch_pipeline.rb +42 -12
  66. data/lib/woods/console/embedded_executor.rb +806 -149
  67. data/lib/woods/console/eval_guard.rb +27 -20
  68. data/lib/woods/console/input_contract.rb +78 -0
  69. data/lib/woods/console/model_validator.rb +29 -1
  70. data/lib/woods/console/rack_middleware.rb +63 -43
  71. data/lib/woods/console/redactor.rb +26 -8
  72. data/lib/woods/console/safe_context.rb +58 -10
  73. data/lib/woods/console/scope_predicate_parser.rb +41 -0
  74. data/lib/woods/console/server.rb +135 -265
  75. data/lib/woods/console/sql_noise_stripper.rb +125 -16
  76. data/lib/woods/console/sql_table_scanner.rb +82 -22
  77. data/lib/woods/console/sql_validator.rb +459 -29
  78. data/lib/woods/console/table_gate.rb +2 -2
  79. data/lib/woods/console/tool_specs.rb +462 -88
  80. data/lib/woods/console/tools/tier1.rb +0 -3
  81. data/lib/woods/console/tools/tier4.rb +17 -7
  82. data/lib/woods/coordination/lock_heartbeat.rb +103 -0
  83. data/lib/woods/coordination/pipeline_lock.rb +263 -53
  84. data/lib/woods/db/migrations/007_typed_snapshot_units.rb +45 -0
  85. data/lib/woods/db/migrator.rb +3 -9
  86. data/lib/woods/db/schema_version.rb +47 -2
  87. data/lib/woods/dependency_graph.rb +898 -64
  88. data/lib/woods/embedding/fake.rb +138 -0
  89. data/lib/woods/embedding/indexer.rb +832 -40
  90. data/lib/woods/embedding/openai.rb +77 -19
  91. data/lib/woods/embedding/provider.rb +189 -11
  92. data/lib/woods/embedding/text_preparer.rb +1 -1
  93. data/lib/woods/embedding/token_counter.rb +0 -7
  94. data/lib/woods/evaluation/ablation_agent_payload.rb +38 -0
  95. data/lib/woods/evaluation/ablation_executor.rb +67 -0
  96. data/lib/woods/evaluation/ablation_provenance.rb +38 -0
  97. data/lib/woods/evaluation/ablation_report_writer.rb +43 -0
  98. data/lib/woods/evaluation/ablation_runner.rb +173 -0
  99. data/lib/woods/evaluation/ablation_summary.rb +65 -0
  100. data/lib/woods/evaluation/ablation_task.rb +66 -0
  101. data/lib/woods/evaluation/ablation_task_set.rb +77 -0
  102. data/lib/woods/evaluation/ablation_timed_executor.rb +91 -0
  103. data/lib/woods/evaluation/ablation_worktree.rb +71 -0
  104. data/lib/woods/evaluation/baseline.rb +60 -0
  105. data/lib/woods/evaluation/baseline_runner.rb +11 -3
  106. data/lib/woods/evaluation/evaluator.rb +41 -8
  107. data/lib/woods/evaluation/query_set.rb +79 -13
  108. data/lib/woods/evaluation/report_generator.rb +20 -1
  109. data/lib/woods/export/unit_facts.rb +0 -11
  110. data/lib/woods/extracted_unit.rb +22 -63
  111. data/lib/woods/extractor.rb +2503 -192
  112. data/lib/woods/extractors/action_cable_extractor.rb +9 -4
  113. data/lib/woods/extractors/ast_source_extraction.rb +20 -2
  114. data/lib/woods/extractors/caching_extractor.rb +46 -12
  115. data/lib/woods/extractors/callback_analyzer.rb +39 -9
  116. data/lib/woods/extractors/component_discovery.rb +123 -0
  117. data/lib/woods/extractors/concern_extractor.rb +17 -3
  118. data/lib/woods/extractors/controller_extractor.rb +389 -29
  119. data/lib/woods/extractors/decorator_extractor.rb +7 -14
  120. data/lib/woods/extractors/engine_extractor.rb +53 -8
  121. data/lib/woods/extractors/event_extractor.rb +55 -4
  122. data/lib/woods/extractors/factory_extractor.rb +49 -11
  123. data/lib/woods/extractors/graphql_extractor.rb +162 -66
  124. data/lib/woods/extractors/i18n_extractor.rb +6 -1
  125. data/lib/woods/extractors/job_extractor.rb +51 -21
  126. data/lib/woods/extractors/lib_extractor.rb +23 -17
  127. data/lib/woods/extractors/line_neutralizer.rb +171 -0
  128. data/lib/woods/extractors/mailer_extractor.rb +9 -1
  129. data/lib/woods/extractors/manager_extractor.rb +19 -2
  130. data/lib/woods/extractors/migration_extractor.rb +22 -11
  131. data/lib/woods/extractors/model_extractor.rb +292 -57
  132. data/lib/woods/extractors/package_extractor.rb +154 -0
  133. data/lib/woods/extractors/phlex_extractor.rb +18 -3
  134. data/lib/woods/extractors/policy_extractor.rb +6 -5
  135. data/lib/woods/extractors/poro_extractor.rb +13 -14
  136. data/lib/woods/extractors/pundit_extractor.rb +3 -3
  137. data/lib/woods/extractors/rails_source_extractor.rb +24 -7
  138. data/lib/woods/extractors/rake_task_extractor.rb +158 -30
  139. data/lib/woods/extractors/reference_patterns.rb +38 -0
  140. data/lib/woods/extractors/route_extractor.rb +58 -2
  141. data/lib/woods/extractors/scheduled_job_extractor.rb +51 -35
  142. data/lib/woods/extractors/serializer_extractor.rb +3 -4
  143. data/lib/woods/extractors/service_extractor.rb +11 -1
  144. data/lib/woods/extractors/shared_dependency_scanner.rb +24 -34
  145. data/lib/woods/extractors/shared_utility_methods.rb +36 -6
  146. data/lib/woods/extractors/source_nesting.rb +560 -0
  147. data/lib/woods/extractors/state_machine_extractor.rb +30 -18
  148. data/lib/woods/extractors/test_mapping_extractor.rb +26 -9
  149. data/lib/woods/extractors/view_component_extractor.rb +28 -3
  150. data/lib/woods/extractors/view_engines/erb.rb +17 -3
  151. data/lib/woods/feedback/gap_detector.rb +9 -3
  152. data/lib/woods/feedback/store.rb +7 -1
  153. data/lib/woods/filename_utils.rb +29 -1
  154. data/lib/woods/flow_analysis/operation_extractor.rb +22 -10
  155. data/lib/woods/flow_assembler.rb +63 -21
  156. data/lib/woods/flow_document.rb +1 -0
  157. data/lib/woods/flow_precomputer.rb +138 -22
  158. data/lib/woods/gem_mapper.rb +285 -0
  159. data/lib/woods/generation.rb +185 -0
  160. data/lib/woods/git_command.rb +38 -0
  161. data/lib/woods/git_provenance.rb +16 -2
  162. data/lib/woods/graph_analyzer.rb +408 -34
  163. data/lib/woods/index_artifact.rb +93 -23
  164. data/lib/woods/mcp/bearer_auth.rb +102 -13
  165. data/lib/woods/mcp/bootstrap_state.rb +77 -0
  166. data/lib/woods/mcp/bootstrapper.rb +582 -77
  167. data/lib/woods/mcp/config_resolver.rb +66 -6
  168. data/lib/woods/mcp/errors.rb +60 -0
  169. data/lib/woods/mcp/index_reader.rb +836 -117
  170. data/lib/woods/mcp/index_reader_pinning.rb +78 -0
  171. data/lib/woods/mcp/origin_guard.rb +66 -7
  172. data/lib/woods/mcp/protocol_policy.rb +98 -0
  173. data/lib/woods/mcp/provider_probe.rb +45 -6
  174. data/lib/woods/mcp/renderers/markdown_renderer.rb +72 -4
  175. data/lib/woods/mcp/renderers/plain_renderer.rb +54 -6
  176. data/lib/woods/mcp/server.rb +907 -154
  177. data/lib/woods/mcp/tasks/extension.rb +196 -0
  178. data/lib/woods/mcp/tasks/request_capture.rb +45 -0
  179. data/lib/woods/mcp/tasks/store.rb +518 -0
  180. data/lib/woods/mcp/tool_contract.rb +171 -0
  181. data/lib/woods/mcp/tool_response_renderer.rb +7 -0
  182. data/lib/woods/mcp/version_aware_tool_dispatch.rb +3 -9
  183. data/lib/woods/model_name_cache.rb +19 -1
  184. data/lib/woods/notion/client.rb +132 -36
  185. data/lib/woods/notion/exporter.rb +456 -61
  186. data/lib/woods/notion/mappers/column_mapper.rb +34 -5
  187. data/lib/woods/notion/mappers/migration_mapper.rb +32 -8
  188. data/lib/woods/notion/mappers/model_mapper.rb +21 -6
  189. data/lib/woods/notion/mappers/shared.rb +45 -3
  190. data/lib/woods/notion/sync_manifest.rb +258 -0
  191. data/lib/woods/obsidian/errors.rb +6 -0
  192. data/lib/woods/obsidian/name_mapper.rb +40 -24
  193. data/lib/woods/obsidian/vault_exporter.rb +103 -36
  194. data/lib/woods/operator/pipeline_guard.rb +118 -21
  195. data/lib/woods/operator/status_reporter.rb +20 -3
  196. data/lib/woods/path_dispatcher.rb +276 -0
  197. data/lib/woods/payload_store.rb +223 -0
  198. data/lib/woods/published_index/edge_shaper.rb +61 -0
  199. data/lib/woods/published_index/generation_catalog.rb +72 -0
  200. data/lib/woods/published_index/typed_unit_reader.rb +48 -0
  201. data/lib/woods/published_index.rb +287 -0
  202. data/lib/woods/railtie.rb +69 -30
  203. data/lib/woods/railtie_support.rb +167 -0
  204. data/lib/woods/release.rb +12 -0
  205. data/lib/woods/reload_policy.rb +206 -0
  206. data/lib/woods/resilience/circuit_breaker.rb +47 -8
  207. data/lib/woods/resilience/index_validator.rb +296 -10
  208. data/lib/woods/resilience/retryable_provider.rb +71 -6
  209. data/lib/woods/resolved_config.rb +55 -11
  210. data/lib/woods/retrieval/context_assembler.rb +132 -40
  211. data/lib/woods/retrieval/query_classifier.rb +25 -6
  212. data/lib/woods/retrieval/ranker.rb +193 -28
  213. data/lib/woods/retrieval/search_executor.rb +206 -39
  214. data/lib/woods/retriever.rb +317 -71
  215. data/lib/woods/retry_after.rb +22 -2
  216. data/lib/woods/ruby_analyzer/class_analyzer.rb +10 -14
  217. data/lib/woods/ruby_analyzer/fqn_builder.rb +2 -0
  218. data/lib/woods/ruby_analyzer/mermaid_renderer.rb +14 -4
  219. data/lib/woods/ruby_analyzer/method_analyzer.rb +1 -1
  220. data/lib/woods/ruby_analyzer.rb +21 -5
  221. data/lib/woods/session_tracer/file_store.rb +138 -19
  222. data/lib/woods/session_tracer/redis_store.rb +122 -12
  223. data/lib/woods/session_tracer/session_flow_assembler.rb +54 -11
  224. data/lib/woods/session_tracer/session_flow_document.rb +52 -6
  225. data/lib/woods/session_tracer/solid_cache_coordination.rb +192 -0
  226. data/lib/woods/session_tracer/solid_cache_store.rb +560 -91
  227. data/lib/woods/session_tracer/store.rb +14 -1
  228. data/lib/woods/storage/metadata_store.rb +230 -26
  229. data/lib/woods/storage/pgvector.rb +180 -22
  230. data/lib/woods/storage/qdrant.rb +367 -41
  231. data/lib/woods/storage/snapshotter/metadata.rb +79 -16
  232. data/lib/woods/storage/snapshotter/vector.rb +128 -17
  233. data/lib/woods/storage/snapshotter.rb +23 -5
  234. data/lib/woods/storage/vector_store.rb +49 -8
  235. data/lib/woods/storage_identity.rb +28 -0
  236. data/lib/woods/tasks.rb +53 -2
  237. data/lib/woods/temporal/json_snapshot_store.rb +112 -42
  238. data/lib/woods/temporal/snapshot_store.rb +139 -42
  239. data/lib/woods/unblocked/client.rb +119 -17
  240. data/lib/woods/unblocked/document_builder.rb +34 -2
  241. data/lib/woods/unblocked/exporter.rb +63 -27
  242. data/lib/woods/unblocked/rate_limiter.rb +23 -9
  243. data/lib/woods/unblocked/sync_manifest.rb +16 -8
  244. data/lib/woods/update_check.rb +24 -1
  245. data/lib/woods/util/uuid5.rb +124 -0
  246. data/lib/woods/version.rb +1 -1
  247. data/lib/woods/watch/daemon.rb +1345 -0
  248. data/lib/woods/watch/listen_watcher.rb +81 -0
  249. data/lib/woods/watch/polling_watcher.rb +137 -0
  250. data/lib/woods/watch/status.rb +169 -0
  251. data/lib/woods/watch/tree_scan.rb +163 -0
  252. data/lib/woods/watch/watcher.rb +100 -0
  253. data/lib/woods.rb +53 -9
  254. data/plugin/.claude-plugin/plugin.json +18 -0
  255. data/plugin/hooks/hooks.json +29 -0
  256. data/plugin/hooks/woods-post-edit.sh +226 -0
  257. data/plugin/hooks/woods-session-start.sh +77 -0
  258. data/plugin/skills/woods-agent-enable/SKILL.md +51 -0
  259. data/plugin/skills/woods-diagnose/SKILL.md +75 -0
  260. data/plugin/skills/woods-investigate/SKILL.md +39 -0
  261. data/plugin/skills/woods-mcp-config/SKILL.md +101 -0
  262. data/plugin/skills/woods-setup/SKILL.md +99 -0
  263. metadata +102 -26
  264. data/lib/woods/console/adapters/cache_adapter.rb +0 -58
  265. data/lib/woods/console/adapters/good_job_adapter.rb +0 -33
  266. data/lib/woods/console/adapters/job_adapter.rb +0 -74
  267. data/lib/woods/console/adapters/sidekiq_adapter.rb +0 -33
  268. data/lib/woods/console/adapters/solid_queue_adapter.rb +0 -33
  269. data/lib/woods/console/bridge.rb +0 -210
  270. data/lib/woods/console/credential_scanner_registry.rb +0 -36
  271. data/lib/woods/console/encrypted_credential_snapshot.rb +0 -16
  272. data/lib/woods/formatting/claude_adapter.rb +0 -98
  273. data/lib/woods/formatting/generic_adapter.rb +0 -56
  274. data/lib/woods/formatting/gpt_adapter.rb +0 -64
  275. data/lib/woods/mcp/http_transport_options.rb +0 -24
  276. data/lib/woods/notion/mapper.rb +0 -40
  277. data/lib/woods/observability/health_check.rb +0 -79
  278. data/lib/woods/observability/instrumentation.rb +0 -34
data/CONTRIBUTING.md CHANGED
@@ -1,176 +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
+ ```
21
27
 
22
- ## Pull Requests
28
+ Create a branch from current `main`. Keep each pull request to one logical change and preserve unrelated formatting and refactors for separate work.
23
29
 
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
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.
33
31
 
34
- ### Pre-PR requirements
32
+ ## Understand the repository
35
33
 
36
- These are hard gates — a PR that fails either is incomplete:
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) |
37
45
 
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.
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 -->
46
49
 
47
- ### Claude Code plugin changes
50
+ ### Agent orientation and static self-map
48
51
 
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:
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:
52
55
 
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.
56
+ ```bash
57
+ output_dir="$(mktemp -d)"
58
+ bin/rake "woods:self_map[$output_dir]"
59
+ bundle exec woods-mcp-start "$output_dir"
60
+ ```
61
61
 
62
- ## Development Setup
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.
68
+
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
104
112
  ```
105
113
 
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.
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.
113
115
 
114
- ## Code Style
116
+ ### Live storage and SQL dialects
115
117
 
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>`
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.
120
119
 
121
- ## Runtime Introspection Requirement
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
124
+ ```
122
125
 
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.
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.
124
127
 
125
- ## License
128
+ ## Keep public surfaces synchronized
126
129
 
127
- By contributing, you agree that your contributions will be licensed under the MIT License.
130
+ A pull request is incomplete when behavior and user guidance disagree.
128
131
 
129
- ## Maintenance release
132
+ Update the canonical owner for any changed contract:
130
133
 
131
- This tree has a one-off, no-publish preparation adapter for the reviewed 1.6.2
132
- security patch while `main` carries the newer 2.0 prerelease line. It does not
133
- establish a permanent stable branch or authorize another maintenance version.
134
- The approved target is `release/1.6.2`, based on immutable `v1.6.1`. Create that
135
- remote target only after the trusted main maintenance policy is reviewed.
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` |
136
144
 
137
- From a clean checkout, the only supported transitions are:
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.
138
146
 
139
- ```sh
140
- bin/rake "release:reopen[1.6.2.alpha]"
141
- # Review and commit the generated development-state diff.
142
- bin/rake "release:prepare[1.6.2]"
143
- ```
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
144
166
 
145
- The tasks never commit, tag, push, dispatch or publish. Never edit VERSION or the
146
- README maintenance `release-state` banner manually. The first reopen creates that
147
- banner; prepare updates it and folds classified Unreleased notes and optional
148
- `changelog/<type>_<slug>.md` entries into a dated release heading. Entry files are
149
- nonempty Markdown without headings; supported types include `fixed`, `security`,
150
- `build`, and `documentation`. Invalid transitions, dirty trees, malformed notes,
151
- and missing/duplicate/unknown fences refuse before writes. This legacy profile
152
- has no v2 migration guide or v2 surface inventory requirement.
153
-
154
- Validate the full suite, lint, booted extraction, real Console credential rotation,
155
- and installed maintenance package tests. CI builds one gem plus its SHA-256
156
- sidecar into `woods-release-<commit SHA>` and tests that artifact on Ruby 3.0/Rails
157
- 6.0 with exactly MCP 0.23.0 and Ruby 4.0/Rails 8.1 with the latest compatible 0.x SDK.
158
- MCP >=0.23.0 is required for upstream transport security fixes; update Woods and
159
- MCP together (`bundle update woods mcp`). Ruby 3.0 remains supported.
160
- The optional package specs require `WOODS_GEM_PATH` and
161
- run standalone, without the repository's `spec_helper` or implementation path:
162
-
163
- ```sh
164
- ruby -rrubygems -e 'load Gem.bin_path("rspec-core", "rspec")' -- \
165
- --options /dev/null spec/integration/maintenance_packaged_gem_spec.rb
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
166
218
  ```
167
219
 
168
- Preparation is not publication; check RubyGems before describing 1.6.2 as released.
169
- The legacy automatic tag publisher is disabled, and Bundler's `release`,
170
- `release:rubygem_push`, and `release:source_control_push` tasks abort. Only a
171
- maintainer may later tag the reviewed merge commit and dispatch the trusted
172
- **main** workflow. That workflow must explicitly allow the exact tag, protected
173
- maintenance branch, immutable 1.6.1 base, reviewed final candidate SHA, required
174
- CI jobs, and immutable artifact. An unpinned candidate remains blocked. First merge a reviewed trusted-main profile update pinning `approved_sha` to that
175
- exact maintenance merge SHA; only then may the maintainer tag it. The
176
- workflow publishes the already tested gem bytes; never rebuild or publish locally.
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 |
231
+
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.
233
+
234
+ After a final release publishes, reopen development with `release:reopen` in a follow-up pull request.
235
+
236
+ ### Stable branches
237
+
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.
239
+
240
+ ### What coding agents may do
241
+
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`.