woods 2.0.0.beta2 → 2.0.0.beta3

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 (218) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +262 -1
  3. data/CONTRIBUTING.md +173 -9
  4. data/README.md +7 -3
  5. data/SECURITY.md +9 -6
  6. data/docs/AGENT_GUIDE.md +83 -4
  7. data/docs/AGENT_SETUP.md +82 -1
  8. data/docs/BACKEND_MATRIX.md +20 -0
  9. data/docs/CLIENT_HOOKS.md +111 -0
  10. data/docs/CONFIGURATION_REFERENCE.md +199 -14
  11. data/docs/CONSOLE_MCP_SETUP.md +35 -5
  12. data/docs/DOCKER_SETUP.md +21 -2
  13. data/docs/EVALUATION.md +464 -1
  14. data/docs/EXTRACTOR_REFERENCE.md +36 -5
  15. data/docs/FAQ.md +11 -12
  16. data/docs/GETTING_STARTED.md +17 -5
  17. data/docs/INCREMENTAL_EXTRACTION.md +117 -1
  18. data/docs/INDEX_LAYOUT.md +382 -0
  19. data/docs/INTERNALS.md +7 -2
  20. data/docs/MCP_SERVERS.md +221 -5
  21. data/docs/MCP_TOOL_COOKBOOK.md +33 -18
  22. data/docs/NOTION_INTEGRATION.md +13 -0
  23. data/docs/OBSIDIAN_INTEGRATION.md +57 -9
  24. data/docs/PUBLISHED_INDEX.md +55 -0
  25. data/docs/README.md +7 -0
  26. data/docs/RETRIEVAL_GUIDE.md +253 -11
  27. data/docs/RUNTIME_TRACING.md +71 -0
  28. data/docs/SOURCE_FRESHNESS.md +143 -0
  29. data/docs/TROUBLESHOOTING.md +117 -5
  30. data/docs/UNBLOCKED_INTEGRATION.md +25 -0
  31. data/docs/UPGRADING_TO_2.md +44 -22
  32. data/docs/WATCH_DAEMON.md +259 -59
  33. data/exe/woods-agent-config +6 -0
  34. data/exe/woods-extract +5 -0
  35. data/exe/woods-hook-context +6 -0
  36. data/lib/generators/woods/templates/woods.rb.tt +1 -3
  37. data/lib/tasks/woods.rake +47 -397
  38. data/lib/woods/agent_configuration/applier.rb +133 -0
  39. data/lib/woods/agent_configuration/cli.rb +101 -0
  40. data/lib/woods/agent_configuration/cli_options.rb +29 -0
  41. data/lib/woods/agent_configuration/document.rb +105 -0
  42. data/lib/woods/agent_configuration/error.rb +7 -0
  43. data/lib/woods/agent_configuration/launcher.rb +75 -0
  44. data/lib/woods/agent_configuration/layout.rb +59 -0
  45. data/lib/woods/agent_configuration/managed_section.rb +62 -0
  46. data/lib/woods/agent_configuration/plan.rb +98 -0
  47. data/lib/woods/agent_configuration/plan_diff.rb +38 -0
  48. data/lib/woods/agent_configuration/planned_files.rb +61 -0
  49. data/lib/woods/agent_configuration/planner.rb +63 -0
  50. data/lib/woods/agent_configuration/planner_validation.rb +77 -0
  51. data/lib/woods/agent_configuration/preflight.rb +100 -0
  52. data/lib/woods/agent_configuration/recovery.rb +49 -0
  53. data/lib/woods/ast/node.rb +2 -0
  54. data/lib/woods/ast/parser.rb +38 -5
  55. data/lib/woods/builder.rb +21 -5
  56. data/lib/woods/cache/cache_middleware.rb +28 -7
  57. data/lib/woods/cache/cache_store.rb +4 -5
  58. data/lib/woods/change_set.rb +5 -4
  59. data/lib/woods/console/credential_index.rb +20 -2
  60. data/lib/woods/console/credential_scanner.rb +14 -14
  61. data/lib/woods/console/credential_scanner_registry.rb +36 -0
  62. data/lib/woods/console/embedded_executor.rb +1 -1
  63. data/lib/woods/console/encrypted_credential_snapshot.rb +16 -0
  64. data/lib/woods/console/rack_middleware.rb +22 -13
  65. data/lib/woods/console/server.rb +18 -16
  66. data/lib/woods/dependency_graph.rb +65 -13
  67. data/lib/woods/embedding/corpus.rb +94 -0
  68. data/lib/woods/embedding/indexer.rb +90 -46
  69. data/lib/woods/embedding/openai.rb +17 -6
  70. data/lib/woods/evaluation/ablation_executor.rb +6 -1
  71. data/lib/woods/evaluation/ablation_timed_executor.rb +22 -4
  72. data/lib/woods/export/typed_reader.rb +56 -0
  73. data/lib/woods/extractor.rb +232 -137
  74. data/lib/woods/extractors/action_cable_extractor.rb +3 -1
  75. data/lib/woods/extractors/behavioral_profile.rb +9 -7
  76. data/lib/woods/extractors/caching_extractor.rb +3 -1
  77. data/lib/woods/extractors/concern_extractor.rb +64 -6
  78. data/lib/woods/extractors/configuration_extractor.rb +7 -3
  79. data/lib/woods/extractors/controller_extractor.rb +13 -4
  80. data/lib/woods/extractors/database_view_extractor.rb +3 -1
  81. data/lib/woods/extractors/decorator_extractor.rb +3 -1
  82. data/lib/woods/extractors/engine_extractor.rb +3 -1
  83. data/lib/woods/extractors/event_extractor.rb +4 -2
  84. data/lib/woods/extractors/factory_extractor.rb +3 -1
  85. data/lib/woods/extractors/graphql_extractor.rb +8 -2
  86. data/lib/woods/extractors/i18n_extractor.rb +3 -1
  87. data/lib/woods/extractors/job_extractor.rb +6 -19
  88. data/lib/woods/extractors/lib_extractor.rb +3 -1
  89. data/lib/woods/extractors/mailer_extractor.rb +20 -5
  90. data/lib/woods/extractors/manager_extractor.rb +3 -1
  91. data/lib/woods/extractors/method_parameters.rb +53 -0
  92. data/lib/woods/extractors/middleware_argument.rb +65 -0
  93. data/lib/woods/extractors/middleware_extractor.rb +9 -3
  94. data/lib/woods/extractors/migration_extractor.rb +3 -1
  95. data/lib/woods/extractors/model_extractor.rb +39 -33
  96. data/lib/woods/extractors/package_extractor.rb +24 -4
  97. data/lib/woods/extractors/phlex_extractor.rb +3 -1
  98. data/lib/woods/extractors/policy_extractor.rb +3 -1
  99. data/lib/woods/extractors/poro_extractor.rb +3 -1
  100. data/lib/woods/extractors/pundit_extractor.rb +3 -1
  101. data/lib/woods/extractors/rails_source_extractor.rb +4 -2
  102. data/lib/woods/extractors/rake_task_extractor.rb +4 -2
  103. data/lib/woods/extractors/route_extractor.rb +3 -1
  104. data/lib/woods/extractors/route_helper_resolver.rb +10 -33
  105. data/lib/woods/extractors/scheduled_job_extractor.rb +41 -15
  106. data/lib/woods/extractors/serializer_extractor.rb +4 -2
  107. data/lib/woods/extractors/service_extractor.rb +3 -1
  108. data/lib/woods/extractors/shared_dependency_scanner.rb +2 -2
  109. data/lib/woods/extractors/shared_utility_methods.rb +27 -15
  110. data/lib/woods/extractors/source_nesting.rb +1 -1
  111. data/lib/woods/extractors/state_machine_extractor.rb +3 -1
  112. data/lib/woods/extractors/test_mapping_extractor.rb +3 -1
  113. data/lib/woods/extractors/validator_extractor.rb +3 -1
  114. data/lib/woods/extractors/view_component_extractor.rb +3 -1
  115. data/lib/woods/extractors/view_template_extractor.rb +3 -1
  116. data/lib/woods/gem_mapper.rb +2 -0
  117. data/lib/woods/git_history.rb +116 -0
  118. data/lib/woods/graph_analyzer.rb +35 -6
  119. data/lib/woods/hooks/context_cli.rb +54 -0
  120. data/lib/woods/hooks/context_event.rb +88 -0
  121. data/lib/woods/hooks/context_hint.rb +73 -0
  122. data/lib/woods/hooks/context_impact.rb +77 -0
  123. data/lib/woods/hooks/context_output.rb +47 -0
  124. data/lib/woods/hooks/context_state.rb +102 -0
  125. data/lib/woods/hooks/refresh.rb +79 -0
  126. data/lib/woods/hooks/rule_projection.rb +78 -0
  127. data/lib/woods/input_rules.rb +19 -0
  128. data/lib/woods/mcp/bearer_auth.rb +20 -12
  129. data/lib/woods/mcp/bootstrapper.rb +62 -0
  130. data/lib/woods/mcp/index_reader.rb +323 -160
  131. data/lib/woods/mcp/initialization_guidance.rb +27 -0
  132. data/lib/woods/mcp/origin_guard.rb +17 -9
  133. data/lib/woods/mcp/published_lexical_retriever.rb +115 -0
  134. data/lib/woods/mcp/renderers/markdown_renderer.rb +8 -1
  135. data/lib/woods/mcp/renderers/plain_renderer.rb +7 -1
  136. data/lib/woods/mcp/search_results.rb +74 -0
  137. data/lib/woods/mcp/server.rb +158 -37
  138. data/lib/woods/mcp/tool_contract.rb +2 -0
  139. data/lib/woods/mcp/tool_response_renderer.rb +25 -0
  140. data/lib/woods/mcp/traversal_evidence.rb +113 -0
  141. data/lib/woods/mcp/traversal_evidence_index.rb +100 -0
  142. data/lib/woods/mcp/traversal_evidence_page.rb +41 -0
  143. data/lib/woods/mcp/traversal_evidence_text.rb +52 -0
  144. data/lib/woods/notion/exporter.rb +56 -17
  145. data/lib/woods/obsidian/destination_plan.rb +98 -0
  146. data/lib/woods/obsidian/name_mapper.rb +19 -3
  147. data/lib/woods/obsidian/note_builder.rb +19 -10
  148. data/lib/woods/obsidian/vault_exporter.rb +88 -32
  149. data/lib/woods/operator/pipeline_guard.rb +18 -13
  150. data/lib/woods/path_dispatcher.rb +7 -1
  151. data/lib/woods/payload_store.rb +27 -26
  152. data/lib/woods/railtie.rb +3 -3
  153. data/lib/woods/railtie_support.rb +12 -12
  154. data/lib/woods/rake_helpers.rb +392 -0
  155. data/lib/woods/resilience/graph_invariant_validator/membership_checks.rb +71 -0
  156. data/lib/woods/resilience/graph_invariant_validator/node_checks.rb +61 -0
  157. data/lib/woods/resilience/graph_invariant_validator/reverse_relationship_checks.rb +46 -0
  158. data/lib/woods/resilience/graph_invariant_validator.rb +119 -0
  159. data/lib/woods/resilience/index_validator/graph_checks.rb +80 -0
  160. data/lib/woods/resilience/index_validator.rb +112 -23
  161. data/lib/woods/retrieval/context_assembler.rb +50 -15
  162. data/lib/woods/retrieval/lexical_assembler.rb +73 -0
  163. data/lib/woods/retrieval/lexical_index.rb +119 -0
  164. data/lib/woods/retrieval/ranker.rb +4 -2
  165. data/lib/woods/retrieval/scope.rb +108 -0
  166. data/lib/woods/retrieval/scoped_graph_store.rb +32 -0
  167. data/lib/woods/retrieval/scoped_vector_store.rb +55 -0
  168. data/lib/woods/retrieval/search_executor.rb +86 -27
  169. data/lib/woods/retrieval/source_evidence.rb +200 -0
  170. data/lib/woods/retriever.rb +98 -22
  171. data/lib/woods/ruby_analyzer/trace_enricher.rb +77 -38
  172. data/lib/woods/session_tracer/middleware.rb +10 -12
  173. data/lib/woods/session_tracer/redis_store.rb +22 -6
  174. data/lib/woods/session_tracer/session_flow_assembler.rb +23 -17
  175. data/lib/woods/session_tracer/solid_cache_coordination.rb +6 -4
  176. data/lib/woods/session_tracer/unit_resolver.rb +63 -0
  177. data/lib/woods/source_inputs/consumer_errors.rb +27 -0
  178. data/lib/woods/source_inputs/handoff.rb +102 -0
  179. data/lib/woods/source_inputs/launcher.rb +157 -0
  180. data/lib/woods/source_inputs/manifest.rb +124 -0
  181. data/lib/woods/source_inputs/private_key.rb +55 -0
  182. data/lib/woods/source_inputs/scanner.rb +171 -0
  183. data/lib/woods/source_inputs/scopes.rb +71 -0
  184. data/lib/woods/source_inputs/session.rb +214 -0
  185. data/lib/woods/source_inputs/status.rb +84 -0
  186. data/lib/woods/source_inputs/verifier.rb +107 -0
  187. data/lib/woods/storage/metadata_store.rb +25 -25
  188. data/lib/woods/storage/pgvector.rb +29 -8
  189. data/lib/woods/storage/qdrant.rb +17 -7
  190. data/lib/woods/storage/vector_store.rb +18 -6
  191. data/lib/woods/tasks.rb +3 -2
  192. data/lib/woods/temporal/json_snapshot_store.rb +29 -8
  193. data/lib/woods/unblocked/exporter.rb +59 -70
  194. data/lib/woods/version.rb +1 -1
  195. data/lib/woods/watch/boot_snapshot.rb +52 -0
  196. data/lib/woods/watch/daemon.rb +136 -28
  197. data/lib/woods/watch/listen_watcher.rb +4 -0
  198. data/lib/woods/watch/polling_watcher.rb +5 -1
  199. data/lib/woods/watch/status.rb +20 -15
  200. data/lib/woods/watch/tree_scan.rb +21 -13
  201. data/lib/woods/watch/watcher.rb +4 -1
  202. data/lib/woods.rb +50 -11
  203. data/plugin/.claude-plugin/plugin.json +1 -1
  204. data/plugin/hooks/adapters/normalize.jq +15 -0
  205. data/plugin/hooks/adapters/normalize.rb +63 -0
  206. data/plugin/hooks/hooks.json +20 -0
  207. data/plugin/hooks/woods-context.sh +50 -0
  208. data/plugin/hooks/woods-input-rules.sh +159 -0
  209. data/plugin/hooks/woods-opencode.mjs +65 -0
  210. data/plugin/hooks/woods-post-edit.sh +2 -225
  211. data/plugin/hooks/woods-refresh.sh +260 -0
  212. data/plugin/hooks/woods-session-start.sh +47 -55
  213. data/plugin/skills/woods-agent-enable/SKILL.md +13 -0
  214. data/plugin/skills/woods-diagnose/SKILL.md +288 -1
  215. data/plugin/skills/woods-investigate/SKILL.md +106 -0
  216. data/plugin/skills/woods-mcp-config/SKILL.md +89 -1
  217. data/plugin/skills/woods-setup/SKILL.md +107 -6
  218. metadata +84 -5
