Skip to content

hot-plug: data-driven domain registration + parity hardening + C# arm handover#178

Merged
AdaWorldAPI merged 4 commits into
mainfrom
claude/medcare-bridge-lance-graph-wmx76z
Jul 7, 2026
Merged

hot-plug: data-driven domain registration + parity hardening + C# arm handover#178
AdaWorldAPI merged 4 commits into
mainfrom
claude/medcare-bridge-lance-graph-wmx76z

Conversation

@AdaWorldAPI

Copy link
Copy Markdown
Owner

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 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 silently dropped); effect facts (reads/writes/calls/raises) are irrelevant to the join, so a name-only ActionDef still 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-vocab 122 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 without dotnet/python3 reported a false failure. Added dotnet_available()/python3_available() probes: skip-with-notice when the toolchain is absent, hard-fail under CI so 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-csharp closes a cross-language parity witness (medcare-rs Rust vs MedCareV2 C#). Corrected after reading the code: the whole C# pull-in chain already ships (reassemble.rs:89, lift_actions lib.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 for AdaWorldAPI / ruff re-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 the ruff_csharp_spo loader 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

claude added 4 commits July 7, 2026 18:04
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
@cursor

cursor Bot commented Jul 7, 2026

Copy link
Copy Markdown

Bugbot couldn't run - usage limit reached

Bugbot 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)

@AdaWorldAPI AdaWorldAPI merged commit 90ba79f into main Jul 7, 2026
2 checks passed

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment on lines +211 to +212
let concept = def.object_class.rsplit('/').next().unwrap_or_default();
let id = crate::canonical_concept_id(concept).unwrap_or_default();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge 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 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants