woods 2.0.1 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (154) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +94 -7
  3. data/CONTRIBUTING.md +134 -19
  4. data/README.md +1 -1
  5. data/docs/AGENT_GUIDE.md +19 -0
  6. data/docs/AGENT_SETUP.md +22 -2
  7. data/docs/BACKEND_MATRIX.md +7 -0
  8. data/docs/CLIENT_HOOKS.md +6 -0
  9. data/docs/CONFIGURATION_REFERENCE.md +133 -25
  10. data/docs/CONSOLE_MCP_SETUP.md +82 -30
  11. data/docs/EMBEDDING_MODELS.md +16 -19
  12. data/docs/EXTRACTOR_REFERENCE.md +219 -21
  13. data/docs/FAQ.md +11 -25
  14. data/docs/GETTING_STARTED.md +7 -1
  15. data/docs/INCREMENTAL_EXTRACTION.md +261 -19
  16. data/docs/INDEX_LAYOUT.md +5 -0
  17. data/docs/INTERNALS.md +9 -0
  18. data/docs/MCP_HTTP_TRANSPORT.md +20 -15
  19. data/docs/MCP_SERVERS.md +87 -8
  20. data/docs/MCP_TOOL_COOKBOOK.md +13 -55
  21. data/docs/NOTION_INTEGRATION.md +7 -1
  22. data/docs/PUBLISHED_INDEX.md +6 -0
  23. data/docs/README.md +6 -1
  24. data/docs/RETRIEVAL_GUIDE.md +17 -0
  25. data/docs/SOURCE_FRESHNESS.md +157 -5
  26. data/docs/TOKEN_BENCHMARK.md +10 -18
  27. data/docs/TROUBLESHOOTING.md +70 -14
  28. data/docs/UNBLOCKED_INTEGRATION.md +60 -8
  29. data/docs/UPGRADING_TO_2.md +153 -38
  30. data/docs/WATCH_DAEMON.md +97 -14
  31. data/exe/woods-console-mcp +2 -2
  32. data/lib/generators/woods/templates/woods.rb.tt +2 -1
  33. data/lib/tasks/woods.rake +23 -7
  34. data/lib/tasks/woods_checks.rake +2 -2
  35. data/lib/woods/agent_configuration/cli.rb +1 -1
  36. data/lib/woods/agent_configuration/layout.rb +16 -2
  37. data/lib/woods/agent_configuration/plan.rb +13 -3
  38. data/lib/woods/agent_configuration/planner_validation.rb +4 -2
  39. data/lib/woods/agent_configuration/preflight.rb +5 -3
  40. data/lib/woods/builder.rb +17 -57
  41. data/lib/woods/cache/cache_middleware.rb +56 -30
  42. data/lib/woods/chunking/contributor_chunks.rb +119 -0
  43. data/lib/woods/chunking/semantic_chunker.rb +44 -21
  44. data/lib/woods/console/connection_manager.rb +56 -3
  45. data/lib/woods/console/embedded_executor.rb +30 -5
  46. data/lib/woods/console/rack_middleware.rb +29 -1
  47. data/lib/woods/dependency_graph.rb +34 -10
  48. data/lib/woods/embedding/fake.rb +12 -0
  49. data/lib/woods/embedding/indexer.rb +195 -98
  50. data/lib/woods/embedding/input_budget.rb +67 -0
  51. data/lib/woods/embedding/openai.rb +70 -20
  52. data/lib/woods/embedding/provider.rb +37 -25
  53. data/lib/woods/embedding/text_preparer.rb +76 -32
  54. data/lib/woods/embedding/token_counter.rb +18 -81
  55. data/lib/woods/embedding/vector_configuration.rb +48 -0
  56. data/lib/woods/extraction_identities.rb +175 -0
  57. data/lib/woods/extractor.rb +304 -107
  58. data/lib/woods/extractors/action_cable_extractor.rb +8 -3
  59. data/lib/woods/extractors/assigned_value_discovery.rb +74 -0
  60. data/lib/woods/extractors/class_declarations.rb +121 -0
  61. data/lib/woods/extractors/configuration_extractor.rb +11 -3
  62. data/lib/woods/extractors/declaration_ancestry.rb +92 -0
  63. data/lib/woods/extractors/event_extractor.rb +8 -0
  64. data/lib/woods/extractors/graphql_extractor.rb +134 -77
  65. data/lib/woods/extractors/job_extractor.rb +5 -1
  66. data/lib/woods/extractors/lib_extractor.rb +132 -15
  67. data/lib/woods/extractors/mailer_extractor.rb +3 -5
  68. data/lib/woods/extractors/manager_extractor.rb +7 -21
  69. data/lib/woods/extractors/migration_declaration.rb +87 -0
  70. data/lib/woods/extractors/migration_extractor.rb +5 -39
  71. data/lib/woods/extractors/phlex_extractor.rb +6 -2
  72. data/lib/woods/extractors/policy_extractor.rb +9 -5
  73. data/lib/woods/extractors/poro_extractor.rb +112 -53
  74. data/lib/woods/extractors/pundit_extractor.rb +11 -6
  75. data/lib/woods/extractors/scheduled_job_extractor.rb +45 -4
  76. data/lib/woods/extractors/serializer_extractor.rb +34 -22
  77. data/lib/woods/extractors/shared_utility_methods.rb +18 -1
  78. data/lib/woods/extractors/source_nesting.rb +142 -106
  79. data/lib/woods/extractors/standalone_module_discovery.rb +123 -0
  80. data/lib/woods/extractors/state_machine_extractor.rb +46 -40
  81. data/lib/woods/extractors/view_component_extractor.rb +9 -7
  82. data/lib/woods/flow_assembler.rb +4 -1
  83. data/lib/woods/generation.rb +25 -0
  84. data/lib/woods/hooks/context_hint.rb +7 -2
  85. data/lib/woods/mcp/bootstrapper.rb +33 -7
  86. data/lib/woods/mcp/config_resolver.rb +26 -7
  87. data/lib/woods/mcp/index_reader.rb +125 -24
  88. data/lib/woods/mcp/index_reader_pinning.rb +16 -0
  89. data/lib/woods/mcp/renderers/markdown_renderer.rb +7 -1
  90. data/lib/woods/mcp/renderers/plain_renderer.rb +3 -1
  91. data/lib/woods/mcp/search_results.rb +7 -1
  92. data/lib/woods/mcp/server.rb +24 -4
  93. data/lib/woods/module_reconciliation.rb +151 -0
  94. data/lib/woods/path_dispatcher.rb +7 -2
  95. data/lib/woods/rake_helpers.rb +43 -11
  96. data/lib/woods/release.rb +1 -1
  97. data/lib/woods/resilience/index_validator.rb +8 -3
  98. data/lib/woods/resilience/retryable_provider.rb +18 -1
  99. data/lib/woods/resolved_config.rb +68 -8
  100. data/lib/woods/retrieval/context_assembler.rb +3 -3
  101. data/lib/woods/retrieval/lexical_assembler.rb +3 -2
  102. data/lib/woods/retrieval/scope.rb +18 -2
  103. data/lib/woods/retrieval/source_evidence.rb +14 -2
  104. data/lib/woods/source_contributor_validation.rb +78 -0
  105. data/lib/woods/source_contributors.rb +116 -0
  106. data/lib/woods/source_inputs/handoff.rb +37 -0
  107. data/lib/woods/source_inputs/launcher.rb +53 -13
  108. data/lib/woods/source_inputs/manifest.rb +84 -3
  109. data/lib/woods/source_inputs/private_key.rb +44 -12
  110. data/lib/woods/source_inputs/scanner.rb +98 -27
  111. data/lib/woods/source_inputs/scopes.rb +1 -1
  112. data/lib/woods/source_inputs/session.rb +147 -15
  113. data/lib/woods/source_inputs/stable_reader.rb +127 -0
  114. data/lib/woods/source_inputs/status.rb +40 -8
  115. data/lib/woods/source_inputs/verifier.rb +28 -5
  116. data/lib/woods/source_path_encoding.rb +33 -0
  117. data/lib/woods/source_references/cache.rb +284 -0
  118. data/lib/woods/source_references/collector.rb +120 -0
  119. data/lib/woods/source_references/extraction.rb +185 -0
  120. data/lib/woods/source_references/inputs.rb +134 -0
  121. data/lib/woods/source_references/parser_adapter.rb +134 -0
  122. data/lib/woods/source_references/pass.rb +152 -0
  123. data/lib/woods/source_references/prism_adapter.rb +116 -0
  124. data/lib/woods/source_references/registry.rb +178 -0
  125. data/lib/woods/source_references/runtime_lookup.rb +127 -0
  126. data/lib/woods/source_references/value_class.rb +82 -0
  127. data/lib/woods/storage/metadata_store.rb +4 -1
  128. data/lib/woods/storage/qdrant.rb +2 -2
  129. data/lib/woods/unblocked/client.rb +12 -7
  130. data/lib/woods/unblocked/document_builder.rb +4 -1
  131. data/lib/woods/unblocked/exporter.rb +127 -37
  132. data/lib/woods/unblocked/sync_manifest.rb +137 -21
  133. data/lib/woods/unblocked/uri_migration.rb +105 -0
  134. data/lib/woods/util/host_guard.rb +3 -2
  135. data/lib/woods/version.rb +1 -1
  136. data/lib/woods/watch/catch_up.rb +138 -0
  137. data/lib/woods/watch/claim_lease.rb +150 -0
  138. data/lib/woods/watch/cli.rb +26 -2
  139. data/lib/woods/watch/daemon.rb +80 -59
  140. data/lib/woods/watch/installation/options.rb +1 -1
  141. data/lib/woods/watch/installation/receipt.rb +6 -1
  142. data/lib/woods/watch/managed_child.rb +1 -1
  143. data/lib/woods/watch/supervisor.rb +1 -1
  144. data/lib/woods/watch/tree_scan.rb +14 -2
  145. data/plugin/.claude-plugin/plugin.json +1 -1
  146. data/plugin/hooks/adapters/normalize.rb +3 -2
  147. data/plugin/hooks/woods-input-rules.sh +4 -0
  148. data/plugin/hooks/woods-refresh.sh +15 -7
  149. data/plugin/hooks/woods-session-start.sh +60 -3
  150. data/plugin/skills/woods-diagnose/SKILL.md +334 -11
  151. data/plugin/skills/woods-investigate/SKILL.md +11 -0
  152. data/plugin/skills/woods-mcp-config/SKILL.md +79 -8
  153. data/plugin/skills/woods-setup/SKILL.md +53 -6
  154. metadata +32 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: c00b317c5f1df618b940db9d8d2b596c27c1538b4d713d60c9263b1f0ab6acae