@@ -24,7 +24,7 @@ This guide covers the most common problems encountered when installing, extracti
24
24
  | `JSON parse errors` (MCP) | Rails boot noise on stdout | Remove `puts` calls from initializers |
25
25
  | Query timeout | Large table, no scope | Add scope conditions to narrow results |
26
26
  | Empty extraction output | `eager_load!` failure | Check for `NameError` in boot output |
27
- | Git metadata missing | Shallow clone in CI | Use `fetch-depth: 2` or higher |
27
+ | Git metadata missing | Shallow clone in CI | Use `fetch-depth: 0` for complete history |
28
28
  | Parallel tool calls all fail | MCP client batches calls | Send calls sequentially, validate params first |
29
29
  | HTTP transport refuses to start on `0.0.0.0` | Missing bearer token | Set `WOODS_MCP_HTTP_TOKEN=…` or bind loopback only |
30
30
  | HTTP transport returns `403 Origin not allowed` | Origin header not in allow-list | Set `WOODS_MCP_HTTP_ALLOWED_ORIGINS="https://example.com"` (comma-separated; default is loopback-only) |
@@ -43,8 +43,55 @@ For a single-call health snapshot, call the Index Server's `woods_status` tool.
43
43
 
44
44
  Agents cold-connecting to a server should call `woods_status` before any other tool, it eliminates most "why is this empty?" guesswork.
