hot-plug: data-driven domain registration + parity hardening + C# arm handover#178
Conversation
Two things: 1. Finish the consumer parity (#177 follow-up). The C#/Python adapter parity tests hard-`.expect()`ed their external toolchain, so any environment without `dotnet` / `python3` reported a FALSE failure for a missing-toolchain condition (not a code defect) — e.g. a bare sandbox with no .NET SDK. Added `dotnet_available()` / `python3_available()` probes: skip with a printed SKIP notice when the toolchain is absent, but HARD-FAIL under `CI` (env var set) so a runner missing the SDK breaks loudly rather than silently dropping coverage. CI images must provision the net8.0 SDK / a python3 interpreter (both already assumed by the pure-BCL / py_compile loops). Verified: both crates' parity targets are now green in a bare sandbox (C# skips 2, Python runs 2), fmt + clippy clean. 2. Add docs/integration/MEDCARE-CSHARP-PARITY-INTEGRATION-PLAN.md (PROPOSAL, operator-gated). The absence of a healthcare ActionDef table is a deficiency, not a limit: the OCR table is hand-authored only because tesseract has no ActiveRecord source; MedCare HAS a C# source, so its healthcare action table should be HARVESTED (ruff_csharp_spo -> ModelGraph -> ogar-from-ruff::lift_actions) not fabricated. And because MedCare IS C#, the same capability surface rendered two ways -- medcare-rs (Rust, ogar-vocab) and MedCareV2 (C#, ogar-adapter-csharp emit) -- is a diverse-redundancy cross-language parity witness. The plan sequences consumer-parity-first (Loop B, P1-P2) then harvest-the-deficiency (Loop A, P3-P6), probe-first with per-phase KILL conditions, and records the missing pieces (a C# frontend for lift_actions; the executor/capability set question). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EYvNjD8M8LMNYbRy3gq2FP
…m walk missing)
Read the actual code across ogar/ruff. The earlier draft overstated the
gap ("no C# frontend exists"). Reality:
- ActionDef (ogar-vocab/src/lib.rs:380): rich DO-arm IR — reads/writes/
calls/raises + kausal + Rubicon carriers.
- lift_actions (ogar-from-ruff/src/lib.rs:574): language-agnostic, ready.
- ruff_spo_triplet: closed Predicate vocab ALREADY includes the DO-arm
(ReadsField/WritesField/WritesIfBlank/Raises/Calls, triple.rs:100-456)
+ reassemble(&[Triple]) -> ModelGraph (reassemble.rs:89).
- ruff_csharp_spo (102 lines): loader/validator, NAMESPACE="medcare",
"MedCare first". The parse is an out-of-process Roslyn harvester
(harvester/Program.cs) — Roslyn isn't Rust-callable.
So the whole C# pull-in chain SHIPS. The only gap is that the Roslyn
harvester emits THINK-arm predicates only (rdf:type/inherits_from/
has_field/field_type/has_function/is_static — per its own test fixture);
it does not yet walk method bodies to emit reads_field/writes_field/
raises/calls. Therefore lift_actions over a C# model works today but
yields name-only ActionDefs.
Consequence: name-only is ENOUGH for the hot-plug table (it keys on the
has_function name + subject classid, not the body). Revised phases:
P3 = the name-only healthcare_actions.rs table on the existing harvest
(critical path); P6 = the harvester DO-arm walk (enrichment, orthogonal,
gated on ruff's fuzzy-recipe-codebook). Only one new OGAR file
(healthcare_actions.rs) sits above an already-shipping chain.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYvNjD8M8LMNYbRy3gq2FP
…ta") Make domain registration data-driven instead of per-domain bespoke code. `entries_from_actions(&[ActionDef]) -> Vec<(String, u16)>` derives a domain's (capability, subject classid) join rows GENERICALLY from any frontend's lifted ActionDefs (Ruby/Rails, Python/Odoo, C#/Roslyn, C++/libclang — all via ogar-from-ruff::lift_actions). The concept is the last '/'-segment of object_class, so any namespace prefix works; an unminted concept maps to id 0 (survives so resolve_hotplug's UnknownClassid / verify_registration's Unminted fuse fires, never a silent drop); effect facts (reads/writes/calls/raises) are irrelevant to the join, so a name-only ActionDef (a C# lift before the harvester's method-body walk lands) still derives a valid row. The hand-authored OCR table (ocr_entries) now routes through this seam — behavior-unchanged (adapter parity still matches ground truth), proving the generic path subsumes the bespoke one. A future consumer (medcare healthcare, thinking-styles) registers a domain by supplying harvested ActionDefs, never by copying the OCR logic. This is the reusable, language-agnostic expansion: config becomes data. 4 new tests: prefix-agnostic, effect-fact-independent, unminted-row-survives-for-the-fuse, OCR-eight-rows-unchanged. ogar-vocab 122 tests green; fmt + clippy clean. Plan doc updated: records this seam + that ruff #57 shipped the C++ DO-arm (ruff_cpp_spo::method_body_arm), which closes the doc's "missing C++ arm" and is the template the remaining C# Roslyn arm mirrors. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EYvNjD8M8LMNYbRy3gq2FP
Parked in OGAR .claude/draft/ because ruff writes are 403-blocked this session (org-level: the Claude GitHub App needs reconnecting for AdaWorldAPI / ruff re-added to scope). A session with ruff write access (ideally + dotnet) picks this up. Corrects the record: an earlier claim this session said the C# DO-arm was the sole remaining MISSING arm. Reading harvester/Program.cs shows EmitBodyArm (lines 97-105 call, 147-257 impl) ALREADY emits the DO-arm predicates (writes_field/reads_field/raises/calls/writes_if_blank), marked DRAFT + untested. The earlier claim was inferred from the loader crate's STALE test fixture, not the harvester source. So the real gap is verify + cover + de-draft, not author-from-scratch. The handover carries: the exact file:line state table; a close-out checklist; a ready-to-apply Rust patch extending the ruff_csharp_spo loader fixture to exercise the DO-arm predicates (the one writable-now change, no dotnet needed, +5 rows -> assert 11); the full-chain verification recipe; and cross-refs to the C++ template (ruff_cpp_spo::method_body_arm, #57), the generic entries_from_actions seam shipped this session (ec52376), and the parity plan. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EYvNjD8M8LMNYbRy3gq2FP
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_ec5fec2f-65ad-4b90-9a94-e82dde7bdb89) |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1b1e83cca0
ℹ️ 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".
| let concept = def.object_class.rsplit('/').next().unwrap_or_default(); | ||
| let id = crate::canonical_concept_id(concept).unwrap_or_default(); |
There was a problem hiding this comment.
Resolve raw ActionDef aliases before classid lookup
When this helper is fed the advertised ogar-from-ruff::lift_actions output, object_class is the raw model name (that lift calls ActionDef::new(..., &model.name) in crates/ogar-from-ruff/src/lib.rs:600-604), not necessarily a <prefix>/<canonical_concept> string. With no slash, this line passes values like WorkPackage straight to canonical_concept_id, which only accepts canonical codebook names, so promoted aliases resolve to 0 instead of their real classid. Any domain registered from harvested actions with curator-shaped class names will then fail hot-plug/registration with sentinel ids; strip the namespace and then use the alias-aware codebook/canonicalization path before falling back to 0.
Useful? React with 👍 / 👎.
Four commits on top of #177, all green, fmt + clippy clean.
entries_from_actions— "config becomes data" (ec52376)ogar_vocab::capability_registry::entries_from_actions(&[ActionDef]) -> Vec<(String, u16)>derives a domain's(capability, subject classid)hot-plug join rows generically from any frontend's liftedActionDefs (Ruby/Rails, Python/Odoo, C#/Roslyn, C++/libclang — all viaogar-from-ruff::lift_actions). The concept is the last/-segment ofobject_class, so any namespace prefix works; an unminted concept maps to id0(survives soresolve_hotplug'sUnknownClassid/verify_registration'sUnmintedfuse fires, never silently dropped); effect facts (reads/writes/calls/raises) are irrelevant to the join, so a name-onlyActionDefstill derives a valid row.The hand-authored OCR table now routes through this seam — behavior-unchanged (adapter parity still matches ground truth), proving the generic path subsumes the bespoke one. A future consumer (medcare healthcare, thinking-styles) registers a domain by supplying harvested
ActionDefs, never by copying table logic. 4 new tests (prefix-agnostic, effect-fact-independent, unminted-row-survives-the-fuse, OCR-eight-rows-unchanged);ogar-vocab122 tests green.Adapter parity: skip-when-toolchain-absent (582d927)
The #177 C#/Python adapter parity tests hard-
.expect()ed their external toolchain, so any environment withoutdotnet/python3reported a false failure. Addeddotnet_available()/python3_available()probes: skip-with-notice when the toolchain is absent, hard-fail underCIso a runner missing the SDK breaks loudly rather than silently dropping coverage. Both crates now green in a bare sandbox (C# skips 2, Python runs 2 for real).MedCare ⇄ C# parity plan + corrections (582d927, e36dfc6)
docs/integration/MEDCARE-CSHARP-PARITY-INTEGRATION-PLAN.md— the ActionDef "deficiency" is a harvest gap, not a limit: MedCare has a C# source, so its healthcare action table should be harvested (ruff_csharp_spo → reassemble → lift_actions), not fabricated. And since MedCare IS C#,ogar-adapter-csharpcloses a cross-language parity witness (medcare-rsRust vs MedCareV2 C#). Corrected after reading the code: the whole C# pull-in chain already ships (reassemble.rs:89,lift_actionslib.rs:574); ruff #57 shipped the C++ DO-arm (method_body_arm); the C# DO-arm is drafted in the harvester, not missing.Draft handover: close out the C# DO-arm (1b1e83c)
.claude/draft/ruff-csharp-do-arm-handover.md— parked here because ruff writes are 403-blocked this session (org-level: the Claude GitHub App needs reconnecting forAdaWorldAPI/ruffre-added to scope; reads work, every write path 403s). Self-contained work order for a session with ruff access: state table, checklist, a ready-to-apply Rust patch (extend theruff_csharp_spoloader fixture to cover the DO-arm predicates, no dotnet needed), full-chain verify recipe, cross-refs.🤖 Generated with Claude Code
https://claude.ai/code/session_01EYvNjD8M8LMNYbRy3gq2FP
Generated by Claude Code