4
- data.tar.gz: b0fceeb5a3220efdd9d3f0f5dd77d9bc12e1d780ddc689dfd2121f163d9ec75d
3
+ metadata.gz: 999a4f9f01b4f8c86e560f47c1051233a4cafff30d463ae8051a50b363fb623b
4
+ data.tar.gz: f9e0c50a283eadde42b4034426c7258325fbad18205fa6016c5fb2e7b59fa1d7
5
5
  SHA512:
6
- metadata.gz: 0ec485c382f5a334d3607f2f18dd1489c02a6de17df0ba8922dd1d7271a36c8ffa1a71ec16346fe148460632e26f7ccbfc51c938ad96d1c8d03e68672d590993
7
- data.tar.gz: 011e559a5cbd6478d579c0fc477c020048352b75872daaab75a52df527dd0b2c0e27645f0627321b03a87c9844aa52b7b5f9fab0e90b7e6ec071a244c8697ef1
6
+ metadata.gz: 845c357cd67e5923bb283d075e608020ff0a7d8ce1df16c379fd6c197084d5ecb398e047d74951006741b14667414eadfb2cbf85fb2be890076df02cb1e12cf4
7
+ data.tar.gz: 0103d3cabd3c4e6eeccf13063470590f8ba93cfd6b1a12acb9728e70adb0e7f7fb89cddcd9048c3db048c3e2633dddf11b74ac53348abf542e242b3a5ee09298
data/CHANGELOG.md CHANGED
@@ -7,26 +7,113 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
- ## [2.0.1] - 2026-09-26
11
-
12
- ### Documentation
13
-
14
- - Clarify supported Console redaction, SQL projection, adapter timeout and HTTP origin behavior, including maintenance-line differences.
10
+ ## [2.1.0] - 2026-09-28
15
11
 
16
12
  ### Fixed
17
13
 
