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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +262 -1
- data/CONTRIBUTING.md +173 -9
- data/README.md +7 -3
- data/SECURITY.md +9 -6
- data/docs/AGENT_GUIDE.md +83 -4
- data/docs/AGENT_SETUP.md +82 -1
- data/docs/BACKEND_MATRIX.md +20 -0
- data/docs/CLIENT_HOOKS.md +111 -0
- data/docs/CONFIGURATION_REFERENCE.md +199 -14
- data/docs/CONSOLE_MCP_SETUP.md +35 -5
- data/docs/DOCKER_SETUP.md +21 -2
- data/docs/EVALUATION.md +464 -1
- data/docs/EXTRACTOR_REFERENCE.md +36 -5
- data/docs/FAQ.md +11 -12
- data/docs/GETTING_STARTED.md +17 -5
- data/docs/INCREMENTAL_EXTRACTION.md +117 -1
- data/docs/INDEX_LAYOUT.md +382 -0
- data/docs/INTERNALS.md +7 -2
- data/docs/MCP_SERVERS.md +221 -5
- data/docs/MCP_TOOL_COOKBOOK.md +33 -18
- data/docs/NOTION_INTEGRATION.md +13 -0
- data/docs/OBSIDIAN_INTEGRATION.md +57 -9
- data/docs/PUBLISHED_INDEX.md +55 -0
- data/docs/README.md +7 -0
- data/docs/RETRIEVAL_GUIDE.md +253 -11
- data/docs/RUNTIME_TRACING.md +71 -0
- data/docs/SOURCE_FRESHNESS.md +143 -0
- data/docs/TROUBLESHOOTING.md +117 -5
- data/docs/UNBLOCKED_INTEGRATION.md +25 -0
- data/docs/UPGRADING_TO_2.md +44 -22
- data/docs/WATCH_DAEMON.md +259 -59
- data/exe/woods-agent-config +6 -0
- data/exe/woods-extract +5 -0
- data/exe/woods-hook-context +6 -0
- data/lib/generators/woods/templates/woods.rb.tt +1 -3
- data/lib/tasks/woods.rake +47 -397
- data/lib/woods/agent_configuration/applier.rb +133 -0
- data/lib/woods/agent_configuration/cli.rb +101 -0
- data/lib/woods/agent_configuration/cli_options.rb +29 -0
- data/lib/woods/agent_configuration/document.rb +105 -0
- data/lib/woods/agent_configuration/error.rb +7 -0
- data/lib/woods/agent_configuration/launcher.rb +75 -0
- data/lib/woods/agent_configuration/layout.rb +59 -0
- data/lib/woods/agent_configuration/managed_section.rb +62 -0
- data/lib/woods/agent_configuration/plan.rb +98 -0
- data/lib/woods/agent_configuration/plan_diff.rb +38 -0
- data/lib/woods/agent_configuration/planned_files.rb +61 -0
- data/lib/woods/agent_configuration/planner.rb +63 -0
- data/lib/woods/agent_configuration/planner_validation.rb +77 -0
- data/lib/woods/agent_configuration/preflight.rb +100 -0
- data/lib/woods/agent_configuration/recovery.rb +49 -0
- data/lib/woods/ast/node.rb +2 -0
- data/lib/woods/ast/parser.rb +38 -5
- data/lib/woods/builder.rb +21 -5
- data/lib/woods/cache/cache_middleware.rb +28 -7
- data/lib/woods/cache/cache_store.rb +4 -5
- data/lib/woods/change_set.rb +5 -4
- data/lib/woods/console/credential_index.rb +20 -2
- data/lib/woods/console/credential_scanner.rb +14 -14
- data/lib/woods/console/credential_scanner_registry.rb +36 -0
- data/lib/woods/console/embedded_executor.rb +1 -1
- data/lib/woods/console/encrypted_credential_snapshot.rb +16 -0
- data/lib/woods/console/rack_middleware.rb +22 -13
- data/lib/woods/console/server.rb +18 -16
- data/lib/woods/dependency_graph.rb +65 -13
- data/lib/woods/embedding/corpus.rb +94 -0
- data/lib/woods/embedding/indexer.rb +90 -46
- data/lib/woods/embedding/openai.rb +17 -6
- data/lib/woods/evaluation/ablation_executor.rb +6 -1
- data/lib/woods/evaluation/ablation_timed_executor.rb +22 -4
- data/lib/woods/export/typed_reader.rb +56 -0
- data/lib/woods/extractor.rb +232 -137
- data/lib/woods/extractors/action_cable_extractor.rb +3 -1
- data/lib/woods/extractors/behavioral_profile.rb +9 -7
- data/lib/woods/extractors/caching_extractor.rb +3 -1
- data/lib/woods/extractors/concern_extractor.rb +64 -6
- data/lib/woods/extractors/configuration_extractor.rb +7 -3
- data/lib/woods/extractors/controller_extractor.rb +13 -4
- data/lib/woods/extractors/database_view_extractor.rb +3 -1
- data/lib/woods/extractors/decorator_extractor.rb +3 -1
- data/lib/woods/extractors/engine_extractor.rb +3 -1
- data/lib/woods/extractors/event_extractor.rb +4 -2
- data/lib/woods/extractors/factory_extractor.rb +3 -1
- data/lib/woods/extractors/graphql_extractor.rb +8 -2
- data/lib/woods/extractors/i18n_extractor.rb +3 -1
- data/lib/woods/extractors/job_extractor.rb +6 -19
- data/lib/woods/extractors/lib_extractor.rb +3 -1
- data/lib/woods/extractors/mailer_extractor.rb +20 -5
- data/lib/woods/extractors/manager_extractor.rb +3 -1
- data/lib/woods/extractors/method_parameters.rb +53 -0
- data/lib/woods/extractors/middleware_argument.rb +65 -0
- data/lib/woods/extractors/middleware_extractor.rb +9 -3
- data/lib/woods/extractors/migration_extractor.rb +3 -1
- data/lib/woods/extractors/model_extractor.rb +39 -33
- data/lib/woods/extractors/package_extractor.rb +24 -4
- data/lib/woods/extractors/phlex_extractor.rb +3 -1
- data/lib/woods/extractors/policy_extractor.rb +3 -1
- data/lib/woods/extractors/poro_extractor.rb +3 -1
- data/lib/woods/extractors/pundit_extractor.rb +3 -1
- data/lib/woods/extractors/rails_source_extractor.rb +4 -2
- data/lib/woods/extractors/rake_task_extractor.rb +4 -2
- data/lib/woods/extractors/route_extractor.rb +3 -1
- data/lib/woods/extractors/route_helper_resolver.rb +10 -33
- data/lib/woods/extractors/scheduled_job_extractor.rb +41 -15
- data/lib/woods/extractors/serializer_extractor.rb +4 -2
- data/lib/woods/extractors/service_extractor.rb +3 -1
- data/lib/woods/extractors/shared_dependency_scanner.rb +2 -2
- data/lib/woods/extractors/shared_utility_methods.rb +27 -15
- data/lib/woods/extractors/source_nesting.rb +1 -1
- data/lib/woods/extractors/state_machine_extractor.rb +3 -1
- data/lib/woods/extractors/test_mapping_extractor.rb +3 -1
- data/lib/woods/extractors/validator_extractor.rb +3 -1
- data/lib/woods/extractors/view_component_extractor.rb +3 -1
- data/lib/woods/extractors/view_template_extractor.rb +3 -1
- data/lib/woods/gem_mapper.rb +2 -0
- data/lib/woods/git_history.rb +116 -0
- data/lib/woods/graph_analyzer.rb +35 -6
- data/lib/woods/hooks/context_cli.rb +54 -0
- data/lib/woods/hooks/context_event.rb +88 -0
- data/lib/woods/hooks/context_hint.rb +73 -0
- data/lib/woods/hooks/context_impact.rb +77 -0
- data/lib/woods/hooks/context_output.rb +47 -0
- data/lib/woods/hooks/context_state.rb +102 -0
- data/lib/woods/hooks/refresh.rb +79 -0
- data/lib/woods/hooks/rule_projection.rb +78 -0
- data/lib/woods/input_rules.rb +19 -0
- data/lib/woods/mcp/bearer_auth.rb +20 -12
- data/lib/woods/mcp/bootstrapper.rb +62 -0
- data/lib/woods/mcp/index_reader.rb +323 -160
- data/lib/woods/mcp/initialization_guidance.rb +27 -0
- data/lib/woods/mcp/origin_guard.rb +17 -9
- data/lib/woods/mcp/published_lexical_retriever.rb +115 -0
- data/lib/woods/mcp/renderers/markdown_renderer.rb +8 -1
- data/lib/woods/mcp/renderers/plain_renderer.rb +7 -1
- data/lib/woods/mcp/search_results.rb +74 -0
- data/lib/woods/mcp/server.rb +158 -37
- data/lib/woods/mcp/tool_contract.rb +2 -0
- data/lib/woods/mcp/tool_response_renderer.rb +25 -0
- data/lib/woods/mcp/traversal_evidence.rb +113 -0
- data/lib/woods/mcp/traversal_evidence_index.rb +100 -0
- data/lib/woods/mcp/traversal_evidence_page.rb +41 -0
- data/lib/woods/mcp/traversal_evidence_text.rb +52 -0
- data/lib/woods/notion/exporter.rb +56 -17
- data/lib/woods/obsidian/destination_plan.rb +98 -0
- data/lib/woods/obsidian/name_mapper.rb +19 -3
- data/lib/woods/obsidian/note_builder.rb +19 -10
- data/lib/woods/obsidian/vault_exporter.rb +88 -32
- data/lib/woods/operator/pipeline_guard.rb +18 -13
- data/lib/woods/path_dispatcher.rb +7 -1
- data/lib/woods/payload_store.rb +27 -26
- data/lib/woods/railtie.rb +3 -3
- data/lib/woods/railtie_support.rb +12 -12
- data/lib/woods/rake_helpers.rb +392 -0
- data/lib/woods/resilience/graph_invariant_validator/membership_checks.rb +71 -0
- data/lib/woods/resilience/graph_invariant_validator/node_checks.rb +61 -0
- data/lib/woods/resilience/graph_invariant_validator/reverse_relationship_checks.rb +46 -0
- data/lib/woods/resilience/graph_invariant_validator.rb +119 -0
- data/lib/woods/resilience/index_validator/graph_checks.rb +80 -0
- data/lib/woods/resilience/index_validator.rb +112 -23
- data/lib/woods/retrieval/context_assembler.rb +50 -15
- data/lib/woods/retrieval/lexical_assembler.rb +73 -0
- data/lib/woods/retrieval/lexical_index.rb +119 -0
- data/lib/woods/retrieval/ranker.rb +4 -2
- data/lib/woods/retrieval/scope.rb +108 -0
- data/lib/woods/retrieval/scoped_graph_store.rb +32 -0
- data/lib/woods/retrieval/scoped_vector_store.rb +55 -0
- data/lib/woods/retrieval/search_executor.rb +86 -27
- data/lib/woods/retrieval/source_evidence.rb +200 -0
- data/lib/woods/retriever.rb +98 -22
- data/lib/woods/ruby_analyzer/trace_enricher.rb +77 -38
- data/lib/woods/session_tracer/middleware.rb +10 -12
- data/lib/woods/session_tracer/redis_store.rb +22 -6
- data/lib/woods/session_tracer/session_flow_assembler.rb +23 -17
- data/lib/woods/session_tracer/solid_cache_coordination.rb +6 -4
- data/lib/woods/session_tracer/unit_resolver.rb +63 -0
- data/lib/woods/source_inputs/consumer_errors.rb +27 -0
- data/lib/woods/source_inputs/handoff.rb +102 -0
- data/lib/woods/source_inputs/launcher.rb +157 -0
- data/lib/woods/source_inputs/manifest.rb +124 -0
- data/lib/woods/source_inputs/private_key.rb +55 -0
- data/lib/woods/source_inputs/scanner.rb +171 -0
- data/lib/woods/source_inputs/scopes.rb +71 -0
- data/lib/woods/source_inputs/session.rb +214 -0
- data/lib/woods/source_inputs/status.rb +84 -0
- data/lib/woods/source_inputs/verifier.rb +107 -0
- data/lib/woods/storage/metadata_store.rb +25 -25
- data/lib/woods/storage/pgvector.rb +29 -8
- data/lib/woods/storage/qdrant.rb +17 -7
- data/lib/woods/storage/vector_store.rb +18 -6
- data/lib/woods/tasks.rb +3 -2
- data/lib/woods/temporal/json_snapshot_store.rb +29 -8
- data/lib/woods/unblocked/exporter.rb +59 -70
- data/lib/woods/version.rb +1 -1
- data/lib/woods/watch/boot_snapshot.rb +52 -0
- data/lib/woods/watch/daemon.rb +136 -28
- data/lib/woods/watch/listen_watcher.rb +4 -0
- data/lib/woods/watch/polling_watcher.rb +5 -1
- data/lib/woods/watch/status.rb +20 -15
- data/lib/woods/watch/tree_scan.rb +21 -13
- data/lib/woods/watch/watcher.rb +4 -1
- data/lib/woods.rb +50 -11
- data/plugin/.claude-plugin/plugin.json +1 -1
- data/plugin/hooks/adapters/normalize.jq +15 -0
- data/plugin/hooks/adapters/normalize.rb +63 -0
- data/plugin/hooks/hooks.json +20 -0
- data/plugin/hooks/woods-context.sh +50 -0
- data/plugin/hooks/woods-input-rules.sh +159 -0
- data/plugin/hooks/woods-opencode.mjs +65 -0
- data/plugin/hooks/woods-post-edit.sh +2 -225
- data/plugin/hooks/woods-refresh.sh +260 -0
- data/plugin/hooks/woods-session-start.sh +47 -55
- data/plugin/skills/woods-agent-enable/SKILL.md +13 -0
- data/plugin/skills/woods-diagnose/SKILL.md +288 -1
- data/plugin/skills/woods-investigate/SKILL.md +106 -0
- data/plugin/skills/woods-mcp-config/SKILL.md +89 -1
- data/plugin/skills/woods-setup/SKILL.md +107 -6
- metadata +84 -5
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f1877858142850cdb2a9802c103198cf5be63439e610ebbc43cd10f97554f10d
|
|
4
|
+
data.tar.gz: 5bad5ce4acdb04312a53d31414ea0b93b3cb4d7c103209f3172bb84e509278c3
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: d878e5d2d4f51dcb4fcf4e39af29e78edf23eae756d0db4f2fe3b4c6e77454295d7a6f455d4d1f3aa397d70fb51253be040ca3ec29e17e32bd5d854dd4b434e4
|
|
7
|
+
data.tar.gz: 37a249bb634be55999c9d6ae1f058cd72a80be48496fc04b93d9d4213fa1355f1c7ff8d2d21abeeabaa8f88846846110a82547572ce74bce90dbb75d18133a0a
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,265 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [2.0.0.beta3] - 2026-09-18
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- Optional `changelog/<type>_<slug>.md` entry files let parallel branches record changes without editing the same Unreleased block. `release:prepare` validates, folds, and removes consumed entries; release validation refuses leftover entries at the tagged SHA (B-179).
|
|
15
|
+
- Add optional `volatile_dependency_limit_per_target` to keep one hot dependency
|
|
16
|
+
from filling the volatility report (B-188). Apply the per-target edge cap before
|
|
17
|
+
the global top 20, preserve the default output, and expose the configured cap
|
|
18
|
+
and reported count alongside the full qualifying count when enabled.
|
|
19
|
+
|
|
20
|
+
- Gate retrieval quality in CI with a versioned Canopy runtime corpus, captured
|
|
21
|
+
real MiniLM vectors, per-strategy quality floors, latency observations, and
|
|
22
|
+
exact output-token counts. Preserve known failed queries in the evidence (#227).
|
|
23
|
+
- Bound `dependencies` and `dependents` traversal work independently of response
|
|
24
|
+
pagination, with configurable node/edge budgets and explicit partial-result
|
|
25
|
+
reasons. Filtered edges and reverse relationship checks consume the edge
|
|
26
|
+
budget; complete small results retain their existing shape (#311).
|
|
27
|
+
|
|
28
|
+
- Document the published filesystem layout for non-Ruby consumers, with pinned
|
|
29
|
+
Bash/jq and Python reads, retention-race handling, and structural snapshot
|
|
30
|
+
publication guidance (#306).
|
|
31
|
+
|
|
32
|
+
- `WOODS_WATCH_TRUST_FOREIGN_HOST=1` lets watch status, incremental/clean guards,
|
|
33
|
+
daemon startup checks, and MCP trust fresh foreign-container heartbeats without
|
|
34
|
+
checking an unrelated local pid. This is opt-in; foreign records expire after
|
|
35
|
+
15 minutes, and malformed or excessively future timestamps are rejected (#321).
|
|
36
|
+
|
|
37
|
+
- `WOODS_WATCH_POLL_INTERVAL` configures positive, finite seconds between watch
|
|
38
|
+
polling scans, including fallback from native watching (default 1.0; #322).
|
|
39
|
+
|
|
40
|
+
- Published manifests record `woods_version`, the last publisher's gem version,
|
|
41
|
+
for Rails extraction and static self-maps. MCP exposes it independently of the
|
|
42
|
+
reader version, and `woods:validate` gives nonfatal warnings for malformed
|
|
43
|
+
writer versions or different major versions. Older manifests remain valid
|
|
44
|
+
without this optional provenance field (#323).
|
|
45
|
+
- Supply concise, capability-aware Index MCP instructions through initialization and modern discovery, with status-first retrieval guidance, bounded traversal and source verification. Preserve the SDK's omission for protocol `2024-11-05`; tool registration and authorization remain unchanged. (#402)
|
|
46
|
+
- Added generation-bound source-input evidence with bounded `current` / `drifted` / `unknown` status, per-consumer incremental provenance, private keyed content identities, and a fresh-process `woods-extract` launcher. The opt-in session hook now checks source content through the shared no-environment status task. Old indexes and unproved boot/runtime consumption remain explicitly unknown.
|
|
47
|
+
Add separately opt-in, bounded Claude orientation and post-edit candidate context from a retained published generation, with explicit uncertainty, repeat suppression, independent refresh controls, and an installed `woods-hook-context` helper.
|
|
48
|
+
Explicit Claude and OpenCode edit adapters preserve every affected patch path,
|
|
49
|
+
including both rename sides, through the shared durable refresh queue. Native
|
|
50
|
+
OpenCode registration is optional and version-pinned; unsupported or escaping
|
|
51
|
+
events produce diagnostics without claiming refresh.
|
|
52
|
+
MCP `search` now reports whether its returned matches exhaust the requested domain, prove an additional match, or leave the remainder unknown after a scan budget or regex timeout. Bounded lookahead shares the existing scan budget, deep reads preserve typed identities, and detected artifact corruption remains an error. JSON and text formats distinguish returned counts from exact totals; agent guidance explains how to narrow partial searches. Mixed Rails/gem source directories remain searchable and loadable by lexical retrieval while rejecting unrelated type mismatches.
|
|
53
|
+
`woods:validate` now checks semantic graph invariants against typed unit indexes and artifacts within one pinned published generation. It detects broken reverse membership, invalid sources, duplicate typed variants, file/type index drift, and missing indexed nodes while accepting cycles, unresolved targets, legacy string edges, and the Woods static source map. Validation remains read-only, preserves existing report/exit behavior, and reports actionable identities without repairing the graph.
|
|
54
|
+
|
|
55
|
+
The validator, deep search, and lexical retrieval accept all four GraphQL unit types in the shared `graphql/` directory, preserving actual typed identities and existing directory-family search labels.
|
|
56
|
+
`dependencies` and `dependents` accept optional `explain: true` to retain typed source ownership, original edge direction and relationship attributes, and bounded shortest witnesses. Explanations distinguish direct records from transitive reachability, preserve unknown labels and ambiguous target candidates, and retain ancestor context across pages. Existing compact responses remain unchanged; explanation work shares traversal budgets.
|
|
57
|
+
Publish additive `reverse_via` target buckets with typed source identities, relationship labels and association attributes, preserving existing `reverse` arrays and legacy graph loading.
|
|
58
|
+
- Mark whole-file caching, configuration, test mapping, Rails source, and gem source graph nodes and typed variants with `kind: "file_profile"`, preserving file membership and identifiers while letting consumers distinguish profiles from constant-owned units (#417).
|
|
59
|
+
Add `woods-agent-config` for explicit Claude Code project/user setup, update, and removal. Preview saves one private edit plan; apply checks its original snapshots, preserves unrelated configuration, tracks owned entries and instruction sections, and supports recovery after interrupted writes. Host/Compose preflight checks the installed Index Server and published index.
|
|
60
|
+
- Add opt-in compact source evidence and declared API outlines to retrieval and lookup, with complete published spans, explicit omissions, honest generation/source provenance, and typed SHA-guarded full-source follow-up. Existing full-source behavior remains the default.
|
|
61
|
+
- Add explicit embedding-free lexical retrieval over published extraction units. Set `WOODS_RETRIEVAL_MODE=lexical` for Index MCP or `config.retrieval_mode = :lexical` for Ruby builders; field-aware ranked results preserve typed identity, generation consistency and budgeted matching evidence without provider or vector access. Semantic retrieval remains the default.
|
|
62
|
+
- Add explicit package and application-relative source-path scopes to ranked retrieval and discovery, with eligibility before candidate limits, typed scope metadata, and native scoped vector searches without re-embedding.
|
|
63
|
+
|
|
64
|
+
### Fixed
|
|
65
|
+
|
|
66
|
+
- Reflect model callbacks from Rails' per-event chains instead of nonexistent
|
|
67
|
+
per-kind readers; include `before_commit` and keep `callback_count` equal to
|
|
68
|
+
the emitted callback list. Preserve framework callbacks with stable Proc/lambda
|
|
69
|
+
source-site labels and address-free default callback-object descriptions in
|
|
70
|
+
metadata and chunks, including Rails 6's `raw_filter`, so separate-process
|
|
71
|
+
extractions of unchanged models remain equivalent. Preserve custom object labels.
|
|
72
|
+
|
|
73
|
+
- Explain the Zeitwerk 2.6.9 naming requirement in identifier-collision errors
|
|
74
|
+
and upgrade guidance before suggesting changes to valid namespace wrappers
|
|
75
|
+
on older-loader or classic-mode hosts (B-149).
|
|
76
|
+
|
|
77
|
+
- Stabilize controller inline callback and condition labels across processes and
|
|
78
|
+
checkout paths, including action chunks; retain `unless` conditions in the
|
|
79
|
+
generated filter-chain header (B-167).
|
|
80
|
+
|
|
81
|
+
- Publish metadata-only changes in local embedding snapshots without re-embedding
|
|
82
|
+
unchanged source; retain no-op dumps and source-hash checkpoints (B-119).
|
|
83
|
+
|
|
84
|
+
- Search Boolean metadata fields as `true`/`false` consistently in SQLite and
|
|
85
|
+
InMemory, preserving numeric `1`/`0` and null semantics (B-199, #356).
|
|
86
|
+
|
|
87
|
+
- Keep the default SQLite metadata database inside the effective `WOODS_OUTPUT`
|
|
88
|
+
directory for embedding tasks, isolating indexes while preserving explicit
|
|
89
|
+
database overrides (B-156). Existing databases are not moved; run `woods:embed`
|
|
90
|
+
for the selected index after upgrading.
|
|
91
|
+
|
|
92
|
+
- Treat non-object JSON snapshots and files removed or made unreadable during
|
|
93
|
+
a read as absent across lookup, listing, diffs, and unit history (B-158).
|
|
94
|
+
|
|
95
|
+
- Count corrupt and unreadable SHA-named JSON snapshots toward retention and
|
|
96
|
+
evict them before valid history, preserving the just-captured snapshot and
|
|
97
|
+
unrelated files (B-163).
|
|
98
|
+
|
|
99
|
+
- Count the final retrieval context after formatting and type-rank metadata,
|
|
100
|
+
keeping `tokens_used` and its trace consistent with the configured counter
|
|
101
|
+
or estimate (B-197, #354).
|
|
102
|
+
|
|
103
|
+
- Order tied hybrid retrieval candidates deterministically before graph seed
|
|
104
|
+
selection, truncation and reciprocal-rank fusion across supported Ruby versions
|
|
105
|
+
(B-192).
|
|
106
|
+
|
|
107
|
+
- Bound OpenAI embedding requests to 36 valid inputs, preserving chunk order and
|
|
108
|
+
rejecting partial or dimension-inconsistent batches (B-157). Chunk-heavy runs
|
|
109
|
+
use more HTTP requests to stay within the API's input and total-token limits.
|
|
110
|
+
|
|
111
|
+
- Match embedded NUL literally in SQLite metadata searches, including substrings after NUL; preserve ASCII case folding and literal wildcard characters (B-198, #355).
|
|
112
|
+
|
|
113
|
+
- Prevent single-component cache keys from colliding with multi-component or
|
|
114
|
+
empty keys by uniformly length-prefixing components (B-155). Custom callers
|
|
115
|
+
using persistent single-component keys should clear that cache domain on upgrade.
|
|
116
|
+
|
|
117
|
+
- Make middleware argument metadata, generated source and hashes stable across Rails processes by describing runtime identities structurally while preserving literal and nested configuration (#362).
|
|
118
|
+
|
|
119
|
+
- Omit per-unit git enrichment for shallow checkouts or unverifiable repository
|
|
120
|
+
depth, with one warning and full-history recovery guidance, instead of
|
|
121
|
+
reporting truncated commit counts as complete churn data (B-189).
|
|
122
|
+
|
|
123
|
+
- Validate direct/legacy Console scope arrays with the active SQL dialect and
|
|
124
|
+
MySQL session quote modes, refusing subqueries hidden by mismatched quote
|
|
125
|
+
stripping while retaining the supported tools' narrower scope grammar (B-154).
|
|
126
|
+
|
|
127
|
+
- Read and clear legacy Redis session indexes before any new record without
|
|
128
|
+
`WRONGTYPE`; atomic SET/ZSET access tolerates concurrent index migration
|
|
129
|
+
and keeps reader-only upgrades compatible with older SET writers (B-162).
|
|
130
|
+
|
|
131
|
+
- Allow full extraction when ActionMailer is absent and skip non-app mailers
|
|
132
|
+
instead of publishing empty units at fabricated paths; share that ownership
|
|
133
|
+
gate with incremental class discovery (B-153).
|
|
134
|
+
|
|
135
|
+
- Repair corrupt pipeline cooldown state on an explicit all-reset, including
|
|
136
|
+
`pipeline_repair` in custom operator-configured servers; ordinary reads still
|
|
137
|
+
deny operations and scoped resets preserve corrupt state (B-159).
|
|
138
|
+
|
|
139
|
+
- Normalize incremental change paths before deduplication and dispatch, including
|
|
140
|
+
trailing root slashes, repeated separators and dot segments (B-148).
|
|
141
|
+
|
|
142
|
+
- Load lazy Rails routes before caching navigation helpers, preserving view-to-controller dependencies during fresh-process incremental extraction (#360).
|
|
143
|
+
|
|
144
|
+
- Reflect model methods after schema loading so full and incremental runs agree
|
|
145
|
+
on Rails-generated constructors while preserving application overrides (B-202, #363).
|
|
146
|
+
|
|
147
|
+
- Parse job `perform_params` and shared `initialize_params` from Ruby parameter
|
|
148
|
+
syntax, avoiding phantom names from keyword/default expressions while preserving
|
|
149
|
+
the existing metadata fields and named rest/block arguments (B-151).
|
|
150
|
+
|
|
151
|
+
- Resolve ERB-backed Solid Queue recurring schedules using Rails configuration
|
|
152
|
+
loading, including relative requires, conditional entries, aliases and custom
|
|
153
|
+
environment sections (B-203, #364).
|
|
154
|
+
|
|
155
|
+
- Preserve navigation edges for real named routes such as `file_path`,
|
|
156
|
+
`image_url`, `download_path`, and `root_path`; unresolved asset/filesystem
|
|
157
|
+
helper names still produce no edge (B-152).
|
|
158
|
+
|
|
159
|
+
- Resolve and track app-owned nested model mixins through runtime source locations, refreshing includer source and callbacks on incremental edits (B-150, #361).
|
|
160
|
+
|
|
161
|
+
- Explain the full-extraction recovery for runtime job removals and bundle
|
|
162
|
+
upgrades; missing gem-path warnings now include the bundle-update remedy
|
|
163
|
+
without changing incremental discovery rules (B-165, B-166).
|
|
164
|
+
|
|
165
|
+
- Preserve all reverse dependencies on symbolic external targets such as `http_api` after incremental graph reloads and re-registration (B-193, #305).
|
|
166
|
+
|
|
167
|
+
- Preserve changes to nested `extracted_at` metadata when deciding whether to
|
|
168
|
+
rewrite a unit; only Woods' top-level extraction stamp is ignored (B-147).
|
|
169
|
+
- Read per-unit git enrichment in one streamed HEAD history walk instead of
|
|
170
|
+
repeated 500-path batches (B-195, #305). Merge commits compare with their first
|
|
171
|
+
parent while all HEAD ancestry is visited; counts can change from legacy
|
|
172
|
+
pathspec simplification. Optional enrichment now requires Git 2.31 or newer;
|
|
173
|
+
incomplete/failed history is omitted with a warning. Run full extraction
|
|
174
|
+
after upgrading to refresh retained metadata. Host speedup remains unmeasured.
|
|
175
|
+
|
|
176
|
+
- Apply the same app-owned path exclusions to full and incremental git enrichment;
|
|
177
|
+
external, vendored, and node_modules units no longer gain empty git metadata (B-194).
|
|
178
|
+
|
|
179
|
+
- Prune default excluded package directories before recursively discovering
|
|
180
|
+
`package.yml`, avoiding scans of indexes and snapshots under `tmp/` (B-196, #305).
|
|
181
|
+
|
|
182
|
+
- Preserve namespaced and CamelCase graph-retrieval subjects, keep snake_case
|
|
183
|
+
lookup working, and exclude tracing instructions from fallback metadata searches (B-190).
|
|
184
|
+
|
|
185
|
+
- Order equal PageRank scores by identifier before assigning retrieval importance
|
|
186
|
+
percentiles, making their ranking weights deterministic across Ruby versions
|
|
187
|
+
and graph insertion orders (B-191).
|
|
188
|
+
|
|
189
|
+
- Reduce payload-seed metadata lookups by classifying each entry once. Preserve
|
|
190
|
+
per-file hardlink/copy fallback, atomic publication and generation retention
|
|
191
|
+
while reducing full and incremental seed overhead (#305).
|
|
192
|
+
|
|
193
|
+
- Keep runtime trace evidence for same-name instance and singleton methods separate, including inherited singleton owners and caller kinds. Legacy untyped events enrich instance methods only; re-record old singleton traces.
|
|
194
|
+
|
|
195
|
+
- Ruby trace enrichment now records the nearest observed calling Ruby method
|
|
196
|
+
instead of the callee receiver. Recording keeps separate fiber stacks, handles
|
|
197
|
+
recursive and unwound calls, and leaves outside-recording callers unknown
|
|
198
|
+
without binding or backtrace inspection (#309).
|
|
199
|
+
|
|
200
|
+
- Scope per-file git metadata to HEAD history instead of every ref, excluding
|
|
201
|
+
unmerged branches and tool checkpoints from churn and authorship (#319). Run
|
|
202
|
+
a full `woods:extract` after upgrading to refresh previously published metadata.
|
|
203
|
+
|
|
204
|
+
- Full extraction no longer creates empty type directories at the index root
|
|
205
|
+
before publishing its generation payload. Existing root directories and legacy
|
|
206
|
+
flat-index files are preserved (#320).
|
|
207
|
+
|
|
208
|
+
- Watch startup reconciles environment-boot-covered restart inputs with one full
|
|
209
|
+
extraction instead of repeatedly exiting 75. Live changes and changes during
|
|
210
|
+
environment initialization still require restart; failed reconciliation and
|
|
211
|
+
deleted restart inputs survive retries and supervisor restarts. Built-in
|
|
212
|
+
watchers establish detection before startup extraction begins (#318).
|
|
213
|
+
|
|
214
|
+
- Add `console_mcp_http_enabled` (default `true`) so stdio-only Console setups
|
|
215
|
+
can explicitly disable HTTP and its boot-time token warnings. Production
|
|
216
|
+
still refuses to boot without a token when HTTP Console is enabled (#304).
|
|
217
|
+
|
|
218
|
+
- Solid Cache conditional writes on MySQL no longer claim ownership of a
|
|
219
|
+
pre-existing row when the adapter reports matched rows as affected rows.
|
|
220
|
+
Validate ownership using the per-attempt serialized payload, with live MySQL
|
|
221
|
+
contention/recovery coverage and documented crash/eviction limits (#228).
|
|
222
|
+
- Keep Notion model/column/migration data and Unblocked full/partial documents tied to their exact extracted type. Refuse incomplete export reads before mutation, and preserve remote documents when same-name, same-file types cannot be represented safely by Unblocked's existing URI scheme.
|
|
223
|
+
Preserve the selected extraction type when framework search and recent changes read colliding identifiers. Session controller lookup and root outgoing-edge selection now retain the controller type. Public untyped lookup, downstream references and the bare-name multi-step context pool retain their existing contracts.
|
|
224
|
+
Obsidian export preserves same-name units of different types, their separate outgoing links, and original public identifiers. Collision-bearing vaults publish a version-2 typed manifest; ordinary version-1 manifests and note paths remain unchanged. Ambiguous bare targets are omitted with a diagnostic, and incomplete typed reads cannot trigger stale-note deletion.
|
|
225
|
+
- Reject ambiguous cross-type dependencies in session context with an actionable `ambiguous_identity` MCP error instead of silently selecting a source or reusing another type's context key. Leave unresolved controllers in the timeline without a misleading source reference. Pin candidate discovery and all assembly reads to one generation; retain typed controller roots, metadata-only timelines, and successful response shapes. Refs #213; this does not migrate global identifiers.
|
|
226
|
+
- Refresh Console credential indexes from a fresh encrypted-file and key snapshot, retain the last valid index when refresh fails, and update all live embedded servers without retaining abandoned servers. Each response scan uses one complete credential index.
|
|
227
|
+
Keep credential scanner refresh snapshots limited to live weakly referenced
|
|
228
|
+
scanners on older Ruby versions, avoiding unsafe receiver access after garbage
|
|
229
|
+
collection while preserving updates to every live Console server.
|
|
230
|
+
- Refuse incomplete native embedding input before changing stores or checkpoints, retaining legacy flat-index support while rejecting malformed JSON. A source-empty unit now retires superseded vectors and checkpoints its no-content state without calling the embedding provider (#442, #444).
|
|
231
|
+
- Terminate the evaluation command's owned process group on timeout, including ordinary descendants whose parent has already exited.
|
|
232
|
+
- Let only the hook that removes a recorded dead-owner marker replace its mkdir lock, so concurrent recovery does not leave all edits queued behind an abandoned empty directory.
|
|
233
|
+
- Refuse Obsidian exports that would overwrite unmanaged notes, indexes, settings, or sidecars. Preflight all destinations, record generated asset digests separately from the public manifest, and suppress sweeps on conflicts or write failures. Legacy assets are adopted only when byte-identical; changed legacy sidecars require inspection and backup or a fresh export directory.
|
|
234
|
+
- Coordinate `Woods.extract!` and `Woods.extract_changed!` with task/watch writers and raise on lock timeout or failed generation publication, so background jobs can retry unsuccessful extraction.
|
|
235
|
+
- Accept Rails 6.0 positional middleware options on Ruby 3 while preserving explicit keyword precedence, required bearer tokens, and unknown-option refusal. Add an installed-gem CI contract for all advertised direct runtime dependency floors, with a constrained Rails 6.0.0 fixture and recorded transitive resolution.
|
|
236
|
+
- Honor configured context-token defaults in Builder-created semantic/lexical retrieval, caches, and MCP while preserving explicit budgets and the legacy custom-collaborator fallback. Deprecate the inert similarity_threshold option with a warning instead of changing ranking behavior (#446).
|
|
237
|
+
- Preserve the dispatched controller's runtime class name in session traces, with a Rails-inflector fallback, so acronym namespaces retain source context in `session_trace`.
|
|
238
|
+
- Preserve each logical directory alias in polling and startup catch-up so an earlier irrelevant alias cannot hide an extraction input such as `app/models`. Detect cycles per traversal branch while retaining ignored-subtree pruning.
|
|
239
|
+
|
|
240
|
+
### Changed
|
|
241
|
+
|
|
242
|
+
- Clarify the existing search-regex timeout exclusion: Ruby 3.0/3.1 remain
|
|
243
|
+
supported without a per-match time bound. Run the Index MCP process on Ruby
|
|
244
|
+
3.2+ for the one-second per-match limit; no runtime mitigation was added
|
|
245
|
+
for older interpreters (B-161).
|
|
246
|
+
|
|
247
|
+
- Reduce payload-clone allocation and traversal overhead while preserving
|
|
248
|
+
hardlinks, copy fallback and immutable generation ownership (#305).
|
|
249
|
+
- Make extraction profiling additive: report git enrichment, reconciliation,
|
|
250
|
+
finalization, pointer publication and retention separately, with a distinct
|
|
251
|
+
whole-run wall-time line (#305).
|
|
252
|
+
- Expand opt-in refresh hooks to the shared extraction input rules, including
|
|
253
|
+
services, controllers, jobs, views, locales, and supported tests/lib paths.
|
|
254
|
+
Restart inputs request fresh full extraction. Preserve queued edits across
|
|
255
|
+
failures and daemon deferral, bound the worker lifetime, and carry JSON batches
|
|
256
|
+
through Docker command prefixes without host-bundle or environment-forwarding
|
|
257
|
+
assumptions. Existing manual incremental and watch behavior is unchanged.
|
|
258
|
+
Add a trusted, disabled-by-default release profile for the supported 1.6.2 security maintenance line. Publication requires a separately reviewed exact candidate SHA pin on main, all fixed maintenance CI rows, and the existing immutable artifact and protected publication safeguards. The v2 release path retains its existing requirements.
|
|
259
|
+
|
|
260
|
+
### Documentation
|
|
261
|
+
|
|
262
|
+
- Clarify that source literals and optional session traces can contain sensitive information, and correct the session FileStore configuration example.
|
|
263
|
+
- Qualify the `~> 2.0` installation examples for stable releases and direct prerelease adopters to the exact published version in the README release table. Keep agent setup and plugin guidance aligned with installed-version capabilities; clarify that the refresh-hook deadline starts after complete event input is collected and queued.
|
|
264
|
+
|
|
265
|
+
### Testing
|
|
266
|
+
|
|
267
|
+
- Raise the default-suite CI line-coverage floor from 85% to 90%, calibrated against current local and CI measurements. Branch coverage remains measured without a gate; per-file floors and combined opt-in coverage remain separate work.
|
|
268
|
+
|
|
10
269
|
## [2.0.0.beta2] - 2026-09-10
|
|
11
270
|
|
|
12
271
|
### Added
|
|
@@ -438,7 +697,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
438
697
|
a previous version migrates automatically on the first record through a
|
|
439
698
|
single atomic server-side script, so concurrent writers racing the legacy
|
|
440
699
|
index cannot erase each other's members or fail mid-migration; eviction
|
|
441
|
-
order
|
|
700
|
+
order follows oldest last request for newly scored members. Migrated members
|
|
701
|
+
receive score zero and evict lexicographically until recorded again (B-160).
|
|
702
|
+
Adds a live-Redis contract spec
|
|
442
703
|
(`spec/session_tracer/redis_store_live_spec.rb`, `WOODS_RUN_LIVE_BACKENDS=1`).
|
|
443
704
|
|
|
444
705
|
### Documentation
|
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.0.
|
|
4
|
+
Woods welcomes bug fixes, extractor coverage, storage and retrieval improvements, MCP compatibility work, documentation, and focused performance changes. This guide covers the shared contribution contract. Coding agents working from a source checkout should also read the repository's [AGENTS.md](https://github.com/lost-in-the/woods/blob/v2.0.0.beta3/AGENTS.md).
|
|
5
5
|
<!-- release-state:end -->
|
|
6
6
|
|
|
7
7
|
## Choose the right channel
|
|
@@ -46,7 +46,7 @@ Create a branch from current `main`. Keep each pull request to one logical chang
|
|
|
46
46
|
| `plugin/skills/` | Distributed Woods skills (setup/upgrade, MCP configuration, investigation, agent enablement, diagnosis) |
|
|
47
47
|
|
|
48
48
|
<!-- release-state:contributing-architecture -->
|
|
49
|
-
Read [CLAUDE.md](https://github.com/lost-in-the/woods/blob/v2.0.0.
|
|
49
|
+
Read [CLAUDE.md](https://github.com/lost-in-the/woods/blob/v2.0.0.beta3/CLAUDE.md) for architecture and implementation gotchas before changing runtime behavior.
|
|
50
50
|
<!-- release-state:end -->
|
|
51
51
|
|
|
52
52
|
### Agent orientation and static self-map
|
|
@@ -96,7 +96,25 @@ bin/rubocop
|
|
|
96
96
|
|
|
97
97
|
Before requesting review, run the full unit suite and style check unless the PR explains why one cannot run.
|
|
98
98
|
|
|
99
|
-
Coverage from the default process excludes opt-in Rails, installed-artifact, and live-backend lanes. Report their results separately; a low percentage for subprocess-driven tasks does not establish that they are untested. CI enforces
|
|
99
|
+
Coverage from the default process excludes opt-in Rails, installed-artifact, and live-backend lanes. Report their results separately; a low percentage for subprocess-driven tasks does not establish that they are untested. CI enforces a 90% aggregate line floor and measures branches, but does not enforce a branch floor. The line gate was calibrated against local and CI default-suite results on 2026-09-18; it does not establish per-file coverage or coverage across the opt-in lanes. Add behavior-based regressions and real optional-gem fixtures for changed extraction paths before proposing higher thresholds.
|
|
100
|
+
|
|
101
|
+
### Pending examples in CI
|
|
102
|
+
|
|
103
|
+
CI fails when a running example becomes unexpectedly pending, including `skip`,
|
|
104
|
+
`xit`, and pending metadata. `spec/support/pending_policy.rb` records the exact
|
|
105
|
+
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
|
|
108
|
+
procfs example, and three filesystem-permission examples when running as root.
|
|
109
|
+
The Linux CI unit jobs run the real procfs identity example as an unprivileged
|
|
110
|
+
user, so the procfs and root exceptions normally apply only to other environments.
|
|
111
|
+
Opt-in suites excluded by their documented environment gates are not pending
|
|
112
|
+
examples. Focused runs do not need to include every reviewed exception.
|
|
113
|
+
|
|
114
|
+
Adding a new skip requires review of both its exact entry and capability check;
|
|
115
|
+
matching an existing reason alone is insufficient. Local runs retain normal
|
|
116
|
+
RSpec pending behavior. Exercise the policy and matrix consistency checks with
|
|
117
|
+
`CI=true bin/rspec spec/ci`.
|
|
100
118
|
|
|
101
119
|
### Rails version matrix
|
|
102
120
|
|
|
@@ -113,6 +131,44 @@ WOODS_RUN_BOOTED_APP=1 BUNDLE_GEMFILE=gemfiles/rails_7.2.gemfile \
|
|
|
113
131
|
bin/rspec spec/integration/booted_extraction_spec.rb
|
|
114
132
|
```
|
|
115
133
|
|
|
134
|
+
The unit suite evaluates every hand-maintained Rails gemfile and checks its
|
|
135
|
+
Appraisal requirements, old-Rails compatibility pins, and CI matrix membership.
|
|
136
|
+
This detects configuration drift; it does not replace the booted rows.
|
|
137
|
+
|
|
138
|
+
### Exact runtime dependency floors
|
|
139
|
+
|
|
140
|
+
The separate `minimum-dependencies` CI job runs on Ruby 3.0 with Bundler 2.5.23:
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
gem install bundler -v 2.5.23
|
|
144
|
+
ruby script/test-minimum-dependencies
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Run outside `bundle exec`. The harness builds and installs the candidate gem,
|
|
148
|
+
resolves `gemfiles/minimum_runtime.gemfile` independently of the development
|
|
149
|
+
bundle, and verifies every loaded Woods library comes from that installed gem.
|
|
150
|
+
All five direct runtime dependencies are pinned to their advertised gemspec
|
|
151
|
+
floors. Transitive dependencies are compatible solver selections, **not** a
|
|
152
|
+
claim that every transitive version is minimal. CI retains the full resolved
|
|
153
|
+
version/platform list, lockfile, candidate gem and SHA-256 under the
|
|
154
|
+
`minimum-runtime-dependencies` artifact; local output defaults to
|
|
155
|
+
`tmp/minimum-dependencies/`.
|
|
156
|
+
|
|
157
|
+
The probe exercises lexical retrieval and typed lookup over a synthetic published
|
|
158
|
+
index, MCP SDK dispatch, Prism parsing, and MessagePack snapshots. It also boots
|
|
159
|
+
an actual Rails 6.0.0 API-style app, loads Woods tasks, and checks the real Rails
|
|
160
|
+
middleware stack for disabled Console passthrough, authorized requests, missing
|
|
161
|
+
and incorrect token refusal, and forbidden origins. Static serving is explicitly
|
|
162
|
+
disabled: Rails 6.0.0's own Static middleware predates Ruby 3 keyword forwarding.
|
|
163
|
+
This is a bounded Woods runtime-floor contract, not evidence that arbitrary
|
|
164
|
+
Rails 6.0.0 applications boot on Ruby 3. The booted Rails matrix uses compatible
|
|
165
|
+
patch releases and covers full extraction; enabled database-backed Console and
|
|
166
|
+
optional storage/provider combinations remain in their separate lanes.
|
|
167
|
+
|
|
168
|
+
The exact-floor job is required by release CI validation. Changing a runtime
|
|
169
|
+
lower bound requires updating its explicit pin and retaining a successful
|
|
170
|
+
installed-artifact run, rather than silently advancing a pin to make CI pass.
|
|
171
|
+
|
|
116
172
|
When adding a Rails line, update `Appraisals`, the corresponding hand-maintained gemfile, and `.github/workflows/ci.yml`. For Rails below 7.1, copy an existing 6.x gemfile so its sqlite3 and concurrent-ruby compatibility pins are preserved.
|
|
117
173
|
|
|
118
174
|
### Live storage and SQL dialects
|
|
@@ -127,6 +183,36 @@ WOODS_RUN_LIVE_BACKENDS=1 BUNDLE_GEMFILE=gemfiles/live_backends.gemfile \
|
|
|
127
183
|
|
|
128
184
|
The lane expects reachable PostgreSQL/pgvector, MySQL, and Qdrant services. Configure endpoints with `WOODS_PG_URL`, `WOODS_MYSQL_URL`, and `WOODS_QDRANT_URL`. The Console contracts exercise blocked-table enforcement and legitimate SQL on both database dialects. New adapter behavior that depends on a real server belongs in this lane.
|
|
129
185
|
|
|
186
|
+
### Solid Cache session compatibility
|
|
187
|
+
|
|
188
|
+
The live-backend job runs `spec/integration/solid_cache_compatibility_spec.rb`
|
|
189
|
+
against SQLite, PostgreSQL, and MySQL. MySQL coverage asserts that the adapter
|
|
190
|
+
has no insert `RETURNING`, checks actual insert ownership (including same-value
|
|
191
|
+
conflicts), exercises concurrent ownership, expiry, conditional deletion, and
|
|
192
|
+
session clear/epoch recovery. A separate case strips only insert-result metadata
|
|
193
|
+
to exercise the older Rails read-back fallback against real MySQL rows.
|
|
194
|
+
|
|
195
|
+
Solid Cache coordination depends on private APIs. Before declaring a newly
|
|
196
|
+
resolved `solid_cache` version supported:
|
|
197
|
+
|
|
198
|
+
1. Record the exact Ruby, Active Record, Solid Cache, and database versions.
|
|
199
|
+
2. Run the complete compatibility file against disposable PostgreSQL and MySQL
|
|
200
|
+
databases, with `WOODS_RUN_LIVE_BACKENDS=1`,
|
|
201
|
+
`BUNDLE_GEMFILE=gemfiles/live_backends.gemfile`, `WOODS_PG_URL`, and
|
|
202
|
+
`WOODS_MYSQL_URL` set. The suite recreates `solid_cache_entries`; never point
|
|
203
|
+
it at an application cache database.
|
|
204
|
+
3. Require every example to pass, including SQLite local-cache bypass, TTL,
|
|
205
|
+
sharding, missing-private-API errors, and the MySQL ownership cases. Attach
|
|
206
|
+
the versions and results to the PR; a passing double-based unit suite is
|
|
207
|
+
insufficient.
|
|
208
|
+
|
|
209
|
+
The live gemfile resolves `solid_cache ~> 1.0`; this is a dependency selection
|
|
210
|
+
range, not evidence that every version in it has been tested. The baseline
|
|
211
|
+
validated for #228 is Ruby 4.0.6, Solid Cache 1.0.10, Active Record 8.1.3.1,
|
|
212
|
+
SQLite 3.53.2, PostgreSQL 16.15, and MySQL 26.7.0.
|
|
213
|
+
Accepted crash/eviction limits are documented in the
|
|
214
|
+
[configuration reference](docs/CONFIGURATION_REFERENCE.md#solid-cache-session-retention-and-compatibility).
|
|
215
|
+
|
|
130
216
|
## Keep public surfaces synchronized
|
|
131
217
|
|
|
132
218
|
A pull request is incomplete when behavior and user guidance disagree.
|
|
@@ -193,8 +279,29 @@ RubyGems treats any letter in a version as a prerelease, so a `~> 1.6` or `~> 2.
|
|
|
193
279
|
### During feature work
|
|
194
280
|
|
|
195
281
|
- Do not edit `lib/woods/version.rb` by hand.
|
|
196
|
-
- Put changelog entries under `## [Unreleased]` only, beneath one of its `###` headings. Duplicate headings are merged at release time, in the order they first appear.
|
|
197
282
|
- Leave the `release-state` fences alone. `release:prepare` rewrites them.
|
|
283
|
+
- 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.
|
|
284
|
+
|
|
285
|
+
Entry files contain nonempty UTF-8 Markdown without ATX (`#`) or setext
|
|
286
|
+
(underlined) headings, usually a bullet
|
|
287
|
+
and indented continuation lines. For example, `changelog/fixed_watch-restart.md`
|
|
288
|
+
can contain `- Preserve pending work across watch restarts.` Supported types are
|
|
289
|
+
`added`, `build`, `changed`, `dependencies`, `documentation`, `fixed`,
|
|
290
|
+
`performance`, `security`, `testing`, and `upgrade-notes`. Slugs start with a
|
|
291
|
+
lowercase letter or digit and use lowercase letters, digits, hyphens, or
|
|
292
|
+
underscores. Use one unique file per change; do not copy its entry into
|
|
293
|
+
Unreleased as well. Keep entry files directly inside a real `changelog/`
|
|
294
|
+
directory; symlinks and directories masquerading as entries are refused.
|
|
295
|
+
Other file extensions are left untouched.
|
|
296
|
+
|
|
297
|
+
`release:prepare` appends entry files in filename order after inline Unreleased
|
|
298
|
+
entries, folds them through the same heading merger, and deletes exactly the
|
|
299
|
+
consumed files. It validates every entry and documentation rewrite before
|
|
300
|
+
changing any files; an invalid entry or a later refusal preserves all entries.
|
|
301
|
+
A prepared release has an empty Unreleased section and no entry files. During
|
|
302
|
+
an ordinary beta cycle, entry files may accumulate even with an empty Unreleased section while
|
|
303
|
+
VERSION stays at the previous beta; the tag validator always rejects entry
|
|
304
|
+
files at the candidate release SHA, regardless of inline notes or the version.
|
|
198
305
|
|
|
199
306
|
### Preparing a release
|
|
200
307
|
|
|
@@ -207,9 +314,9 @@ One command per transition. It never commits, tags, pushes, or publishes.
|
|
|
207
314
|
| Release candidate to the release | `bin/rake "release:prepare[2.0.0]"` |
|
|
208
315
|
| After the release publishes, reopen development | `bin/rake "release:reopen[2.1.0.alpha]"` |
|
|
209
316
|
|
|
210
|
-
`release:prepare` refuses a dirty working tree, a version that moves backwards, a version whose base is not the line `main` is developing, and an alpha target. It then bumps VERSION, folds `## [Unreleased]` into `## [<version>] - <date>` with one block per `###` heading, restates the fences, regenerates the surface inventory, and prints the tag and dispatch commands. Every rewrite is computed before any of it is written, so a refusal leaves the working tree untouched.
|
|
317
|
+
`release:prepare` refuses a dirty working tree, a version that moves backwards, a version whose base is not the line `main` is developing, and an alpha target. It then bumps VERSION, folds `## [Unreleased]` and optional entry files into `## [<version>] - <date>` with one block per `###` heading, restates the fences, regenerates the surface inventory, and prints the tag and dispatch commands. Every rewrite is computed before any of it is written, so a refusal leaves the working tree untouched.
|
|
211
318
|
|
|
212
|
-
A final release also absorbs every prerelease section of its own base version. Cutting `2.0.0` folds `## [2.0.0.beta1]` and `## [2.0.0.rc1]` into `## [2.0.0] - <date>` and removes their headings, prerelease entries first and anything written after them second, so the notes a user reads for 2.0.0 are the whole story rather than three fragments. An empty `## [Unreleased]` is therefore legitimate for a final release cut straight from a release candidate. A beta or a release candidate has nothing to absorb, so an empty Unreleased section refuses: there is nothing new to publish.
|
|
319
|
+
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.
|
|
213
320
|
|
|
214
321
|
Review the diff and run the release contracts:
|
|
215
322
|
|
|
@@ -227,8 +334,8 @@ A release is pinned by its tag, never by a branch:
|
|
|
227
334
|
|
|
228
335
|
| Step | Command | What guards it |
|
|
229
336
|
|---|---|---|
|
|
230
|
-
| Tag the merge commit | `git tag v<version> <merge-sha> && git push origin v<version>` (lightweight or annotated both work) | `script/validate-release` requires the tag to sit on `main` history, match `Woods::VERSION`, match the dated `CHANGELOG.md` heading, and not be an alpha |
|
|
231
|
-
| Trigger the release workflow | `gh api --method POST repos/lost-in-the/woods/dispatches -f event_type=release -F 'client_payload[tag]=v<version>' -F 'client_payload[ci_run_id]=<id>'` where `<id>` is the green CI run on the
|
|
337
|
+
| Tag the merge commit | `git tag v<version> <merge-sha> && git push origin v<version>` (lightweight or annotated both work) | `script/validate-release` requires the tag to sit on `main` history (or the explicitly approved maintenance history below), match `Woods::VERSION`, match the dated `CHANGELOG.md` heading, and not be an alpha |
|
|
338
|
+
| Trigger the release workflow | `gh api --method POST repos/lost-in-the/woods/dispatches -f event_type=release -F 'client_payload[tag]=v<version>' -F 'client_payload[ci_run_id]=<id>'` where `<id>` is the green CI run the tag push itself started, the one whose branch column reads `v<version>`; main's run on the same commit is refused with `tested ref is main` (requires Contents write) | `.github/workflows/release.yml` re-validates the named CI run through the API, verifies the artifact digest, and runs secret-free candidate package tests before publishing |
|
|
232
339
|
| Verify publication | `gem info woods --remote` shows the new version; for a prerelease, `gem info woods --remote --prerelease`. The README gem badge updates on its own | just before pushing, the workflow re-runs `script/verify-release-tag` so a tag that moved since validation aborts the publish |
|
|
233
340
|
|
|
234
341
|
Nothing is published from a laptop: the workflow builds and pushes the gem from the validated CI artifact, so the bytes on RubyGems are the bytes CI tested. `rake release` and `rake release:rubygem_push`, which `bundler/gem_tasks` installs, are blocked for that reason.
|
|
@@ -284,9 +391,66 @@ short body that links `CHANGELOG.md` at the tag itself (not at `main`) and
|
|
|
284
391
|
anchors straight to that version's dated heading, so the note a reader lands
|
|
285
392
|
on always matches the bytes RubyGems published.
|
|
286
393
|
|
|
394
|
+
### One-off 1.6.2 security maintenance release
|
|
395
|
+
|
|
396
|
+
The [security policy](SECURITY.md#supported-versions) supports 1.6.x security
|
|
397
|
+
fixes until 2027-02-20. While main develops v2, the sole maintenance exception
|
|
398
|
+
is `v1.6.2` from the short-lived `release/1.6.2` branch, descending from the
|
|
399
|
+
immutable v1.6.1 commit `73423a42644176b09961be373e13648c94690933`.
|
|
400
|
+
This is a stable patch, separate from the next v2 prerelease; it does not declare
|
|
401
|
+
v2 final or establish a general-purpose maintenance publishing path.
|
|
402
|
+
|
|
403
|
+
`script/release_profile.rb` on trusted main owns this exact tag/branch/base and
|
|
404
|
+
its required CI jobs. `MAINTENANCE_APPROVED_SHA` starts as `nil`: publication
|
|
405
|
+
fails closed until a **separate reviewed main PR** pins the exact prepared
|
|
406
|
+
maintenance commit. Neither a dispatch parameter nor candidate code can choose
|
|
407
|
+
another profile, branch, base, SHA or weaker CI requirements. The SHA binds the
|
|
408
|
+
whole reviewed candidate, including its CI definition and installed-package
|
|
409
|
+
tests; review those files as release controls, not just their job names.
|
|
410
|
+
|
|
411
|
+
The preparation order is:
|
|
412
|
+
|
|
413
|
+
1. Merge the main-side maintenance policy/tooling PR. Before creating the remote
|
|
414
|
+
target, confirm its effective branch rules require pull requests and prevent
|
|
415
|
+
force pushes and deletion; configure those rules before creating the target. The GitHub
|
|
416
|
+
rules API can check `release/1.6.2` before the branch exists.
|
|
417
|
+
2. Create that target from the immutable v1.6.1 commit. Review the narrow security
|
|
418
|
+
backport and its legacy preparation adapter against that line. Disable the
|
|
419
|
+
inherited automatic tag-push publisher before any maintenance tag exists.
|
|
420
|
+
3. Use the legacy adapter's `release:reopen[1.6.2.alpha]` and
|
|
421
|
+
`release:prepare[1.6.2]` transitions in clean, separately reviewed commits.
|
|
422
|
+
The adapter owns the legacy documentation profile; do not copy v2 fences or
|
|
423
|
+
surface claims into v1, and never hand-edit VERSION.
|
|
424
|
+
4. Review and merge the prepared candidate into `release/1.6.2`. Require passing
|
|
425
|
+
unit, booted Rails, installed-package, lint, coverage, security and build
|
|
426
|
+
jobs. Review the complete CI and package-test implementation at that SHA.
|
|
427
|
+
Then pin that **exact final commit** in `MAINTENANCE_APPROVED_SHA` through the
|
|
428
|
+
separate main PR. No pin means no maintenance release.
|
|
429
|
+
5. Only after the pin merges, the maintainer may tag that exact commit and wait
|
|
430
|
+
for its tag-push CI run. Dispatch uses the ordinary tag/run-ID payload.
|
|
431
|
+
Every exact maintenance matrix row in the trusted profile must succeed;
|
|
432
|
+
missing, duplicated, skipped or failed rows refuse publication.
|
|
433
|
+
|
|
434
|
+
The validators require the approved SHA to remain reachable from the freshly
|
|
435
|
+
fetched maintenance branch and to descend from the fixed legacy base. They retain
|
|
436
|
+
exact tag/VERSION/changelog checks, the unpublished-version check, one immutable
|
|
437
|
+
CI artifact ID/digest, and protected `release` environment approval. Both Ruby
|
|
438
|
+
package-test rows install that same artifact and run the pinned v1-specific
|
|
439
|
+
`maintenance_packaged_gem_spec.rb` outside the repository load path. The oldest
|
|
440
|
+
Ruby maintenance row explicitly activates MCP 0.23.0, the reviewed security
|
|
441
|
+
floor; the latest row resolves the candidate's supported SDK range. Candidate
|
|
442
|
+
code still executes only in secret-free, read-only jobs. After environment
|
|
443
|
+
approval, maintenance history/publication checks run again before requesting
|
|
444
|
+
RubyGems credentials; the remote tag is checked again immediately before push.
|
|
445
|
+
|
|
446
|
+
A candidate fix or changed prepared SHA requires a new reviewed main pin and a
|
|
447
|
+
fresh tag-push CI run. Updating main's tooling alone never authorizes different
|
|
448
|
+
candidate bytes. Main's v2 release contract remains unchanged. Do not create or
|
|
449
|
+
push tags, dispatch, publish, or claim 1.6.2 is available during preparation.
|
|
450
|
+
|
|
287
451
|
### Stable branches
|
|
288
452
|
|
|
289
|
-
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
|
|
453
|
+
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.2` security exception above does not establish an `N-M-stable` branch.
|
|
290
454
|
|
|
291
455
|
### What coding agents may do
|
|
292
456
|
|
data/README.md
CHANGED
|
@@ -15,15 +15,15 @@
|
|
|
15
15
|
>
|
|
16
16
|
> `main` is the development branch and can run ahead of the latest published gem. The gem badge above shows the latest published version; documentation for a published version lives on its tag.
|
|
17
17
|
>
|
|
18
|
-
> ### Version: 2.0.0.
|
|
18
|
+
> ### Version: 2.0.0.beta3 is published as a prerelease; `main` documents 2.0.0
|
|
19
19
|
>
|
|
20
20
|
> | Line | Version | Documentation |
|
|
21
21
|
> |---|---|---|
|
|
22
22
|
> | Documented here | **2.0.0**, unreleased | this README and the [documentation index](docs/README.md) |
|
|
23
|
-
> | Latest prerelease | **2.0.0.
|
|
23
|
+
> | Latest prerelease | **2.0.0.beta3** | [the v2.0.0.beta3 tag](https://github.com/lost-in-the/woods/tree/v2.0.0.beta3) |
|
|
24
24
|
> | Latest published gem | **1.6.1** | [the v1.6.1 tag](https://github.com/lost-in-the/woods/tree/v1.6.1) |
|
|
25
25
|
>
|
|
26
|
-
> RubyGems treats 2.0.0.
|
|
26
|
+
> RubyGems treats 2.0.0.beta3 as a prerelease, so `gem "woods", "~> 2.0"` does not resolve it. Install it explicitly with `gem "woods", "2.0.0.beta3"`. The released constraint stays `gem "woods", "~> 1.6"`.
|
|
27
27
|
<!-- release-state:end -->
|
|
28
28
|
|
|
29
29
|
Woods boots your Rails app, extracts the behavior Rails assembles at runtime, and serves it to AI tools through the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/). Agents can inspect resolved routes, schema, associations, callbacks, included concerns, dependencies, and execution flows instead of guessing from source files alone.
|
|
@@ -62,6 +62,10 @@ The default setup provides structural code intelligence. It does not require an
|
|
|
62
62
|
|
|
63
63
|
### 1. Install Woods
|
|
64
64
|
|
|
65
|
+
First [choose a version that is published on RubyGems](docs/GETTING_STARTED.md#1-install-the-gem).
|
|
66
|
+
The example below requires a stable 2.x release; beta and release-candidate
|
|
67
|
+
installations need an exact published prerelease pin from that guide.
|
|
68
|
+
|
|
65
69
|
```ruby
|
|
66
70
|
# Gemfile
|
|
67
71
|
group :development do
|
data/SECURITY.md
CHANGED
|
@@ -60,11 +60,12 @@ Woods runs inside your Rails application and has access to:
|
|
|
60
60
|
- **Application source code**: extracted and written to the output directory as JSON
|
|
61
61
|
- **Database schema**: column names, types, indexes, and foreign keys (no row data)
|
|
62
62
|
- **Git metadata**: commit history, contributors, file change frequency
|
|
63
|
-
- **
|
|
63
|
+
- **Optional session traces**: session/trace identifiers, request paths and controller/action timelines when session tracing is configured
|
|
64
|
+
- **Live database rows** (Console MCP Server), queries within a rolled-back transaction
|
|
64
65
|
|
|
65
66
|
### Output Directory
|
|
66
67
|
|
|
67
|
-
Extracted data is written to `tmp/woods/` by default. This directory contains your application's source code and schema in structured JSON format. Treat
|
|
68
|
+
Extracted data is written to `tmp/woods/` by default. This directory contains your application's source code and schema in structured JSON format. Source literals, comments and configuration metadata may contain sensitive values. Treat the index with the same sensitivity as your source code; do not expose it to untrusted parties. Optional session stores can be configured under this directory and add request metadata that may be more sensitive than the source itself. Apply the [session tracing access and retention guidance](docs/CONFIGURATION_REFERENCE.md#session-tracer-options) to those stores too.
|
|
68
69
|
|
|
69
70
|
### Console Server
|
|
70
71
|
|
|
@@ -80,13 +81,15 @@ If extraction output leaks, what can an attacker do with it?
|
|
|
80
81
|
|
|
81
82
|
**What the output contains.** Application source code (inlined concerns, callback-resolved behavior), database schema (column names, types, indexes, foreign keys), route tables, migration history, gem versions, and git metadata (commit history, contributor emails, file change frequency).
|
|
82
83
|
|
|
83
|
-
**
|
|
84
|
+
**Structural extraction boundaries.** Structural extraction collects schema rather than dumping live database rows, and does not intentionally collect environment variables or the Rails encrypted credential store. This is not a guarantee that output is free of secrets or customer information: extracted source can contain hardcoded values, comments or examples, and Index tools can return that source. Console redaction and export-specific scrubbing do not sanitize the structural index.
|
|
85
|
+
|
|
86
|
+
**Optional session data.** When configured, session tracing records session/trace identifiers, request paths and controller/action timelines. Request paths and identifiers can contain sensitive user information. The configured session store controls where these records live; configured Index session tools can return them. Session storage and access need their own retention and authorization controls. Console MCP is a separate live-data boundary described above.
|
|
84
87
|
|
|
85
88
|
| Leak scenario | Attacker gains | Attacker does not gain |
|
|
86
89
|
|---|---|---|
|
|
87
|
-
| `tmp/woods/` directory exfiltrated | Source code
|
|
88
|
-
| MCP Index Server token leaked (HTTP transport) |
|
|
90
|
+
| `tmp/woods/` directory exfiltrated | Source code, schema and metadata, including sensitive values present in source; session records if their store is configured here | No additional live database or shell access merely from possessing these files |
|
|
91
|
+
| MCP Index Server token leaked (HTTP transport) | Access to published source/metadata and configured session tools, including sensitive content they contain | The packaged default does not boot Rails or provide live database queries or shell execution; custom tool wiring has its own access boundary |
|
|
89
92
|
| Notion sync database compromised | Model and column summaries synced to Notion | Anything not mirrored, source code stays local |
|
|
90
|
-
| Console MCP Server exposed (dev/staging) |
|
|
93
|
+
| Console MCP Server exposed (dev/staging) | Live database access constrained by TableGate, credential scanning, Redactor and SqlValidator; this remains admin-trust access | Supported packaged modes expose no write or eval tools; rollback and redaction have the limits described in the Console guide |
|
|
91
94
|
|
|
92
95
|
**Mitigation.** Treat `tmp/woods/` as source-equivalent, keep it out of world-readable directories and public container images. Rotate `WOODS_MCP_HTTP_TOKEN` on compromise. Keep `console_mcp_enabled = false` in production regardless of environment, since the console layers are defense-in-depth and not primary controls.
|