woods 2.0.0.beta2 → 2.0.0.beta4
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 +339 -1
- data/CONTRIBUTING.md +188 -12
- data/README.md +93 -174
- data/SECURITY.md +9 -6
- data/docs/AGENT_GUIDE.md +109 -8
- data/docs/AGENT_SETUP.md +98 -7
- data/docs/BACKEND_MATRIX.md +25 -0
- data/docs/CLIENT_HOOKS.md +111 -0
- data/docs/CONFIGURATION_REFERENCE.md +267 -16
- data/docs/CONSOLE_MCP_SETUP.md +80 -7
- data/docs/DOCKER_SETUP.md +22 -3
- data/docs/EVALUATION.md +464 -1
- data/docs/EXTRACTOR_REFERENCE.md +45 -6
- data/docs/FAQ.md +11 -12
- data/docs/GETTING_STARTED.md +17 -5
- data/docs/INCREMENTAL_EXTRACTION.md +147 -7
- data/docs/INDEX_LAYOUT.md +382 -0
- data/docs/INTERNALS.md +7 -2
- data/docs/MCP_SERVERS.md +276 -5
- data/docs/MCP_TOOL_COOKBOOK.md +37 -22
- data/docs/MCP_WORKTREE_SETUP.md +43 -83
- data/docs/NOTION_INTEGRATION.md +13 -0
- data/docs/OBSIDIAN_INTEGRATION.md +57 -9
- data/docs/PUBLISHED_INDEX.md +72 -0
- data/docs/README.md +7 -0
- data/docs/RETRIEVAL_GUIDE.md +273 -12
- data/docs/RUNTIME_TRACING.md +71 -0
- data/docs/SOURCE_FRESHNESS.md +143 -0
- data/docs/TROUBLESHOOTING.md +129 -18
- data/docs/UNBLOCKED_INTEGRATION.md +25 -0
- data/docs/UPGRADING_TO_2.md +48 -22
- data/docs/WATCH_DAEMON.md +277 -67
- data/exe/woods-agent-config +6 -0
- data/exe/woods-extract +5 -0
- data/exe/woods-hook-context +6 -0
- data/exe/woods-mcp-start +14 -9
- data/lib/generators/woods/pgvector_generator.rb +8 -2
- data/lib/generators/woods/templates/woods.rb.tt +1 -3
- data/lib/tasks/woods.rake +47 -397
- data/lib/woods/agent_configuration/applier.rb +135 -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 +72 -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 +18 -17
- data/lib/woods/console/credential_scanner_registry.rb +36 -0
- data/lib/woods/console/dispatch_pipeline.rb +7 -0
- data/lib/woods/console/embedded_executor.rb +32 -10
- 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/console/sql_noise_stripper.rb +9 -7
- data/lib/woods/console/sql_table_scanner.rb +47 -7
- data/lib/woods/console/sql_validator.rb +49 -9
- data/lib/woods/console/sqlite_read_guard.rb +46 -0
- data/lib/woods/coordination/pipeline_lock.rb +3 -2
- data/lib/woods/dependency_graph.rb +65 -13
- data/lib/woods/embedding/corpus.rb +94 -0
- data/lib/woods/embedding/indexer.rb +114 -60
- 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 +277 -149
- 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/declared_parent.rb +55 -0
- 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 +10 -13
- 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 +13 -9
- data/lib/woods/extractors/mailer_extractor.rb +26 -15
- 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 +26 -34
- 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 +13 -9
- 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 +48 -19
- 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 +22 -13
- data/lib/woods/mcp/bootstrapper.rb +79 -4
- data/lib/woods/mcp/config_resolver.rb +2 -1
- data/lib/woods/mcp/index_reader.rb +334 -162
- 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 +22 -9
- data/lib/woods/mcp/renderers/plain_renderer.rb +18 -8
- data/lib/woods/mcp/search_results.rb +74 -0
- data/lib/woods/mcp/server.rb +178 -63
- data/lib/woods/mcp/tool_contract.rb +3 -1
- data/lib/woods/mcp/tool_response_renderer.rb +41 -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/mcp/traversal_response.rb +22 -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 +13 -6
- data/lib/woods/payload_store.rb +27 -26
- data/lib/woods/published_index/typed_unit_reader.rb +40 -3
- data/lib/woods/published_index.rb +2 -2
- data/lib/woods/railtie.rb +3 -3
- data/lib/woods/railtie_support.rb +12 -12
- data/lib/woods/rake_helpers.rb +382 -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 +84 -0
- data/lib/woods/retrieval/lexical_index.rb +120 -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/file_store.rb +6 -1
- 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 +31 -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 +35 -10
- 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 +58 -9
- 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 +154 -32
- 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 +19 -0
- data/plugin/skills/woods-diagnose/SKILL.md +319 -1
- data/plugin/skills/woods-investigate/SKILL.md +145 -0
- data/plugin/skills/woods-mcp-config/SKILL.md +90 -2
- data/plugin/skills/woods-setup/SKILL.md +110 -6
- metadata +87 -5
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.beta4/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.beta4/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.
|
|
@@ -177,7 +263,7 @@ By contributing, you agree that your contribution is licensed under the [MIT Lic
|
|
|
177
263
|
|
|
178
264
|
## Release flow
|
|
179
265
|
|
|
180
|
-
`main` is the development branch and
|
|
266
|
+
`main` is the development branch. It carries an alpha marker before the first prerelease of a version line and after reopening development following a final release. During beta/RC iteration, it retains the last prepared prerelease version until the next `release:prepare`. Every published release is identified by its exact tagged commit; later commits on `main` are not that release even if `Woods::VERSION` is unchanged.
|
|
181
267
|
|
|
182
268
|
| State | `Woods::VERSION` | Tagged | On RubyGems | Documentation links point at |
|
|
183
269
|
|---|---|---|---|---|
|
|
@@ -188,13 +274,36 @@ By contributing, you agree that your contribution is licensed under the [MIT Lic
|
|
|
188
274
|
|
|
189
275
|
RubyGems treats any letter in a version as a prerelease, so a `~> 1.6` or `~> 2.0` constraint never resolves a beta or a release candidate. Adopting one is explicit: `gem "woods", "2.0.0.beta1"`.
|
|
190
276
|
|
|
191
|
-
`spec/release_v2/version_state_spec.rb`
|
|
277
|
+
`spec/release_v2/version_state_spec.rb` verifies that VERSION is either an alpha or the changelog carries its dated heading, and that the four `release-state` documentation fences match the state VERSION declares. Those checks do not establish that a checkout is the published release.
|
|
278
|
+
|
|
279
|
+
For Git-sourced candidates, record the locked Git revision, loaded gem path, and working-tree changes alongside `Woods::VERSION`. Version-only preflight establishes the declared version, not whether a particular post-tag fix is present. Match capability claims to the pinned commit or published tag. Generated release-state links continue to describe the prepared version; use the candidate commit when linking evidence about unreleased changes.
|
|
192
280
|
|
|
193
281
|
### During feature work
|
|
194
282
|
|
|
195
283
|
- 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
284
|
- Leave the `release-state` fences alone. `release:prepare` rewrites them.
|
|
285
|
+
- 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
|
+
|
|
287
|
+
Entry files contain nonempty UTF-8 Markdown without ATX (`#`) or setext
|
|
288
|
+
(underlined) headings, usually a bullet
|
|
289
|
+
and indented continuation lines. For example, `changelog/fixed_watch-restart.md`
|
|
290
|
+
can contain `- Preserve pending work across watch restarts.` Supported types are
|
|
291
|
+
`added`, `build`, `changed`, `dependencies`, `documentation`, `fixed`,
|
|
292
|
+
`performance`, `security`, `testing`, and `upgrade-notes`. Slugs start with a
|
|
293
|
+
lowercase letter or digit and use lowercase letters, digits, hyphens, or
|
|
294
|
+
underscores. Use one unique file per change; do not copy its entry into
|
|
295
|
+
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.
|
|
298
|
+
|
|
299
|
+
`release:prepare` appends entry files in filename order after inline Unreleased
|
|
300
|
+
entries, folds them through the same heading merger, and deletes exactly the
|
|
301
|
+
consumed files. It validates every entry and documentation rewrite before
|
|
302
|
+
changing any files; an invalid entry or a later refusal preserves all entries.
|
|
303
|
+
A prepared release has an empty Unreleased section and no entry files. During
|
|
304
|
+
an ordinary beta cycle, entry files may accumulate even with an empty Unreleased section while
|
|
305
|
+
VERSION stays at the previous beta; the tag validator always rejects entry
|
|
306
|
+
files at the candidate release SHA, regardless of inline notes or the version.
|
|
198
307
|
|
|
199
308
|
### Preparing a release
|
|
200
309
|
|
|
@@ -205,11 +314,13 @@ One command per transition. It never commits, tags, pushes, or publishes.
|
|
|
205
314
|
| Alpha to the first beta | `bin/rake "release:prepare[2.0.0.beta1]"` |
|
|
206
315
|
| Beta to the next beta or a release candidate | `bin/rake "release:prepare[2.0.0.rc1]"` |
|
|
207
316
|
| Release candidate to the release | `bin/rake "release:prepare[2.0.0]"` |
|
|
208
|
-
| After
|
|
317
|
+
| After a final release publishes, reopen development | `bin/rake "release:reopen[2.1.0.alpha]"` |
|
|
318
|
+
|
|
319
|
+
`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.
|
|
209
320
|
|
|
210
|
-
`release:
|
|
321
|
+
`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.
|
|
211
322
|
|
|
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.
|
|
323
|
+
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
324
|
|
|
214
325
|
Review the diff and run the release contracts:
|
|
215
326
|
|
|
@@ -227,8 +338,8 @@ A release is pinned by its tag, never by a branch:
|
|
|
227
338
|
|
|
228
339
|
| Step | Command | What guards it |
|
|
229
340
|
|---|---|---|
|
|
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
|
|
341
|
+
| 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 |
|
|
342
|
+
| 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
343
|
| 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
344
|
|
|
234
345
|
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 +395,74 @@ short body that links `CHANGELOG.md` at the tag itself (not at `main`) and
|
|
|
284
395
|
anchors straight to that version's dated heading, so the note a reader lands
|
|
285
396
|
on always matches the bytes RubyGems published.
|
|
286
397
|
|
|
398
|
+
### One-off 1.6.3 security maintenance release
|
|
399
|
+
|
|
400
|
+
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
|
|
403
|
+
immutable v1.6.2 commit `4b40e17fd68122a70ccf00d9d2ffb8af42171d3d`.
|
|
404
|
+
This is a stable patch, separate from the next v2 prerelease; it does not declare
|
|
405
|
+
v2 final or establish a general-purpose maintenance publishing path.
|
|
406
|
+
|
|
407
|
+
`script/release_profile.rb` on trusted main owns this exact tag/branch/base and
|
|
408
|
+
its required CI jobs. `MAINTENANCE_APPROVED_SHA` starts as `nil`: publication
|
|
409
|
+
fails closed until a **separate reviewed main PR** pins the exact prepared
|
|
410
|
+
maintenance commit. Neither a dispatch parameter nor candidate code can choose
|
|
411
|
+
another profile, branch, base, SHA or weaker CI requirements. The SHA binds the
|
|
412
|
+
whole reviewed candidate, including its CI definition and installed-package
|
|
413
|
+
tests; review those files as release controls, not just their job names.
|
|
414
|
+
|
|
415
|
+
The preparation order is:
|
|
416
|
+
|
|
417
|
+
1. Merge the main-side maintenance policy/tooling PR. Before creating the remote
|
|
418
|
+
target, confirm its effective branch rules require pull requests and prevent
|
|
419
|
+
force pushes and deletion; configure those rules before creating the target. The GitHub
|
|
420
|
+
rules API can check `release/1.6.3` before the branch exists.
|
|
421
|
+
2. Create that target from the immutable v1.6.2 commit. Review the narrow security
|
|
422
|
+
backport and its legacy preparation adapter against that line. Confirm the
|
|
423
|
+
inherited automatic tag-push publisher remains disabled before any maintenance tag exists.
|
|
424
|
+
3. Use the legacy adapter's `release:reopen[1.6.3.alpha]` and
|
|
425
|
+
`release:prepare[1.6.3]` transitions in clean, separately reviewed commits.
|
|
426
|
+
The adapter owns the legacy documentation profile; do not copy v2 fences or
|
|
427
|
+
surface claims into v1, and never hand-edit VERSION.
|
|
428
|
+
4. Review and merge the prepared candidate into `release/1.6.3`. Require passing
|
|
429
|
+
unit, booted Rails, installed-package, lint, coverage, security and build
|
|
430
|
+
jobs. Review the complete CI and package-test implementation at that SHA.
|
|
431
|
+
Then pin that **exact final commit** in `MAINTENANCE_APPROVED_SHA` through the
|
|
432
|
+
separate main PR. No pin means no maintenance release.
|
|
433
|
+
5. Only after the pin merges, the maintainer may tag that exact commit and wait
|
|
434
|
+
for its tag-push CI run. Dispatch uses the ordinary tag/run-ID payload.
|
|
435
|
+
Every exact maintenance matrix row in the trusted profile must succeed;
|
|
436
|
+
missing, duplicated, skipped or failed rows refuse publication.
|
|
437
|
+
|
|
438
|
+
[Temporary security-advisory forks](https://docs.github.com/en/code-security/tutorials/fix-reported-vulnerabilities/collaborate-in-a-fork)
|
|
439
|
+
do not run CI or enforce destination branch protections when the advisory is
|
|
440
|
+
merged. Review and test those patches privately, then require the upstream CI
|
|
441
|
+
matrix triggered by the push to `release/1.6.3` before pinning its prepared SHA.
|
|
442
|
+
A private test report cannot replace the upstream tag-push run and immutable
|
|
443
|
+
artifact required for publication. Keep the advisory unpublished until the fixed
|
|
444
|
+
gems are available.
|
|
445
|
+
|
|
446
|
+
The validators require the approved SHA to remain reachable from the freshly
|
|
447
|
+
fetched maintenance branch and to descend from the fixed legacy base. They retain
|
|
448
|
+
exact tag/VERSION/changelog checks, the unpublished-version check, one immutable
|
|
449
|
+
CI artifact ID/digest, and protected `release` environment approval. Both Ruby
|
|
450
|
+
package-test rows install that same artifact and run the pinned v1-specific
|
|
451
|
+
`maintenance_packaged_gem_spec.rb` outside the repository load path. The oldest
|
|
452
|
+
Ruby maintenance row explicitly activates MCP 0.23.0, the reviewed security
|
|
453
|
+
floor; the latest row resolves the candidate's supported SDK range. Candidate
|
|
454
|
+
code still executes only in secret-free, read-only jobs. After environment
|
|
455
|
+
approval, maintenance history/publication checks run again before requesting
|
|
456
|
+
RubyGems credentials; the remote tag is checked again immediately before push.
|
|
457
|
+
|
|
458
|
+
A candidate fix or changed prepared SHA requires a new reviewed main pin and a
|
|
459
|
+
fresh tag-push CI run. Updating main's tooling alone never authorizes different
|
|
460
|
+
candidate bytes. Main's v2 release contract remains unchanged. Do not create or
|
|
461
|
+
push tags, dispatch, publish, or claim 1.6.3 is available during preparation.
|
|
462
|
+
|
|
287
463
|
### Stable branches
|
|
288
464
|
|
|
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
|
|
465
|
+
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.
|
|
290
466
|
|
|
291
467
|
### What coding agents may do
|
|
292
468
|
|