ruff_ruby_spo: OpenProject schema migration-replay (W3) + routes.rb harvest arm (gap b)#73
Conversation
…ne schema surface (W3) replay_post_baseline_migrations runs after parse_tables_dir, scanning db/migrate/*.rb + modules/*/db/migrate/*.rb (filename/timestamp sorted across both trees) for four top-level forms applied onto the in-memory baseline columns: add_column, rename_column, remove_column/ remove_columns, change_column (bare + parenthesized). columns_from flips "baseline-only" -> "baseline+replay" only when a mutation actually applies; a no-migration corpus stays byte-identical (regression-tested). 9 unit tests + 1 corpus-gated drift fuse. Measured on the OpenProject corpus (recorded in the op-nexgen board): WorkPackage lifts 27 -> 31 columns (+position, story_points, remaining_hours, budget_id) — NOT to ~109; the remaining breadth lives in change_table block bodies and runtime custom/plugin fields, which this deliberately does not parse. Authored 2026-07-06 in the op-nexgen W3 wave (patch banked at openproject-nexgen-rs .claude/ruff-expansions/); ship was deferred on push access. Applies clean on main @ 9640375; cargo test -p ruff_ruby_spo 130 passed, clippy clean, schema.rs fmt-clean.
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_4a1247cb-bcfd-4ee1-9b68-24336271c49d) |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bba7ae261c
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| index: &BTreeMap<String, usize>, | ||
| ) -> usize { | ||
| let mut applied = 0; | ||
| for raw in src.lines() { |
There was a problem hiding this comment.
Skip rollback bodies during migration replay
When a post-baseline migration is written with explicit def up/def down (or equivalent rollback-only blocks), this loop still scans the rollback half. For example, def up; add_column :widgets, :foo, :string; end followed by def down; remove_column :widgets, :foo; end will add and then remove foo, even though Rails keeps it after applying the migration, so baseline+replay can drop valid columns. Please track the active migration direction or skip rollback bodies before applying these mutations.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Confirmed and fixed in #74. The replay now tracks Ruby block depth and skips def down bodies (only up/change apply). This corrected a real corpus result: WorkPackage is 33 columns, not 31 — create_work_package_semantic_ids's def up adds sequence_number/identifier and its def down removes them, so the old "net to zero" was dropping valid columns exactly as you describe. Corpus gate + drift fuse updated 31 → 33; regression test replay_skips_def_down_rollback_body added.
Generated by Claude Code
| for raw in src.lines() { | ||
| let line = raw.trim(); | ||
|
|
||
| if let Some((table, field)) = parse_add_column(line) { |
There was a problem hiding this comment.
Handle parenthesized add_column calls
For post-baseline migrations that use the valid Rails form add_column(:widgets, :price, :integer), this call path ignores the mutation because parse_add_column does not strip call parentheses while the new rename/remove/change parsers do. That leaves newly added columns out of baseline+replay for any migration using this common style; please normalize the argument tail for add_column as well before parsing.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Confirmed and fixed in #74. parse_add_column now strip_call_parens on the argument tail like the rename/remove/change parsers, so add_column(:t, :c, :type) is applied instead of dropped. Regression test replay_add_column_parenthesized_form_applies added.
Generated by Claude Code
…on (gap b)
Closes the routes.rb stratum (E-CLICKWEG-CHOREOGRAPHY-1 gap b): an
AST-walk (lib-ruby-parser) over config/routes.rb + modules/*/config/
routes.rb that resolves a route-helper stem (+ verb) to its
controller#action, making the InvokesAction/NavigatesTo join resolvable
for the first time. Two additive predicates RoutesTo (stem ->
verb:controller#action) + RouteScope (member/collection/canonical/
standalone), both Authoritative; predicate count-lock 71 -> 73.
Faithful Rails semantics per the council-hardened v3 spec
(openproject-nexgen-rs .claude/ruff-expansions/2026-07-10-routes-arm-spec.md):
canonical-seven with collection_token/member_token (news -> news_index
when singular==plural), singleton-collection-singular, absolute
/-controller strip, controller:-kwarg ranking, module:-scope-wrap,
naked-verb-in-resources nesting, string-path auto-naming, as:"",
decl-order first-wins + duplicate_stem_conflicts, multi-name replay,
escaped_dynamic for non-literal args, only/except coercion, shallow
member-drop. 19 unit fixtures + an env-gated (RAILS_CORPUS_SRC) drift
fuse pinned to the measured corpus: 29 files, 1625 declared, 1534
emitted, escaped {mounts=9 via_all=6 dynamic=13}, escaped_other exactly
["use_doorkeeper"].
Central-review fixes over the first implementation (gated in the shared
target, verified on the real corpus):
- namespace-path controller fallback: a bare verb with explicit action
in a namespace (get "plugin/:id", action: :show_plugin in
namespace :admin;:settings) resolves to admin/settings#show_plugin
(the real Admin::SettingsController) instead of leaking the verb name
into escaped_other. escaped_other is now exactly the Doorkeeper macro.
- escaped_dynamic pinned to the measured 13 (not the grep-estimated 2):
the walker counts every conservatively-declined declaration, all
counted, none silently dropped.
- as: value used verbatim for the collection name (Rails guide:
resources :foos, as: :bar -> bar, not bars) — test corrected.
- 3-level nesting spot-check uses new_project_meeting_agenda_item_outcome
(the except: %i[index show] filter removes the show helper) — the
walker correctly respects except:.
Edit allowlist honored: routes.rs (new) + lib.rs (register) + schema.rs
(pub(crate) singularize) + triple.rs (2 variants). Base ruff main #72 +
W3. Pre-existing #72-file fmt/pedantic warnings left untouched (out of
scope).
Bugbot couldn't run - usage limit reachedBugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit. A user or team admin can review and increase usage limits in the Cursor dashboard. (requestId: serverGenReqId_72c46fe3-8a60-4c4c-8ab2-83e9981b7c5a) |
…hesized add_column (codex #73 P2) Two P2 correctness fixes from codex review on the merged PR #73, both in the post-baseline migration-replay (schema.rs): 1. Skip `def down` rollback bodies. `def up`/`def change` describe the schema AFTER the migration; `def down` reverses it. Replaying the down half undid valid columns. A lightweight Ruby block-depth tracker (is_ruby_block_opener + an == "end" close check) now suppresses mutations while inside a `def down` method. Corpus impact, verified: WorkPackage is 33 columns, not 31 — create_work_package_semantic_ids's `def up` adds sequence_number/identifier and its `def down` removes them; Rails applies only up, so they are RETAINED. The old "net to zero" pin of 31 was exactly the bug the reviewer caught. Corpus schema gate + drift fuse updated 31 -> 33 with the corrected provenance. 2. Parse parenthesized add_column(:t, :c, :type). parse_add_column did not strip_call_parens while the rename/remove/change parsers did, so the common parenthesized form was silently dropped. Normalized the argument tail like its siblings. Two regression tests added (replay_add_column_parenthesized_form_applies, replay_skips_def_down_rollback_body). Follow-up to merged #73; branch restarted from main per merged-PR protocol. Tests 151 (ruby) + 133 (triplet), clippy-clean, corpus gates green.
…status-c6e8in ruff_ruby_spo: migration-replay codex fixes — skip def down bodies + parenthesized add_column (#73 follow-up)
Two OpenProject-transcode harvest expansions on the mandated single branch.
1.
routes.rs— the routes.rb harvest arm (closes gap b)Closes the routes.rb stratum (E-CLICKWEG-CHOREOGRAPHY-1 gap b, RAILS-COVERAGE-KIT §6): an AST-walk (
lib-ruby-parser) overconfig/routes.rb+modules/*/config/routes.rbthat resolves a route-helper stem (+ verb) →controller#action, making theInvokesAction/NavigatesTojoin resolvable for the first time.RoutesTo(stem → verb:controller#action) +RouteScope(member/collection/canonical/standalone), both Authoritative; predicate count-lock 71 → 73.collection_token/member_token(news → news_indexwhen singular==plural), singleton-collection-singular, absolute/-controller strip,controller:-kwarg ranking,module:-scope-wrap, naked-verb-in-resourcesnesting, string-path auto-naming,as: "", decl-order first-wins +duplicate_stem_conflicts, multi-name replay,escaped_dynamicfor non-literal args,only/exceptcoercion,shallowmember-drop.RAILS_CORPUS_SRC) drift fuse pinned to the measured corpus: 29 files, 1625 declared, 1534 emitted, escaped {mounts=9, via_all=6, dynamic=13},escaped_otherexactly["use_doorkeeper"].get "plugin/:id", action: :show_plugininnamespace :admin;:settings→admin/settings#show_plugin, never a verb name inescaped_other);as:value used verbatim for the collection name;except:respected (the excepted show route is not emitted).2.
schema.rs— post-baseline migration-replay (W3)replay_post_baseline_migrationsruns afterparse_tables_dir, replayingadd_column/rename_column/remove_column(s)/change_columnfromdb/migrate/*.rb+modules/*/db/migrate/*.rb(filename-sorted) onto the squashed baseline.columns_fromflips"baseline-only"→"baseline+replay"only when a mutation applies; a no-migration corpus stays byte-identical. Measured: WorkPackage 27 → 31 columns. 9 unit tests + a corpus-gated drift fuse.Verification
cargo test -p ruff_ruby_spo -p ruff_spo_triplet— 149 + 133 passed, 0 failedRAILS_CORPUS_SRC=/home/user/openprojectcargo clippy -p ruff_ruby_spo -p ruff_spo_triplet— clean on the touched filesrustfmt --checkclean on the 4 edited filesroutes.rs(new) +lib.rs(register) +schema.rs(W3 +pub(crate) singularize) +triple.rs(2 variants). Base = ruff main (nav plane + concept re-keying: the transcode-doctrine Phase 0/1/3/5 machinery #72) + W3.🤖 Generated with Claude Code
https://claude.ai/code/session_01LcsmBBVpETdnTqvDzx6ZiG