Consumer migration §5 steps 2–5: harvest config, un-vendor ruff, de-pin, ogar-emit consumer path#71
Conversation
§5 step 2 of the OGAR V3 consumer-migration plan (.claude/handovers/2026-07-05-ogar-v3-consumer-migration-plan.md) — the additive, zero-risk first step. Stands up .claude/harvest/ as the ONE training wheel op-nexgen owns: the ORM→AR back-projection, promoted from the buried D-AR-3.5 vendor patch to explicit resolver config (data). Files: - README.md — what the directory is (the one training wheel), the data-not-code doctrine, the measure-don't-claim oracle discipline, the rules-file schema, and the §4-vs-schema.rs discrepancy log. - orm-ar-backprojection.toml — 15 [[rule]] rows (7 direct / 6 guess / 2 weak). Rules sourced from plan §4 + the vendored D-AR-3.5 harvest (vendor/AdaWorldAPI-ruff/crates/ruff_ruby_spo/src/schema.rs): - §4 table rows → typed_column (row1), null_false_presence (row2), foreign_key_name (row3), polymorphic_pair (row4), unique_index (row5), habtm_join_table (row6), counter_cache (row7), and the §4 row8 lft/rgt|parent_id split into nested_set + adjacency_tree. - schema.rs rules the §4 table omits → explicit_column (t.column form), implicit_pk + id_false_suppresses_pk (PK defaults), timestamps_pair, and the DIRECT DSL association extraction references_belongs_to + references_polymorphic (higher-confidence than §4's name-pattern guess). All 15 rules are validation="unmeasured" pending the AR-oracle pass; no rule ships as coverage until the 90/10 oracle diff confirms it. op-codegen-residual is untouched: its RESIDUAL_MANIFEST is output-side three-buckets data (a different concern) whose migration is a later step (§5 step 6); the README records where it lives. Doctrines restated: config is data — where data is insufficient, make ruff smarter (spec it, don't fake it here); measure, don't claim — every guess is validated against the AR oracle, no shipped coverage unmeasured. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D9qw3LYDprZi4uts2m57iq
…amed rev (consumer-migration §5 step 3) The vendor fork's entire ahead-delta (D-AR-3.5 schema stratum, Ruby method-visibility filtering, extract_tree_with DO-arm harvest, Predicate::ColumnNotNull + Model.inherits/Field.not_null) landed upstream as AdaWorldAPI/ruff 8d6c31b (strict-superset audit + 66/66 + 47/47 green there), so the vendored ruff_spo_triplet/ruff_ruby_spo retire in favor of rev-pinned git deps — the same pin ritual this repo already runs for OGAR. ruff_openproject (OP-local curation, never an upstream crate) relocates to crates/ as a workspace member with its fixtures made crate-local; ruff_python_dto_check (standalone, no manifest) relocates unmodified. The workspace exclude for the vendor mirror goes away with the mirror. Per the vendored-tree fuse doctrine (OGAR EPIPHANIES E-VENDOR-DELTA-IS-THE-TRAINING-WHEEL): a vendored tree gets a drift fuse or a deletion date — this is the deletion date. Drift protection is now the rev pin + the lock-pin bump ritual. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D9qw3LYDprZi4uts2m57iq
Absorbs the e84e1c0 IfMod pin bump and moves to c514b96, which adds the compute-linkage pass (schema field + compute def => emitted_by, Inferred-grade). ruff_openproject gains the additive schema-aware entry points (extract_graph_with_schema / extract_triples_with_schema) and the locked-shape test now exercises the honest path: fields from the schema stratum (fixture gains a db/migrate/tables/ baseline — work_packages.rb with the total_hours column, plus time_entries.rb for symmetry with the app/models fixture), raises from the IfMod fix, emitted_by from the linkage pass — extract_triples_produces_locked_shape green for the first time since the vendor era documented it as drift. op-codegen-pipeline steady. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D9qw3LYDprZi4uts2m57iq
… ruling — no rev pins) Operator ruling: rev pins on AdaWorldAPI-internal git deps are retired — "they're out of desperation to lock what hadn't converged in transpiler sink-in yet." Drift protection is the fuse tests, not pins. - ruff_openproject, op-codegen-pipeline: ruff_ruby_spo / ruff_spo_triplet flip from `rev = "c514b96d38d764bb16ac070fb2fdbd13477f41bc"` to `branch = "claude/odoo-rs-transcode-lf8ya5"`. - op-canon, op-codegen-projection, op-surreal-ast: ogar-vocab / ogar-class-view / ogar-render-askama flip from `branch = "main"` to the same convergence branch, so every OGAR dep in the workspace resolves to one git source (op-surreal-ast wasn't named in the original sweep but its ogar-vocab dep at branch=main produced a second, ambiguous source — flipped it too to restore single-source coherence). - lance-graph-contract stays on `branch = "main"` everywhere (already floating, never rev-pinned). Flips to `branch = "main"` once the convergence branches merge. Cargo.lock still locks a concrete rev for reproducibility. Verified: cargo tree -i ruff_spo_triplet / -i ogar-vocab each resolve to exactly one git source. cargo test -p ruff_openproject (3 lib + 4 integration), -p op-canon (24 lib + 8 doctest), -p op-codegen-projection (34 lib), -p op-codegen-pipeline (10 lib + 7 integration) all green. cargo check --workspace clean. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01D9qw3LYDprZi4uts2m57iq
…> OGAR lift/mint -> adapter emit (§5 steps 4-5)
Adds the ogar-emit consumer path alongside (not replacing) the native
ruff -> op-surreal-ast -> SurrealQL path: source -> ruff -> OGAR
lift/mint -> Class -> ogar-adapter-surrealql::emit_surrealql_ddl.
- op-codegen-pipeline/Cargo.toml: optional deps ogar-vocab,
ogar-from-ruff, ogar-adapter-surrealql (all on the convergence
branch, single-source with the rest of the workspace's OGAR deps;
ogar-adapter-surrealql pulled with default features only — its
surrealdb-parser feature stays off). New `ogar-emit` feature gates
the new module so the native path's dependency graph is untouched
by default.
- New `op_codegen_pipeline::ogar_consumer` module (feature-gated):
`compile_op::<P>` (thin wrapper over
`ogar_from_ruff::mint::compile_graph_ruby`), `emit_surreal_via_ogar`,
`render_surreal_via_ogar` (full extract -> filter -> emit chain), and
`render_classid_of::<P>` (accessor-only classid composition via
`ogar_vocab::app::render_classid_for`). Unit tests pin the
OpenProject/Redmine convergence (WorkPackage/Issue, TimeEntry,
Project all converge with distinct APP_PREFIXes) and
`render_classid_of::<OpenProjectPort>("WorkPackage") ==
Some(0x0102_0001)`.
- New tests/ogar_consumer_fixture.rs (gated on the `ogar-emit`
feature): drives the shared `tests/fixtures/rails_mini/` fixture
through the OGAR path, asserting DEFINE TABLE for WorkPackage +
TimeEntry, no AdhocThing, and typed schema-stratum fields (`subject`
required `string`, `done_ratio` optional `int`) — line-presence
assertions, deliberately not byte-golden against the native path's
different emit shape.
- Updated the ogar-v3 consumer migration plan (§5 steps 4-5) to
[DONE] / [DONE — additive], noting ogar-emitter was deliberately
deferred (targets V3 triples, not this step's DDL need) and that
full test-porting off the native path (step 6, retiring
op-surreal-ast) remains open.
Verified: cargo test -p op-codegen-pipeline (default features: 10 lib
+ 7 integration, native path untouched) and --features ogar-emit (13
lib + 2 new integration + 7 rails_fixture, all green). cargo tree -p
op-codegen-pipeline --features ogar-emit -i ruff_spo_triplet resolves
to one git source. cargo check --workspace --features
op-codegen-pipeline/ogar-emit clean.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D9qw3LYDprZi4uts2m57iq
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 5092688. Configure here.
| pub fn render_surreal_via_ogar(rails_root: &Path) -> String { | ||
| let (mut graph, _report) = ruff_ruby_spo::extract_app_with_schema(rails_root, crate::NAMESPACE); | ||
| crate::filter_to_core(&mut graph); | ||
| emit_surreal_via_ogar(&graph) |
There was a problem hiding this comment.
OGAR path drops pre-filter FK knowledge
Medium Severity
The render_surreal_via_ogar function filters the ModelGraph with filter_to_core before OGAR processing. This prematurely discards knowledge of non-core models. The native pipeline retains all model names to correctly resolve foreign key record<Target> types. Consequently, the OGAR path may generate SurrealQL DDL with mis-typed foreign key columns compared to the production pipeline.
Reviewed by Cursor Bugbot for commit 5092688. Configure here.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 509268807c
ℹ️ 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".
| crate::filter_to_core(&mut graph); | ||
| emit_surreal_via_ogar(&graph) |
There was a problem hiding this comment.
Preserve non-core targets before OGAR emission
When the ogar-emit path is run on a real OpenProject checkout, this filters the ModelGraph before compiling/emitting OGAR classes. The native path in this crate keeps a pre-filter target snapshot specifically because core models can point at non-core targets such as assigned_to -> Principal; after this filter, those target classes are unavailable for stub/table emission, so the OGAR DDL can contain relationships to classes that were removed from the emitted class set. Compile from the full graph or carry/synthesize target stubs before emitting the filtered core surface.
Useful? React with 👍 / 👎.
| # normally; they bypass workspace-member discovery. | ||
| exclude = [ | ||
| "vendor/AdaWorldAPI-ruff", | ||
| "crates/ruff_openproject", |
There was a problem hiding this comment.
Wire the moved dto-check tests into Cargo
This commit relocates the ruff_python_dto_check SQLX source and tests under crates/ruff_python_dto_check, but the workspace member list only adds crates/ruff_openproject, and there is no crates/ruff_python_dto_check/Cargo.toml in the tree. Cargo ignores directories without a manifest/member, so the moved tests under crates/ruff_python_dto_check/tests are never discovered and cargo test -p ruff_python_dto_check cannot exercise the SQLX target. Add a manifest/workspace member or keep these files out of crates/ as non-build fixtures.
Useful? React with 👍 / 👎.


Executes the open steps of
.claude/handovers/2026-07-05-ogar-v3-consumer-migration-plan.md§5 (steps 2–5 now[DONE]in the plan itself):.claude/harvest/(4102eb0): the ORM→AR back-projection training wheel as data — 15 rules (7 direct / 6 guess / 2 weak), every onevalidation = "unmeasured", doctrines (config-is-data, measure-don't-claim) in the README + TOML meta. Direct DSL rules suppress their name-pattern guess fallbacks; theunique_indexguess is flagged "make ruff smarter" (no backing harvest yet), not faked here.a561b41,ea8fcf7):vendor/AdaWorldAPI-ruffdeleted;ruff_openprojectpromoted tocrates/with crate-local fixtures; the vendor's ahead-deltas were upstreamed to ruff first (strict-superset audit), so nothing was lost. Theextract_triples_produces_locked_shapetest goes green for the first time since the vendor era — every fact mechanically grounded (schema-stratum column, IfMod-fix raise, compute-linkageemitted_by).9222d9b): operator ruling — no rev pins; all AdaWorldAPI deps float on the convergence branch, single-source verified (cargo tree -ione git source for bothruff_spo_tripletandogar_vocab; includes theop-surreal-astogar-vocabflip that the gate itself demanded).ogar-emitconsumer path (5092688): feature-gated moduleogar_consumer—compile_op::<P>→emit_surreal_via_ogar→render_surreal_via_ogar, accessor-thin, zero op-local transpiler logic,ogar-emitterdeliberately deferred (V3-triple emitter; the DDL path isogar-adapter-surrealql). Includes the OP↔Redmine convergence pin (same concept viaclass_idaccessors, divergingAPP_PREFIX) and fixture-driven emit tests onrails_mini. Native projection path untouched (default-features run proves it) until step 6's gated retirements.Tests: default features 10 lib + 7 integration (native path unchanged);
--features ogar-emit13 lib + 2 consumer integration + 7 rails_fixture, all green;ruff_openproject4/4 + 3/3;op-canon24 + 8 doctests; workspace check clean.🤖 Generated with Claude Code
https://claude.ai/code/session_01D9qw3LYDprZi4uts2m57iq
Generated by Claude Code