45
45
 
46
+ If `woods:validate` warns that the manifest writer and reader major versions
47
+ differ, run full extraction using the intended gem and follow the upgrade guide.
48
+ An invalid `woods_version` warns without failing structural validation; a missing
49
+ or null value is normal for older indexes. Compare `index.woods_version` with
50
+ `server.version` in MCP status; never infer an unknown writer from the reader's
51
+ version. See [manifest writer provenance](PUBLISHED_INDEX.md#manifest-writer-provenance).
52
+
46
53
  If a tool call fails with **"Tool not found: … not available in the installed Woods v…"**, the client is asking for a tool a newer gem provides. Run `bundle update woods` and reconnect the MCP server, then retry.
47
54
 
55
+ ### Semantic graph validation errors
56
+
57
+ In development versions containing #413, `woods:validate` rejects graphs that
58
+ parse as JSON but disagree with their indexes. Errors name the section and
59
+ identity, for example `reverse["http_api"]: missing "Order"`, a duplicate typed
60
+ variant, or an indexed unit absent from `nodes`. This is unreleased after
61
+ `2.0.0.beta2`; check the installed gem before expecting these diagnostics.
62
+
63
+ Keep the failing generation and report the exact errors. Run a full extraction
64
+ in a fresh application process with the intended bundle, then validate again.
65
+ Do not edit derived reverse/file/type indexes to silence the check. If a fresh
66
+ full run still fails, report the invariant and source units as an extraction bug.
67
+ The checker never repairs or republishes the index itself.
68
+
69
+ An unresolved target is legal; missing both a node and its unit cannot be
70
+ classified as external versus accidentally omitted from current metadata alone.
71
+ A green report also does not prove that an indexed relationship executes at
72
+ runtime. See [the checked invariants and limitations](INDEX_LAYOUT.md#semantic-graph-validation).
73
+
74
+ ### Corrupt pipeline cooldown state
75
+
76
+ In a custom Index MCP server configured with an `operator` and
77
+ `pipeline_guard`, a corrupt `pipeline_guard.json` denies full pipeline runs
78
+ until repaired. The packaged `woods-mcp` executable does not expose pipeline
79
+ operations; confirm the connected server's tools before using this recovery.
80
+
81
+ For versions containing B-159, call `pipeline_repair` with
82
+ `{"action":"reset_cooldowns"}` to replace malformed JSON, non-object JSON, or an
83
+ empty guard file with an empty state object under the guard's file lock. This
84
+ explicit action clears the extraction and embedding cooldowns; subsequent runs
85
+ can start immediately. The direct Ruby equivalent is `guard.reset!(:all)`.
86
+ Scoped resets leave corrupt state untouched. Valid state keeps any unrelated
87
+ operation entries, and missing state remains a no-op without creating a file.
88
+ A permission failure must be corrected before repair can succeed.
89
+
90
+ This recovery is unreleased after `2.0.0.beta2`; check the installed version.
91
+ Older versions report corrupt state as nothing to repair. Stop pipeline writers,
92
+ back up the configured guard state's `pipeline_guard.json`, and remove only that
93
+ file before restarting, or upgrade to a version containing the fix.
94
+
48
95
  ## Extraction Problems
49
96
 
50
97
  ### Extraction produces empty or incomplete output
@@ -171,21 +218,60 @@ bundle exec rake woods:extract
171
218
 
172
219
  ---
173
220
 
221
+ ### External dependency targets lose dependents after incremental extraction
222
+
223
+ **Symptom:** An external target such as `http_api` loses previously indexed
224
+ dependents after an incremental run.
225
+
226
+ **Cause:** Versions affected by B-193 can split symbolic and string target
227
+ identities when restoring and updating the graph.
228
+
229
+ **Fix:** Check whether the installed version includes B-193; this fix is
230
+ unreleased. After upgrading to a version containing the fix, run
231
+ `bundle exec rake woods:extract` once to rebuild lost reverse dependencies.
232
+ Loading an already damaged graph does not restore discarded entries. See the
233
+ [incremental graph contract](INCREMENTAL_EXTRACTION.md#the-contract).
234
+
174
235
  ### Git metadata is missing or shows zeros
175
236
 
176
- **Symptom:** Units have `last_modified_at: null` or `change_frequency: 0` in the JSON output.
237
+ **Symptom:** Per-unit `metadata.git` is absent, or an older Woods version reports
238
+ most files as `change_frequency: new` in a shallow CI checkout.
177
239
 
178
- **Cause:** The git repository is a shallow clone (common in CI with `fetch-depth: 1`). Woods uses `git log` to compute change frequency, a shallow clone has no history to analyze.
240
+ **Cause:** A shallow clone truncates HEAD ancestry. The shallow-checkout guard is
241
+ unreleased after 2.0.0.beta2: current source omits git enrichment and warns once,
242
+ rather than treating the truncated history as complete. If repository depth
243
+ cannot be verified, enrichment is also omitted; check git access and version.
179
244
 
180
- **Fix:** Fetch at least two commits:
245
+ **Fix:** Fetch complete history (`git fetch --unshallow` for an existing shallow
246
+ clone), then run full extraction to replace retained metadata:
181
247
 
182
248
  ```yaml
183
249
  # .github/workflows/index.yml
184
250
  - uses: actions/checkout@v4
185
251
  with:
186
- fetch-depth: 2 # minimum for incremental; use 0 for full history
252
+ fetch-depth: 0
187
253
  ```
188
254
 
255
+ Two commits can suffice for an incremental diff, but do not establish the full
256
+ ancestry needed for churn metadata.
257
+
258
+ ---
259
+
260
+ ### Git enrichment warns that history could not be read completely
261
+
262
+ Current source uses an explicit merge-diff mode requiring **Git 2.31 or newer**.
263
+ This is unreleased after 2.0.0.beta2: first confirm the installed Woods version.
264
+ Check `git --version` inside the same container/process environment as extraction,
265
+ and upgrade git if it is older. On a supported version, check that the application's
266
+ `HEAD` and object store can be read using the same `WOODS_GIT_DIR` setting.
267
+
268
+ A failed or incomplete history stream is discarded as a whole; extraction continues
269
+ without that enrichment, rather than publishing partial or zero-count history.
270
+ Previously retained incremental units can still carry older metadata. After fixing
271
+ git, run a full extraction to refresh every unit. See the
272
+ [history contract](CONFIGURATION_REFERENCE.md#git-enrichment-history) for merge
273
+ counting and upgrade compatibility.
274
+
189
275
  ---
190
276
 
191
277
  ### Every unit reports `commit_count: 0` and `change_frequency: "new"`
@@ -336,6 +422,11 @@ cat ./tmp/woods/generation.json # {"number": 42, "payload": "
336
422
  ls ./tmp/woods/payloads/gen-42/manifest.json
337
423
  ```
338
424
 
425
+ Custom scripts that require a root `dependency_graph.json` have the same failure.
426
+ Update their gate using the [filesystem layout contract](INDEX_LAYOUT.md), which
427
+ includes Bash/jq and Python readers. An upload must pin and copy one complete
428
+ payload before publishing its captured pointer; keep a failed copy unpublished.
429
+
339
430
  `woods-mcp-start` and `IndexReader` already resolve this automatically, this is only for manual inspection. If neither path has a manifest, your Docker volume mount is not configured correctly. See [DOCKER_SETUP.md](DOCKER_SETUP.md).
340
431
 
341
432
  ---
@@ -839,3 +930,24 @@ view sharing the same name. Current writers distinguish typed storage identities
839
930
  public names remain unchanged. Snapshot migration 007 runs automatically and keeps
840
931
  existing rows, but cannot recover variants lost by older writers. See the
841
932
  [upgrade guide](UPGRADING_TO_2.md) for storage, flow rebuild, and rollback details.
933
+
934
+ ## Watch exits 75 repeatedly at startup
935
+
936
+ Check the installed Woods version and its [watch daemon guide](WATCH_DAEMON.md).
937
+ Older releases, including `2.0.0.beta2`, can repeatedly request restart when a
938
+ boot-captured file is newer than the last index generation. Stop the supervisor,
939
+ run one successful `bundle exec rake woods:extract` in the application environment,
940
+ then start the standalone `bundle exec rake woods:watch` process again.
941
+
942
+ With startup snapshot support, a fresh environment boot performs the full
943
+ reconciliation automatically. Changes during environment initialization or live
944
+ watching still require restart. Do not prepend `environment` to the watch command
945
+ or start it inside an already initialized process when relying on this recovery.
946
+
947
+ ## Source freshness is unknown or drifted
948
+
949
+ Read `woods_status.index.source_freshness.reasons`. Old indexes, an inaccessible
950
+ source root/private key, a quick scan limit and an unverified boot boundary are
951
+ different causes. Try `source_check: "deep"` for a budget limit; use the fresh
952
+ launcher for a new verified baseline. Do not delete pending hook events or alter
953
+ key permissions just to suppress a warning. See [source freshness](SOURCE_FRESHNESS.md).
@@ -139,6 +139,20 @@ https://github.com/your-org/your-repo/blob/main/app/models/order.rb
139
139
 
140
140
  This means Unblocked citations link directly to the relevant source code.
141
141
 
142
+ Different identifiers defined in one synced file retain the existing URI
143
+ rule: the lexically first identifier keeps the bare URI; siblings use
144
+ `?unit=<encoded identifier>`. Original identifiers and unambiguous URIs do
145
+ not change when two extracted types share a name but have different files.
146
+
147
+ Two **different types with the same identifier and source file** cannot be
148
+ represented by this URI rule. Woods reports `ambiguous export URI` and skips
149
+ all documents sharing that base URI. It also disables stale-document deletion
150
+ for that run, including with `UNBLOCKED_FORCE_PURGE=1`. Existing remote
151
+ documents remain untouched. This is a deliberate refusal pending an explicit
152
+ remote URI migration; renaming public extraction identifiers is not required.
153
+ Collision detection includes all published types, including excluded types
154
+ and units outside a partial sync's top-N selection.
155
+
142
156
  ## Rate Limits
143
157
 
144
158
  The Unblocked API allows 1,000 calls per day (resets at midnight PST). A typical
@@ -236,6 +250,17 @@ state an unchanged codebase costs ~0 calls.
236
250
  Pair with `woods:incremental` to re-extract only changed files; the sync then
237
251
  pushes only the documents whose content actually changed.
238
252
 
253
+ Before any API mutation, Woods reads and validates the complete published unit
254
+ set under one generation pin. Full and partial selection use actual unit types;
255
+ the GraphQL family includes its four published subtypes once. Standalone
256
+ `sync_type` and `sync_type_partial` calls perform the same preflight. Missing,
257
+ unreadable, or mismatched identities refuse before uploads, deletion, or
258
+ manifest writes. Preserve the error, validate and regenerate the index, then
259
+ retry. Force flags cannot bypass this check. Preflight reads all published
260
+ unit bodies, so local read cost and temporary memory scale with the index even
261
+ for a single-type sync. Custom readers must provide complete published
262
+ enumeration or complete per-bucket listings plus strict typed lookup.
263
+
239
264
  ### Escape hatches
240
265
 
241
266
  - `UNBLOCKED_FORCE_FULL_SYNC=1`: re-push every document, ignoring the unchanged
@@ -5,8 +5,8 @@ Woods 2.0 changes observable index identifiers, publication layout, vector-store
5
5
  This guide assumes the last v1 release, 1.6.1, and targets 2.0.0.
6
6
 
7
7
  <!-- release-state:upgrade-availability -->
8
- > RubyGems lists 2.0.0.beta2 as a prerelease. Pin it explicitly with
9
- > `gem "woods", "2.0.0.beta2"`; `~> 2.0` resolves only once
8
+ > RubyGems lists 2.0.0.beta3 as a prerelease. Pin it explicitly with
9
+ > `gem "woods", "2.0.0.beta3"`; `~> 2.0` resolves only once
10
10
  > 2.0.0 is published.
11
11
  <!-- release-state:end -->
12
12
 
@@ -47,6 +47,25 @@ After this runbook you will have:
47
47
 
48
48
  ## Before changing the bundle
49
49
 
50
+ ### Check the loader for wrapper-nested classes
51
+
52
+ File-path-governed naming of classes inside class namespaces requires **Zeitwerk
53
+ mode with Zeitwerk 2.6.9 or later** (`cpath_expected_at`, introduced in
54
+ [Zeitwerk 2.6.9](https://github.com/fxn/zeitwerk/blob/main/CHANGELOG.md#269-25-july-2023)). This is a requirement
55
+ of that naming capability, not a higher Rails minimum. Older Zeitwerk and
56
+ classic-mode applications can still extract ordinary declarations, but Woods
57
+ cannot use the loader to distinguish a file's class from its enclosing class
58
+ wrappers. Two sibling files may then derive the same wrapper identifier and
59
+ extraction will abort rather than silently discard one.
60
+
61
+ For a `same-type identifier collision`, inspect both named files and the
62
+ application's loader mode/version before rewriting valid namespace wrappers.
63
+ On an older-loader host, move to a compatible Zeitwerk version and Zeitwerk mode,
64
+ verify that the application boots and eager-loads, then run a fresh full
65
+ extraction. Rebuild embeddings and exports if identifiers change. A genuine
66
+ duplicate under a supported loader still needs distinct constants or one source
67
+ file. Woods does not provide a classic-mode naming fallback for this case.
68
+
50
69
  ### 1. Record the current installation
51
70
 
52
71
  Run in the same environment that boots Rails:
@@ -106,7 +125,7 @@ Pay particular attention to:
106
125
  - `output_dir` and environment overrides;
107
126
  - storage and embedding provider settings;
108
127
  - the configured embedding model/dimension;
109
- - `console_mcp_enabled`, the `console_mcp_token` secret source, allowed origins, path, and embedded read-tool flags;
128
+ - `console_mcp_enabled`, `console_mcp_http_enabled`, the HTTP `console_mcp_token` secret source, allowed origins, path, and embedded read-tool flags;
110
129
  - snapshot, session, Notion, Obsidian, and Unblocked settings;
111
130
  - old `config.extractors` or `config.add_gem` calls, which are not implemented selectors.
112
131
 
@@ -198,6 +217,15 @@ tmp/woods/
198
217
 
199
218
  Woods tasks, readers, exporters, and MCP servers resolve this automatically. Custom tooling must read `generation.json`, resolve its `payload` relative to the index root, reject paths that escape that root, and then read the payload files. A missing payload key represents the legacy flat layout.
200
219
 
220
+ Use the [filesystem layout contract](INDEX_LAYOUT.md) for Bash/jq and Python
221
+ examples. Multi-file reads and uploads must keep the selected payload pinned
222
+ against retention for the complete read/copy; pointer resolution alone does not
223
+ protect a directory from being pruned.
224
+
225
+ The optional manifest `woods_version` records its last publisher. A matching
226
+ major version after an incremental run does not establish that older units were
227
+ migrated; keep the full re-extraction requirement. See [writer provenance](PUBLISHED_INDEX.md#manifest-writer-provenance).
228
+
201
229
  Graph consumers must also tolerate multiple typed variants for the same textual identifier. Do not collapse nodes by identifier alone when type is part of identity.
202
230
 
203
231
  The bundle requires patched MessagePack >=1.8.2 and JSON >=2.19.9, <3.
@@ -254,28 +282,22 @@ Structural reads still work from a read-only index mount, but the `reload` tool
254
282
 
255
283
  ### Console users: preserve or configure the HTTP token
256
284
 
257
- If Console MCP is disabled, no Console token action is required. If it is enabled, configure a token of at least 32 characters through a secret manager or environment variable rather than committing it to the initializer:
258
-
259
- ```bash
260
- WOODS_CONSOLE_MCP_TOKEN="$(openssl rand -hex 32)"
261
- export WOODS_CONSOLE_MCP_TOKEN
262
- ```
263
-
264
- ```ruby
265
- config.console_mcp_token = ENV.fetch("WOODS_CONSOLE_MCP_TOKEN")
266
- ```
267
-
268
- Persist the generated value in the application's normal secret store before opening a new shell or deploying. Never print, log, or commit the token during an agent-operated upgrade.
269
-
270
- Rails-mounted Console HTTP clients must send the token as `Authorization: Bearer <token>`. With Console enabled, a missing token has these outcomes:
285
+ If HTTP Console is enabled, preserve or configure a secret token of at least
286
+ 32 characters. Missing tokens warn outside production and HTTP requests fail
287
+ closed with 401; production boot refuses them. Configured short tokens raise
288
+ while HTTP Console is enabled. Keep token values in the application's normal
289
+ secret store, never in committed configuration.
271
290
 
272
- - outside production, Rails warns and the Console HTTP endpoint returns 401;
273
- - in production, Rails refuses to boot;
274
- - in every environment, a configured token shorter than 32 characters raises a configuration error.
291
+ Stdio does not use a bearer token. On versions supporting
292
+ `console_mcp_http_enabled`, set it to `false` for stdio-only use without HTTP
293
+ boot validation, while keeping the master `console_mcp_enabled` flag on.
294
+ The HTTP flag defaults to `true` to preserve existing deployments; choosing a
295
+ stdio client alone does not turn HTTP off. Older versions without this flag
296
+ still require a token at production boot whenever Console is enabled.
275
297
 
276
- The stdio Console transport does not send or authenticate with the bearer token. Outside production it can run without one, although Rails still warns because enabling Console also activates the guarded Rack endpoint. In production, Rails boot validation still requires the token even when stdio is the intended transport. The safest default is to configure the token whenever Console is enabled, or leave Console disabled.
298
+ Follow [Console MCP setup](CONSOLE_MCP_SETUP.md) for transport-specific setup
299
+ and the [Configuration reference](CONFIGURATION_REFERENCE.md) for defaults.
277
300
 
278
- See [Console MCP setup](CONSOLE_MCP_SETUP.md) for client examples and [Configuration reference](CONFIGURATION_REFERENCE.md) for the complete security settings.
279
301
 
280
302
  ## Verify before rollout
281
303