14
+ - Correct the generated development README banner after reopening a patch release: the next version is unreleased, while the existing stable gem remains installable with its released constraint (#555).
15
+ - Store app-owned event publisher/subscriber paths and ViewComponent sidecar paths relative to the application root, including event source annotations, so identical checkouts produce matching metadata and event source hashes while source reads and external paths retain their original locations.
16
+ - Restrict Phlex, ViewComponent and ActionCable discovery and direct extraction to application-owned runtime descendants. Dependency-only definitions and source-less framework classes are excluded consistently, and incremental reconciliation removes their stale units. Source containment now distinguishes sibling directories and treats dependency exclusions relative to the application root (#594).
17
+ - Resolve constant references to indexed library declarations on autoload-only paths when the exact registered constant and absolute source path match. Reference lookup still never executes autoloads or application methods; uncertain paths, namespaces, scopes, private access and typed collisions remain unresolved. Run a full extraction after upgrading to rebuild missing edges.
18
+ - Prefer an executable application bin/rake for the direct Console launcher, including a relative application directory; preserve explicit commands, remote defaults, and bundle exec rake fallback.
19
+ - Reject unsupported Console launcher configuration instead of silently ignoring it, count singular associations as zero or one through their guarded relation, and return a stable HTTP 503 after a Rails eager-load naming error until the application is fixed and restarted.
20
+ - Return zero for missing polymorphic association targets in Console association counts, and give a clear validation error for scoped polymorphic counts.
21
+ - Include metadata-only units in durable-store reconciliation, so deleting an empty-source unit removes its SQLite metadata under the existing mass-deletion safeguards.
22
+ - Keep embedding requests, stored vector widths, and cache identities consistent when restoring provider configuration or changing models, endpoints, or requested dimensions; refuse malformed or mismatched vectors before publication.
23
+ - Bound complete embedding inputs, including context prefixes, without silently truncating source. Known OpenAI models use a conservative UTF-8 byte-BPE bound; Ollama disables server truncation and reports strict refusals while local counts remain estimates. No tokenizer downloads occur during preparation. Oversized prefixes and unsplittable inputs fail with bounded unit diagnostics.
24
+ - Track preparation policy and ordered prepared-input fingerprints in embedding checkpoints so dependency, path, namespace and chunk changes invalidate stale vectors even when source hashes match. Old checkpoints require a one-time re-embed; completed durable batches remain resumable, and a changed unit is checkpointed only after its replacement and obsolete-chunk cleanup succeed. Unknown custom-adapter cleanup remains adapter-owned.
25
+ - Refuse incremental and targeted-refresh publication when a whole-app extractor raises before mutation or cannot initialize, even if sibling extractors succeed. The optional embedded `pipeline_extract` tool now reports failed full/incremental publication through its Tasks result instead of marking the task completed. Previous published generations remain active; retry the complete batch after fixing the cause. (#584)
26
+ - Reject changelog fragments without a leading list item before preparing a release, naming the invalid file and leaving the checkout unchanged.
27
+ - Index GraphQL schema classes, inherited resolver classes, and the runtime types of every application schema. Preserve distinct Ruby identities that share a GraphQL name, classify shared query roots consistently, and keep full, incremental, and targeted refresh results aligned when a schema changes a type's role. Failed schema introspection now reports the schema and preserves the prior published generation instead of publishing a partial inventory (#558, #562, #563).
18
28
  - Report invalidly encoded HTTP origin settings with bounded startup diagnostics; preserve existing origin defaults and access checks.
19
29
  - Use the same Console configuration error for malformed origins during automatic and manual middleware construction.
30
+ - Align HTTP preflight and SDK dispatch with one origin policy, normalize default ports, and reject malformed configured origins once at boot with a bounded diagnostic naming the entry. Explicit browser-origin allowlists replace loopback defaults.
20
31
  - HTTP startup diagnostics consistently name `WOODS_MCP_HTTP_ALLOWED_ORIGINS` for malformed origins, including under a POSIX (`C`) locale.
32
+ - Accept uniquely proven runtime job and serializer source moves while preserving complete-discovery and duplicate-source checks. Collision errors now name a fresh full extraction as recovery after an unproven move.
33
+ - Refresh the complete job and serializer inventories after Ruby source changes so indirect runtime inputs, such as a job queue read from another constant, cannot leave incremental metadata stale. Non-Ruby batches retain graph-scoped selection. (#638)
34
+ - Resolve incremental Git changes relative to the Rails application in nested repositories, normalize and contain explicit changed paths before filtering, and ignore blank CI range variables while retaining failures for invalid revisions. Document exact-baseline cache restore and full extraction on CI cold starts (#571).
35
+ - Preserve nested runtime-discovered jobs and serializers by reconciling their complete file/runtime union; refresh BehavioralProfile through resolved Rails configuration. Fresh one-shot incremental tasks escalate schema and boot inputs to full extraction, while embedded incremental callers receive an actionable fresh-boot refusal. Disabling flow precomputation withdraws old flow documents and controller annotations atomically. These corrections are unreleased after 2.0.0. (#588)
36
+ - Reject conflicting source ownership during incremental extraction and targeted refresh before publication, matching the full-extraction collision policy. Preserve the preceding generation on refusal, retain same-file re-derivation and independent cross-type identities, and allow confirmed file moves or authoritative wholesale source replacement. Repair previously overwritten indexes with a successful full extraction after fixing the producer or declarations (#561).
37
+ - Preserve effective settings of injected built-in embedding providers through snapshot restoration and known wrappers; omit unsafe endpoint metadata and require explicit host configuration when a provider cannot be reconstructed safely.
38
+ - Preserve the declared identity of behaviorful inline modules in unmanaged library directories, restoring typed lookup and source-reference edges. Inline namespace-only wrappers retain distinct nested identities; mixin modules and empty namespace preludes keep their existing handling. Run a full extraction after upgrading to replace affected path-derived identities. (#639)
39
+ - Preserve application Bundler settings during agent preflight, accept Git checkout permission differences in watcher ownership, keep project-scoped agent receipts independent of user configuration directories, and document safe temporary plan paths on hosts with symlinked `/tmp`.
40
+ - Prefer an executable application `bin/rake` in `woods-extract`, retaining `bundle exec rake` as the fallback. Recognize installed Bundler Git gems as external source while keeping local overrides and explicitly scoped roots in application freshness coverage.
41
+ - Refuse incremental extraction and targeted refresh over legacy flat manifests or generations written by Woods 1.x before creating a payload. Full extraction remains the migration path, and generation indexes without writer provenance remain compatible with early v2 betas.
42
+ - Aggregate compatible reopened library declarations under their existing constant identity while retaining every source contributor, physical source boundaries, per-file Git/package facts and dependency edges. Full, incremental and refresh extraction reconcile the complete library family; incompatible owners still refuse publication. Run a full extraction once after upgrading to rebuild versioned source-reference evidence.
43
+ - Preserve nested class identities in one-line library namespaces, keeping namespace and class units distinct and restoring their source-reference edges. Read complete source when selecting module owners so heredoc text cannot become a declaration. Run a full extraction after upgrading to replace affected identifiers.
44
+ - Decode library contributors as UTF-8 independently of the process locale, retaining publication refusal and per-file diagnostics for invalid source bytes.
45
+ - Report local-preset vector reloads as degraded with an explicit MCP restart remedy. A running server no longer reports successful zero-vector refresh while retaining an older vector snapshot beside live SQLite metadata.
46
+ - Accept the GraphQL family alias for exact MCP lookup while returning the concrete published type, and normalize uppercase hexadecimal snapshot SHA inputs before detail and diff lookup.
47
+ - Preserve concrete search result types across scoped and unscoped family filters; support encoded slash-bearing unit resources; distinguish missing flow units and snapshots from valid empty answers; and report repeated malformed generation reads without exposing raw exceptions or falling back to unrelated payloads.
48
+ - Honor the owning Rails `main` or `once` autoloader when naming declared source constants, preserving loader-specific inflections, namespaces, collapses, ignored paths and copied-root ambiguity. This prevents once-autoloaded library children from colliding under their shared wrapper identifier. Rebuild affected indexes with one full extraction after upgrading. (#579)
49
+ - Publish a usable code index when freshness evidence alone exceeds its size limit, with bounded `unavailable` status, stable `source_manifest_too_large` reason, measured bytes and limit. Preserve verified source-reference reuse for incremental work, keep watcher catch-up bounded, and avoid recommending an identical full rebuild in status or hooks; corrupt evidence, failed writes and unstable source retain their safeguards.
50
+ - Preserve source-consumption evidence for successfully extracted models when another model fails, so an optional framework model with a missing table does not prevent unrelated incremental extraction or refresh. The failed extractor remains explicitly unverified; failed models and changed retained source are not certified (#568).
51
+ - Accept incremental source-ownership moves out of surviving files when completed discovery proves a unique replacement. Collect changed-file candidates before registration and pruning so file order cannot decide acceptance, while preserving collision and incomplete-loading safeguards (#574).
52
+ - Read typed and bulk published units as UTF-8 regardless of the process locale, restoring typed lookup, scoped search, exports, and lexical MCP startup for indexes with non-ASCII identifiers or source (#570). Reject malformed UTF-8 without replacing source bytes.
53
+ - Block Bundler source-control publication tasks, reject nested changelog entries before preparation, include published-index checks in surface inventory, validate release text as UTF-8 under any locale, and correct transition/recovery guidance. No release transition or publication occurs as part of these fixes.
54
+ - Preserve every repeated method definition when chunking singleton class bodies, nested classes, and inlined overrides. Later definitions receive deterministic occurrence suffixes instead of overwriting earlier source.
55
+ - Restore ordinary Ruby `Resolvers::Base` subclasses omitted by runtime GraphQL discovery. Verify loaded ancestry and preserve their historical `graphql_type` identities during full extraction, incremental updates, and refresh. Run a full extraction to recover helpers missing from an earlier 2.1 development index.
56
+ - Preserve schedules with the same task name across Solid Queue, Sidekiq-Cron and Whenever. Only conflicting identifiers gain a deterministic format qualifier; unique identifiers stay unchanged. Full, incremental, refresh and file extraction use the same allocation. Ambiguous task keys within one format fail before publication instead of overwriting a schedule (#594).
57
+ - Verify source inputs through stable directory symlinks under their logical application paths, so unrelated static-assets links no longer prevent first publication. Retain distinct aliases, exclude output aliases, bound cycle traversal and refuse changing links or scoped external files before reading their contents. Run a fresh full extraction after upgrading to establish the updated source-capture rules (#653).
58
+ - Encode Unblocked branch citations, retain separate repository/ref ownership scopes, and provide resumable explicit URI migration with a no-write preview. Refuse foreign-collection URI overwrites and incomplete pagination; preserve unknown remote documents and report incomplete cleanup. The upgraded manifest is version 2; keep a backup and one writer per local manifest and remote scope.
59
+ - Avoid reading every GraphQL and framework unit during unscoped identifier searches; use published summary types and resolve legacy types only when needed. Searches retain readable matches and report partial coverage when an individual candidate unit is unreadable or corrupt.
60
+ - Use owned runtime ancestry for manager and Pundit classification, select actual migration declarations without executing historical files, and retain distinct class/instance method chunks with punctuation and operator names. Migration helper siblings no longer supply identity or DDL metadata; ambiguous declarations are reported and duplicate identities retain the publication collision guard.
61
+ - Keep source-freshness evidence bounded and scoped to the checked checkout: enforce the 16 MiB reader limit for source evidence and launcher handoffs, distinguish installed gems from uncaptured application source, retain accessible scan results after entry errors, and avoid invented additions/deletions from missing or incomplete baselines. Status exposes root provenance and separate deep-check versus fresh-capture advice; copied-index task checks default to the invoking application directory.
62
+ - Remove vanished SQLite metadata during full and incremental embedding with dump-backed vectors, including source-empty and typed records, while retaining incremental purge guards. Restore vector type filters from the same SQLite metadata store used by standalone retrieval (#572).
63
+ - Extract default-attribute and parenthesized `state_machines` declarations, including multiline arguments, without changing named-machine identifiers. Keep each declaration's states, events and initial state within its own block; do not execute callbacks or infer dynamic attribute names.
64
+ - Skip Unblocked inventory documents without a usable URI. Report incomplete synchronization with a migration or manual-cleanup remedy when legacy ownership remains unresolved, including stale legacy receipts; URI prefixes alone never authorize deleting another ref's documents.
65
+ - Name bounded, escaped paths and verification reasons when source-reference publication refuses undecodable filenames, while keeping the preceding generation active.
66
+ - Handle undecodable filenames without crashing source capture or polling: keep source evidence incomplete, bound escaped diagnostics, and retain the previous generation when reference verification fails. Preserve valid UTF-8 paths under the C locale.
67
+ - Preserve Unicode roots, output directories and edit paths through Ruby-only refresh and SessionStart hooks under the C locale. Keep failed batches queued and sanitize malformed JSON/encoding diagnostics (#592).
68
+ - Add explicit token-checked recovery for abandoned managed watcher claims protected by lifetime filesystem leases, preserving live owners and permanent coordination files during cleanup. Treat blank idle timeouts consistently, resolve boot-free check defaults beside the selected Rakefile, refuse implicit verified-launch output mismatches before publication, and report safe, actionable identity-key diagnostics.
69
+ - Catch source edits made between extraction capture and publication when watch starts, including the interval after final verification and whole-second file timestamps. Compare candidate content with the published source identities to avoid repeating unchanged work, and reconcile older indexes without capture metadata once before using the new boundary (#585).
70
+ - Keep deletion-only watcher startup reconciliation queued after extraction, lock, or publication failures, including when no source paths can safely be named (#640). Check publication errors before accepting empty-touch incremental runs, so failed withdrawal of disabled flow artifacts reports degraded status and retries the original event (#641).
71
+ - Preserve the canonical identities of loaded Struct/Data classes assigned inside class or module wrappers under `app/models` and `lib`, instead of omitting them or colliding on the wrapper name. Verified assigned classes also participate as source-reference targets; constructor block bodies remain outside reference coverage. Run a full extraction after upgrading to repair earlier identities and rebuild the version 3 source-reference cache; older caches refuse incremental publication (#559).
72
+
73
+ ### Added
74
+
75
+ - Discover all application-owned `ActionMailer::Base` descendants, including `ApplicationMailer`, direct framework subclasses and application subclasses of external mailer bases. Their defaults, layouts, callbacks and actions remain reflective: extraction does not deliver mail or invoke callback/default procs (#594).
76
+ - Add internal constant-reference collection and conservative resolution foundations for PORO and library dependency coverage (#475). The integrated pass described below adds these verified references to the published graph.
77
+ - Add conservative method-body constant references from models, controllers, services, POROs, library units and concerns to verified indexed targets (#475). Publish forward/reverse relationships and cached unresolved candidates atomically; incremental extraction and refresh update unchanged callers when targets appear or disappear. Supporting writers require a full extraction to establish a reference-cache baseline when upgrading older indexes. They refuse publication if captured source changes during boot/extraction or a scoped refresh leaves changed reference-bearing Ruby units unprocessed, retaining the preceding generation. Dynamic or ambiguous references remain outside the declared coverage.
78
+ - Discover callable standalone modules under `app/models` as `poro` units with `ruby_kind: module` metadata, using verified runtime and source ownership. Namespace-only wrappers and uncertain owners remain excluded; runtime model mixins retain concern ownership. Run a full extraction after upgrading to populate these units and their source references (#552).
79
+ - Add `release:retarget` for moving an untagged alpha to the next minor development line, preserving changelog entries and refusing dirty or previously tagged version lines.
80
+
81
+ ### Build
82
+
83
+ - Add an exact-candidate 1.6.4 maintenance release profile and select the legacy package SDK floor through trusted release metadata, preserving published 1.6.3 and normal v2 controls.
84
+ - Add a 2.0.1 maintenance release profile bound to its exact branch and 2.0.0 base, retaining normal v2 CI and package tests and revalidating every maintenance profile after release approval.
85
+
86
+ ### Documentation
87
+
88
+ - Mark included capabilities as Woods 2.1 in the public guides, align the upgrade baseline with reference-cache format 3, and link the published Console security advisory. Plugin availability checks remain independent of release preparation.
89
+
90
+ - Clarify v1-to-v2 configuration changes, snapshot preservation across cleaning, retained runtime freshness evidence, early middleware setup, structural readiness and custom-process MCP capabilities. These notes describe existing behavior; they do not add tools or restore unsafe eval.
91
+ - Clarify supported Console redaction, SQL projection, adapter timeout and HTTP origin behavior, including maintenance-line differences.
92
+ - Update plugin setup, MCP configuration, and diagnosis guidance for Woods 2.0.1 and the 1.6.4 maintenance patch, with version-specific Console compatibility links and separate checks for unreleased 2.1 capabilities.
93
+ - Clarify search completeness, GraphQL lookup aliases, local MCP reload limits, explicit Unblocked legacy-ref migration, extraction launcher selection, and legacy index write guards in public guides and agent skills. Explain usable code indexes with oversized source evidence and the `inspect_source_limits` remedy without promising freshness or recommending repeated identical rebuilds.
94
+ - Pin the reviewed 1.6.4 and 2.0.1 maintenance candidates after complete protected-branch CI; retain exact-artifact, fresh tag CI, and protected publication gates.
95
+
96
+ ### Performance
97
+
98
+ - Scope incremental job and serializer reconciliation to relevant paths, affected units and newly nested runtime classes. Identical registered unit bytes no longer enter touched, Git-enrichment or dependents-update sets.
21
99
 
22
100
  ### Security
23
101
 
102
+ The Console protections below include the fixes already published in Woods 2.0.1 and 1.6.4; see [GHSA-wxxx-6hqc-qm8g](https://github.com/lost-in-the/woods/security/advisories/GHSA-wxxx-6hqc-qm8g).
103
+
24
104
  - Refuse unsupported PostgreSQL escaped identifiers in raw Console SQL before query execution. Ordinary quoted identifiers, literal contents, and comments retain their existing behavior.
25
105
  - Share validated Console query projections between execution and typed EAV redaction context, including whitespace normalization.
106
+ - Strengthen Console SQL result provenance and protected query-input checks across supported database families. Preserve EAV redaction for typed keys and keep independent table patterns compatible.
107
+ - Apply and restore the supported MariaDB statement timeout through MySQL-family adapters.
26
108
  - Refuse SQL relation and CTE column alias lists while output redaction is configured, with an explicit redaction-identity error before execution. Direct unaliased projections remain supported.
27
- - Harden Console request validation, protected result handling, and HTTP authentication and origin enforcement.
109
+ - Tighten Console SQL policy, adapter compatibility, and whole-row and multi-source redaction while retaining supported structured reads. See the repository security advisories for release details.
110
+ - Strengthen per-mount Console transport guards and protected-value rendering. See the repository security advisories for release details.
28
111
  - Apply typed key/value redaction consistently to case-variant and schema-qualified source table names, retaining all matching model types when source schemas are ambiguous.
29
- - Isolate cached retrieval contexts between application instances sharing a cache backend.
112
+ - Strengthen retrieval context isolation and reload behavior on shared cache stores. See the repository security advisories for release details.
113
+
114
+ ### Testing
115
+
116
+ - Extend the POSIX-locale CI lane to extractor, embedding and plugin regressions, and isolate update-check specs from the caller's `WOODS_NO_UPDATE_CHECK` setting.
30
117
 
31
118
  ## [2.0.0] - 2026-09-23
32
119
 
data/CONTRIBUTING.md CHANGED
@@ -1,7 +1,7 @@
1
1
  # Contributing to Woods
2
2
 
3
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.1/AGENTS.md).
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.1.0/AGENTS.md).
5
5
  <!-- release-state:end -->
6
6
 
7
7
  ## Choose the right channel
@@ -13,6 +13,22 @@ Woods welcomes bug fixes, extractor coverage, storage and retrieval improvements
13
13
 
14
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.
15
15
 
16
+ ### Work tracking
17
+
18
+ [GitHub issues](https://github.com/lost-in-the/woods/issues) own active work,
19
+ acceptance criteria and resolution evidence. Projects organize those issues.
20
+ Use `release-gate` for required work on the next combined candidate,
21
+ `post-release` for deferred work, and `needs-decision` for maintainer choices.
22
+ Recommended cleanup may share a release milestone without being a blocker.
23
+
24
+ The old `docs/backlog.json` queue is retired. `.Codex/backlog-archive.json`
25
+ preserves every B-ID, completed record, approved decision and issue migration
26
+ link. `tracked-on-github` is a migration state, and `not-planned` records a
27
+ declined proposal; neither claims a bug was fixed. The original v2 snapshot lives at
28
+ `.Codex/release-v2/backlog-archive.json`; its statuses are historical. New work
29
+ belongs in issues rather than another local JSON queue. Preserve evidence when
30
+ closing an issue, and distinguish fixed defects from declined proposals.
31
+
16
32
  ## Development setup
17
33
 
18
34
  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.
@@ -46,7 +62,7 @@ Create a branch from current `main`. Keep each pull request to one logical chang
46
62
  | `plugin/skills/` | Distributed Woods skills (setup/upgrade, MCP configuration, investigation, agent enablement, diagnosis) |
47
63
 
48
64
  <!-- release-state:contributing-architecture -->
49
- Read [CLAUDE.md](https://github.com/lost-in-the/woods/blob/v2.0.1/CLAUDE.md) for architecture and implementation gotchas before changing runtime behavior.
65
+ Read [CLAUDE.md](https://github.com/lost-in-the/woods/blob/v2.1.0/CLAUDE.md) for architecture and implementation gotchas before changing runtime behavior.
50
66
  <!-- release-state:end -->
51
67
 
52
68
  ### Agent orientation and static self-map
@@ -103,8 +119,7 @@ Coverage from the default process excludes opt-in Rails, installed-artifact, and
103
119
  CI fails when a running example becomes unexpectedly pending, including `skip`,
104
120
  `xit`, and pending metadata. `spec/support/pending_policy.rb` records the exact
105
121
  file, full description, reason, and unavailable capability for each reviewed
106
- exception. Current exceptions cover the two optional tiktoken benchmarks, one
107
- optional Tokenizers example, two Ruby-before-3.2 regexp-timeout examples, one
122
+ exception. Current exceptions cover the two optional tiktoken benchmarks, two Ruby-before-3.2 regexp-timeout examples, one
108
123
  procfs example, and three filesystem-permission examples when running as root.
109
124
  The Linux CI unit jobs run the real procfs identity example as an unprivileged
110
125
  user, so the procfs and root exceptions normally apply only to other environments.
@@ -285,7 +300,7 @@ For Git-sourced candidates, record the locked Git revision, loaded gem path, and
285
300
  - Put changelog entries under `## [Unreleased]`, beneath one of its `###` headings, or in an optional `changelog/<type>_<slug>.md` file. Entry files avoid conflicts between parallel branches; inline entries remain supported. Duplicate headings merge at release time, in the order they first appear.
286
301
 
287
302
  Entry files contain nonempty UTF-8 Markdown without ATX (`#`) or setext
288
- (underlined) headings, usually a bullet
303
+ (underlined) headings, starting with a `- ` list item
289
304
  and indented continuation lines. For example, `changelog/fixed_watch-restart.md`
290
305
  can contain `- Preserve pending work across watch restarts.` Supported types are
291
306
  `added`, `build`, `changed`, `dependencies`, `documentation`, `fixed`,
@@ -293,8 +308,9 @@ can contain `- Preserve pending work across watch restarts.` Supported types are
293
308
  lowercase letter or digit and use lowercase letters, digits, hyphens, or
294
309
  underscores. Use one unique file per change; do not copy its entry into
295
310
  Unreleased as well. Keep entry files directly inside a real `changelog/`
296
- directory; symlinks and directories masquerading as entries are refused.
297
- Other file extensions are left untouched.
311
+ directory; all nested directories and symlinks are refused before preparation,
312
+ including ones whose names do not end in `.md`. Regular files with other
313
+ extensions are left untouched.
298
314
 
299
315
  `release:prepare` appends entry files in filename order after inline Unreleased
300
316
  entries, folds them through the same heading merger, and deletes exactly the
@@ -305,6 +321,10 @@ an ordinary beta cycle, entry files may accumulate even with an empty Unreleased
305
321
  VERSION stays at the previous beta; the tag validator always rejects entry
306
322
  files at the candidate release SHA, regardless of inline notes or the version.
307
323
 
324
+ The Bundler tasks `release`, `release:rubygem_push`, and
325
+ `release:source_control_push` are blocked, including their prerequisites.
326
+ Use the preparation tasks below; tagging and publishing remain maintainer steps.
327
+
308
328
  ### Preparing a release
309
329
 
310
330
  One command per transition. It never commits, tags, pushes, or publishes.
@@ -320,6 +340,14 @@ One command per transition. It never commits, tags, pushes, or publishes.
320
340
 
321
341
  `release:reopen` accepts only a final release and a strictly later alpha. It does not reopen a beta/RC or move the same version line backwards to alpha. Continue prerelease development with Unreleased notes or changelog fragments, then use `release:prepare` for the next forward beta, RC, or final when authorized.
322
342
 
343
+ When an unpublished alpha needs a minor release instead of a patch, use
344
+ `bin/rake "release:retarget[2.1.0.alpha]"` from `2.0.1.alpha`. Retarget accepts
345
+ only the next minor `X.Y.0.alpha`, refuses a dirty checkout or any local release
346
+ tag for the current or target line, and leaves changelog entries untouched.
347
+ Fetch release tags before using it. It updates VERSION and the generated fences
348
+ without committing, tagging or publishing. Retarget main before tagging a
349
+ separate maintenance release from its previous development line.
350
+
323
351
  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 without entry files refuses: there is nothing new to publish.
324
352
 
325
353
  Review the diff and run the release contracts:
@@ -397,9 +425,13 @@ on always matches the bytes RubyGems published.
397
425
 
398
426
  ### One-off 1.6.3 security maintenance release
399
427
 
428
+ This section records the published 1.6.3 preparation flow. Its approved SHA,
429
+ base and test profile remain fixed; new 1.6.x work uses the separate 1.6.4
430
+ profile below rather than changing the published candidate.
431
+
400
432
  The [security policy](SECURITY.md#supported-versions) supports 1.6.x security
401
- fixes until 2027-02-20. While main develops v2, the sole maintenance exception
402
- is `v1.6.3` from the short-lived `release/1.6.3` branch, descending from the
433
+ fixes until 2027-02-20. The legacy maintenance exception is `v1.6.3` from the
434
+ short-lived `release/1.6.3` branch, descending from the
403
435
  immutable v1.6.2 commit `4b40e17fd68122a70ccf00d9d2ffb8af42171d3d`.
404
436
  This is a stable patch, separate from the next v2 prerelease; it does not declare
405
437
  v2 final or establish a general-purpose maintenance publishing path.
@@ -460,19 +492,102 @@ fresh tag-push CI run. Updating main's tooling alone never authorizes different
460
492
  candidate bytes. Main's v2 release contract remains unchanged. Do not create or
461
493
  push tags, dispatch, publish, or claim 1.6.3 is available during preparation.
462
494
 
463
- ### 2.0.1 maintenance CI contract
464
-
465
- The 2.0.1 candidate uses trusted main's reviewed maintenance profile, not this
466
- candidate's copy of the release tooling. That profile intentionally omits
467
- `exact_ci_jobs` and `package_spec`: validation uses the normal v2 required-job
468
- list and `spec/integration/packaged_gem_spec.rb`, rather than the legacy matrix
469
- or package spec. The 1.6.4 profile is separate and has no C-locale artifact-reader
470
- CI lane. Candidate evidence must identify the actual jobs and artifacts tested;
471
- profile approval and publication remain maintainer steps.
495
+ ### One-off 1.6.4 security maintenance release
496
+
497
+ The next legacy maintenance profile allows only `v1.6.4` from `release/1.6.4`,
498
+ descending from the immutable published v1.6.3 commit
499
+ `60d6b7c4a3ddc421073f1fb57a7249eccb77826e`. Its approved candidate is
500
+ `11672856b09d30b9fb3ac40c17aa336e795b91e7`, pinned by `V1_PATCH_APPROVED_SHA`
501
+ in trusted main's `script/release_profile.rb`. All 19 required jobs passed in
502
+ [protected-branch CI run 36205164768](https://github.com/lost-in-the/woods/actions/runs/36205164768).
503
+ The published 1.6.3 pin and the separate 2.0.1 profile remain independent.
504
+
505
+ 1. Before creating the remote `release/1.6.4` target, require pull requests and
506
+ prevent force pushes and deletion through its effective branch rules. Create
507
+ it from the fixed v1.6.3 commit and keep the backport limited to that line.
508
+ 2. Use the inherited legacy `release:reopen[1.6.4.alpha]` and
509
+ `release:prepare[1.6.4]` adapter in clean, separately reviewed commits. Keep
510
+ the old automatic publisher disabled. Do not hand-edit VERSION or transplant
511
+ v2 documentation fences. The candidate PR targets `release/1.6.4`.
512
+ 3. Review the candidate's own CI definition, branch trigger and package tests.
513
+ Adding a branch trigger on main does not update the legacy branch. Require
514
+ every exact legacy matrix row listed in trusted main's maintenance profile:
515
+ unit, booted Rails, installed package, lint, coverage, security and build,
516
+ plus the 1.6.4-only `Maintenance security backends` job with real PostgreSQL
517
+ and MySQL. The published 1.6.3 job list stays unchanged.
518
+ After upstream branch CI passes, pin its exact prepared SHA through a
519
+ separate reviewed main PR. Private qualification does not replace this CI.
520
+ 4. Only after that pin merges may the maintainer tag the candidate and dispatch
521
+ its successful **tag-push** CI run. Both package rows test the same immutable
522
+ artifact using `maintenance_packaged_gem_spec.rb`. The trusted profile emits
523
+ MCP `0.23.0` for the Ruby 3.0 row; the newer Ruby row resolves the supported
524
+ SDK range. v2 profiles emit no legacy SDK floor and use normal v2 tests.
525
+
526
+ All branch/base, exact SHA, tag/VERSION/changelog, unpublished-version,
527
+ immutable-artifact and protected-environment checks apply. Maintenance history
528
+ and publication checks repeat after approval, before RubyGems credentials; the
529
+ remote tag is verified immediately before push. A changed candidate requires a
530
+ new reviewed pin and fresh CI. Keep private qualification and advisory details
531
+ private until the fixed gems are available. The approved pin does not itself
532
+ publish a release or change live branch rules; the remaining gates still apply.
533
+
534
+ ### One-off 2.0.1 security maintenance release
535
+
536
+ The separate v2 maintenance profile allows only `v2.0.1` from `release/2.0.1`,
537
+ descending from the immutable v2.0.0 commit
538
+ `838252a79b89846937be6dbd21e283fa7cad897f`. Its approved candidate is
539
+ `07442a730c3d8f305711d81b3242bcb136433bd3`, pinned by `V2_MAINTENANCE_APPROVED_SHA`
540
+ in trusted main's `script/release_profile.rb`. All 24 required jobs passed in
541
+ [protected-branch CI run 36205538304](https://github.com/lost-in-the/woods/actions/runs/36205538304).
542
+ The pin does not publish a release; fresh tag CI and artifact checks still apply.
543
+ The legacy 1.6.3 and 1.6.4 profiles, SHA pins, CI rows and SDK floors are independent.
544
+
545
+ 1. Merge the trusted tooling and complete the separately reviewed move of main
546
+ to the 2.1 development line before a 2.0.1 tag exists. Keep the minimal 2.0.1
547
+ patch separate from main's accumulated development changes.
548
+ 2. Before creating the remote `release/2.0.1` branch, require pull requests and
549
+ prevent force pushes and deletion through its effective branch rules. Create
550
+ it from the fixed v2.0.0 commit, then review the narrow backport and release
551
+ controls on that line.
552
+ 3. Use the supported `release:reopen[2.0.1.alpha]` and
553
+ `release:prepare[2.0.1]` tasks in clean, separately reviewed commits. Never
554
+ edit VERSION or generated fences by hand. The generic preparation report's
555
+ instruction to merge into main does **not** apply to this candidate: its PR
556
+ targets `release/2.0.1`.
557
+ 4. Require the normal v2 CI contract, including live backends, MCP transports
558
+ and minimum dependencies, and review the complete candidate CI and package
559
+ tests. The maintenance branch needs the reviewed v2 CI definition with its
560
+ branch trigger; adding that trigger on main alone does not update the branch.
561
+ After upstream branch CI passes, pin its exact final SHA through a separate
562
+ reviewed main PR. A private report is not a substitute for upstream CI.
563
+ 5. Only after that pin merges may the maintainer tag the exact candidate and
564
+ dispatch with its successful **tag-push** CI run ID. The release workflow
565
+ runs normal `spec/integration/packaged_gem_spec.rb` on the same immutable
566
+ artifact in both Ruby/Rails package rows. It does not select the v1 job set,
567
+ legacy package spec or MCP 0.23.0 exception.
568
+
569
+ All maintenance profiles require fresh branch/base and tag checks, exact
570
+ VERSION/changelog agreement, an unpublished version, an immutable CI artifact
571
+ and protected environment approval. They repeat history/publication validation
572
+ after approval, before requesting RubyGems credentials, and verify the remote
573
+ tag immediately before push. A changed candidate needs a new reviewed main pin
574
+ and fresh tag-push CI. Keep the advisory unpublished until fixed gems are
575
+ available. Tags, dispatch, publication and live branch rules remain maintainer
576
+ steps; this runbook does not execute them.
577
+
578
+ ### Maintenance CI compatibility
579
+
580
+ The 1.6.4 profile intentionally has no C-locale artifact-reader CI lane; its
581
+ legacy matrix and installed maintenance-package contracts define its validation.
582
+ The trusted-main 2.0.1 profile omits `exact_ci_jobs` and `package_spec`, so it
583
+ inherits the normal v2 required-job list and
584
+ `spec/integration/packaged_gem_spec.rb`. An omitted override does not select
585
+ legacy jobs or waive validation. Review candidate CI against trusted-main
586
+ `script/release_profile.rb` and `script/validate-release-run`.
472
587
 
473
588
  ### Stable branches
474
589
 
475
- 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 development branch. The explicitly approved short-lived `release/1.6.3` security exception above does not establish an `N-M-stable` branch.
590
+ 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 development branch. The explicitly approved short-lived maintenance exceptions above do not establish an `N-M-stable` branch.
476
591
 
477
592
  ### What coding agents may do
478
593
 
data/README.md CHANGED
@@ -51,7 +51,7 @@ These steps are for **Woods 2.x**. Choose a published 2.x version from [RubyGems
51
51
  <!-- release-state:version-banner -->
52
52
  <!-- release-state:end -->
53
53
 
54
- Once a stable 2.x release is published, add `gem "woods", "~> 2.0"` to your Gemfile's `:development` group. For a prerelease, use its exact published version instead. Then run:
54
+ Add `gem "woods", "~> 2.1"` to your Gemfile's `:development` group for the 2.1 line. For an older release or a prerelease, use its published version and matching tag documentation. Then run:
55
55
 
56
56
  ```bash
57
57
  bundle install
data/docs/AGENT_GUIDE.md CHANGED
@@ -86,6 +86,10 @@ Bare names identify units, not methods across the application. For example,
86
86
  be a FactoryBot factory in `spec/factories/`. Find the owning class with
87
87
  `search` and `lookup`, then pass its identifier with `#order`.
88
88
 
89
+ **Included in Woods 2.1 (#593):** an unknown indexed flow unit returns
90
+ `not_found`; a known unit with no discovered operations remains a successful
91
+ empty flow. Neither outcome establishes whether an unindexed caller exists.
92
+
89
93
  Flow assembly derives operations from source. A receiverless local call such
90
94
  as `order` inside `CheckoutService#call` may remain visible without expanding
91
95
  the local method body. Follow it in source or trace `CheckoutService#order`
@@ -150,6 +154,10 @@ do not establish absence of production callers. Check source before claiming abs
150
154
  The `graph_coverage` notice makes this scope explicit in supporting responses;
151
155
  that metadata is included in Woods `2.0.0`.
152
156
 
157
+ A supporting post-2.0 writer recovers additional [constant source references](EXTRACTOR_REFERENCE.md#constant-source-references)
158
+ (included in Woods 2.1). Upgrading the reader alone cannot add relationships
159
+ to an old index. The coverage warning still applies.
160
+
153
161
  Start at depth 1 or 2. A deeper unfiltered traversal can obscure the direct evidence that matters. Common relationship values include associations (`belongs_to`, `has_many`, `has_one`), code references, renders, redirects, form actions, and navigation links.
154
162
 
155
163
  Both return at most 50 nodes by default and say so with a `Showing N of M (truncated)`
@@ -322,3 +330,14 @@ on the evidence. Verify direct and inferred downstream candidates using typed
322
330
  lookup and `dependents explain:true`; suggested tests do not prove coverage.
323
331
  Silence does not establish no impact. See [bounded context hints](WATCH_DAEMON.md#optional-bounded-context-hints)
324
332
  for opt-in, independent refresh controls, limits and repeat suppression.
333
+
334
+ ### Library units with several source files
335
+
336
+ In supporting builds (included in Woods 2.1), `source_contributors` identifies
337
+ all physical files behind an aggregate library unit. Treat `file_path` as the
338
+ primary display path. Use each contributor's own SHA256 and coordinates when
339
+ citing it; the composite source hash is not the hash of the primary file.
340
+ Generated headers and spans crossing contributors have no physical location.
341
+ Per-file Git facts are separate, and a missing aggregate churn count does not
342
+ mean the library has no history. Package/path scope must include all contributors
343
+ to return the aggregate. See [library extraction](EXTRACTOR_REFERENCE.md#libextractor).
data/docs/AGENT_SETUP.md CHANGED
@@ -140,9 +140,10 @@ client format is Claude Code (tested with 2.1.267).
140
140
  Create a private plan, inspect its paths and diff, then apply that same plan:
141
141
 
142
142
  ```bash
143
+ woods_plan_dir="$(ruby -rtmpdir -e 'puts File.realpath(Dir.mktmpdir("woods-agent-plan-"))')"
143
144
  bundle exec woods-agent-config setup --client claude --scope project \
144
- --root "$PWD" --instructions CLAUDE.md,AGENTS.md --plan /tmp/woods-setup.json --diff
145
- bundle exec woods-agent-config apply /tmp/woods-setup.json \
145
+ --root "$PWD" --instructions CLAUDE.md,AGENTS.md --plan "$woods_plan_dir/setup.json" --diff
146
+ bundle exec woods-agent-config apply "$woods_plan_dir/setup.json" \
146
147
  --client claude --scope project --root "$PWD"
147
148
  ```
148
149
 
@@ -153,6 +154,10 @@ keep them private and remove them when no longer needed. `show FILE` prints its
153
154
  summary; `show FILE --diff` checks the original snapshots and prints a unified
154
155
  diff. Applying a changed snapshot fails rather than replacing the new content.
155
156
  A repeated identical setup makes no configuration edits.
157
+ The example creates a private temporary directory and resolves its real path:
158
+ on macOS, `/tmp` commonly points through a symlink. Plan files and managed
159
+ targets reject symlink components; use the real parent directory for both
160
+ preview and apply, and never use a symlink as the plan file itself.
156
161
 
157
162
  | Selection | Managed files |
158
163
  |---|---|
@@ -165,6 +170,15 @@ With `CLAUDE_CONFIG_DIR`, user scope uses that directory's `.claude.json`,
165
170
  configures the Index Server. Client trust and project approval remain Claude
166
171
  Code settings; apply does not change them.
167
172
 
173
+ Included in Woods 2.1: project ownership ignores the user's home and
174
+ `CLAUDE_CONFIG_DIR`, including that unused field in older receipts and plans.
175
+ The application root, client, scope, target paths, and original file snapshots
176
+ must still match. User-scoped ownership remains tied to its user configuration
177
+ paths. Record the loaded revision before relying on this compatibility fix.
178
+ Older executables cannot consume newly written project identities. Keep a
179
+ supporting Woods revision for updates/removal, or remove owned configuration
180
+ with it before permanently downgrading.
181
+
168
182
  Preflight runs the selected installed bundle, validates its index, and checks
169
183
  its actual registered capabilities. It does not boot Rails or contact an
170
184
  embedding provider. The bundle must already resolve in frozen mode; prepare
@@ -175,6 +189,12 @@ to the selected root. For Compose, also select `--mode compose --service web
175
189
  access that project. Preflight verifies the index and installed gem inside that
176
190
  service. Both host and container subprocesses have time limits.
177
191
 
192
+ Included in Woods 2.1: host preflight preserves intended `BUNDLE_PATH`,
193
+ `BUNDLE_APP_CONFIG`, and `BUNDLE_WITHOUT` settings while clearing activation of
194
+ the caller's bundle and selecting the application's Gemfile. Older builds can
195
+ report missing gems that the application already has; check the loaded revision
196
+ and environment before reinstalling dependencies or writing bundle settings.
197
+
178
198
  Use `update --plan FILE` with the same client/scope/root and the desired launch
179
199
  options to change the owned entry or instruction selection. Update explicitly
180
200
  records the current template and installed-gem evidence; background hooks never
@@ -27,6 +27,13 @@ The shape determines the capability matrix:
27
27
  | Cross-machine query | After deploying/copying `output_dir` | Yes, when the filesystem is shared | External vectors are shared; metadata/config still require a shared or deployed `output_dir` |
28
28
  | `woods.json` schema-versioned config snapshot | Yes | Yes | Yes |
29
29
 
30
+ **Included in Woods 2.1:** `:local` cannot atomically reload snapshot vectors
31
+ alongside its SQLite metadata in a running MCP server. `reload` reports degraded
32
+ and retains the previous aligned state; restart `woods-mcp` after `woods:embed`
33
+ to load the latest vectors. Write access alone does not resolve this limitation.
34
+ The in-memory stores used by `:shared_filesystem` support transactional reload
35
+ under the writer lock. See [MCP reload requirements](MCP_SERVERS.md#index-server).
36
+
30
37
  `Builder#build_vector_store` accepts exactly `:in_memory`, `:pgvector`, `:qdrant`, anything else raises `ArgumentError: Unknown vector_store`. `build_metadata_store` accepts `:in_memory`, `:sqlite`. `build_graph_store` accepts `:in_memory` only. Presets are `:local`, `:shared_filesystem`, `:postgresql`, and `:production`.
31
38
 
32
39
  ---
data/docs/CLIENT_HOOKS.md CHANGED
@@ -79,6 +79,12 @@ deadline, locks and active-daemon behavior described in
79
79
  inside the application container. The host needs Bash 3.2 or later, Unix tools,
80
80
  and either jq or Ruby; OpenCode supplies its own JavaScript runtime.
81
81
 
82
+ The Unicode Ruby-fallback repair (#592, included in Woods 2.1) explicitly
83
+ decodes hook JSON and paths as UTF-8, including under `LC_ALL=C`. It covers edit
84
+ queues, legacy queue import and Claude SessionStart checks. With older plugin
85
+ hooks, install jq or use a UTF-8 locale; a zero hook exit status alone does not
86
+ confirm that an edit was queued or refreshed.
87
+
82
88
  The complete event is validated before queueing. Empty/NUL paths, traversal,
83
89
  foreign-project paths and symlink path components are rejected. Deleted paths
84
90
  need not exist; contained symlinks are deliberately unsupported too. Spaces,