Skip to content

feat(cli): realize the extract/check/explain surface (explain in core, extract verb)#142

Merged
PhysShell merged 3 commits into
mainfrom
claude/roslyn-tools-own-net-l36cwp
Jun 27, 2026
Merged

feat(cli): realize the extract/check/explain surface (explain in core, extract verb)#142
PhysShell merged 3 commits into
mainfrom
claude/roslyn-tools-own-net-l36cwp

Conversation

@PhysShell

@PhysShell PhysShell commented Jun 27, 2026

Copy link
Copy Markdown
Owner

Что и зачем

Follow-up to #139 (the roslyn-tools note's re-aimed "Next PR"). It completes the advertised three-verb CLI — extract / check / explain — but split to where the architecture puts each, not bolted onto one binary. The "one checker" rule holds: the C# tool only emits facts; the core owns diagnostics.

  • explain → the core (python -m ownlang explain OWN001), next to the diagnostic catalogue. Prints what a code means, why it fires, and how to fix it. --json <findings|sarif> harvests every distinct code (code/ruleId keys) so you can explain exactly what a run produced. New EXPLANATIONS catalogue + DI001–005 titles in diagnostics.py.
  • extract → the C# tool gets an optional leading extract verb (bare form stays the default) and --out as the long twin of -o, so ownsharp-extract extract --project App.csproj --out facts.json works as advertised. Backward compatible.
  • check → documented as the own-check.sh orchestrator, which already chains extractor + core and (since feat(extractor): accept .csproj/.sln input (roslyn-tools CLI-first project resolution) #139) accepts a .csproj/.sln. Re-implementing a checker in C# stays the rejected path.

Тип изменения

  • Новая возможность (feature) — explain command + extract verb
  • Исправление бага
  • Документация
  • CI / инфраструктура

Проверка

  • explain is fully exercised locally (the core is Python): new tests/test_explain.py (folded into run_tests.py) covers the text builder, exit-code contract (no-codes / all-unknown / unreadable JSON → 2), --json harvest from findings arrays and SARIF logs (de-duped, first-seen), and DI-catalogue coverage. Whole suite green.
  • ruff + mypy --strict clean on the changed files.
  • End-to-end verified locally: ownir … --format sarifexplain --json <sarif> surfaces OWN001.
  • The C# extract verb / --out builds only in CI (no local dotnet); a wpf-extractor step asserts bare == --project == extract --out at the OwnIR fact boundary, plus an explain smoke.

Связанный issue

Нет — продолжение #139 / docs/notes/roslyn-tools-and-cli.md.

Чеклист

  • Тесты проходят (tests/run_tests.py, incl. new test_explain) + ruff + mypy --strict
  • CI-голдены на новую возможность (extract-verb parity + explain smoke)
  • Документация обновлена (note + frontend README)
  • Принцип "один чекер" сохранён — explain в ядре, C# только эмитит факты

Notes

A System.CommandLine migration of the C# tool (auto --help, validation) and --ref-dir-from-project-bin auto-derivation are the remaining polish — deliberately deferred over a blind framework swap, since the extractor compiles only in CI here.

🤖 Generated with Claude Code


Generated by Claude Code

Summary by CodeRabbit

  • New Features
    • Added ownlang explain to show diagnostic titles, why they appear, and suggested fixes, including bulk explanation via --json from findings/SARIF.
    • Introduced new DI001–DI005 diagnostic explanations.
    • Extended Roslyn extract to optionally accept a leading extract verb and to support --out/-o for the facts file.
  • Documentation
    • Updated CLI docs and examples for extract, check, and explain.
  • Bug Fixes
    • Improved explain argument validation and exit-code behavior for known vs unknown codes.
  • Tests / CI
    • Expanded explain test coverage and strengthened CI parity checks across project input modes.

…, extract verb)

Completes the advertised three-verb CLI from the roslyn-tools note, split to where
the architecture puts each — one checker, the C# tool only emits facts:

- explain (core): `python -m ownlang explain OWN001 [DI002 ...]` prints what a code
  means, why it fires, and how to fix it; `--json <findings|sarif>` harvests every
  distinct code (code/ruleId keys) and explains them. New EXPLANATIONS catalogue +
  DI001-005 titles in diagnostics.py. Exit 2 on no-codes / all-unknown / unreadable
  JSON. Tests in tests/test_explain.py (folded into run_tests.py).
- extract (C# tool): optional leading `extract` verb (bare form stays default) and
  `--out` as the long twin of `-o`, so `ownsharp-extract extract --project App.csproj
  --out facts.json` works as advertised. Backward compatible.
- check: documented as the own-check.sh orchestrator (already chains extractor +
  core and takes a .csproj/.sln) rather than a second checker in C#.

CI: extract-verb fact-parity (bare == --project == `extract --out`) + an explain
smoke (code + --json harvest from a real SARIF). Docs (note + READMEs) updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KzA6KGQVRzP52tBEKwJ3SW
@coderabbitai

coderabbitai Bot commented Jun 27, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 9037c39f-7990-4686-bf4b-b53f78f0d206

📥 Commits

Reviewing files that changed from the base of the PR and between ed83428 and 9f125fb.

📒 Files selected for processing (1)
  • ownlang/diagnostics.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • ownlang/diagnostics.py

📝 Walkthrough

Walkthrough

The PR adds an explain CLI with DI diagnostic catalog entries and JSON harvesting, extends the Roslyn extractor to accept an optional extract verb and --out, and updates tests, CI, and docs for the new command flows.

Changes

CLI surface updates

Layer / File(s) Summary
Explain command and catalogue
ownlang/diagnostics.py, ownlang/__main__.py
ownlang gains explain routing, JSON code harvesting, explanation formatting, and DI001–DI005 titles and explanations.
Explain tests and CI
tests/test_explain.py, tests/run_tests.py, .github/workflows/ci.yml
tests/test_explain.py covers explanation text, exit codes, JSON harvesting, and parser routing; the runner includes the suite, and CI checks explain OWN001 plus SARIF --json behavior.
Extractor verb and notes
frontend/roslyn/OwnSharp.Extractor/Program.cs, frontend/roslyn/README.md, docs/notes/roslyn-tools-and-cli.md, .github/workflows/ci.yml
The Roslyn extractor accepts an optional leading extract verb and --out, and the README, notes, and CI parity checks reflect the updated CLI surface.

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant Main as ownlang.__main__.main()
  participant Cmd as cmd_explain()
  participant Diagnostics as ownlang.diagnostics
  participant SARIF as SARIF JSON
  User->>Main: python -m ownlang explain OWN001 --json findings.sarif
  Main->>Cmd: route explain arguments
  Cmd->>SARIF: read code and ruleId values
  Cmd->>Diagnostics: look up TITLES and EXPLANATIONS
  Cmd->>User: print explanation text and exit status
Loading

Review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • PhysShell/Own.NET#25 — also updates the Roslyn/OwnSharp.Extractor CLI documentation and command split.
  • PhysShell/Own.NET#43 — related to SARIF-based harvesting and explain --json command flow.
  • PhysShell/Own.NET#139 — extends the same Roslyn extractor .csproj and --project parity checks now asserted in CI.

Poem

A rabbit hopped through explain with glee,
and DI codes lined up neatly for me.
From SARIF crumbs I nibbled the trail,
then popped out Fix: like a tiny white sail.
🐇✨

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: completing the CLI surface with explain in core and the extract verb.
Description check ✅ Passed The description is mostly complete and covers the required details, testing, issue linkage, and checklist.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/roslyn-tools-own-net-l36cwp

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai 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.

Actionable comments posted: 2

🧹 Nitpick comments (2)
ownlang/diagnostics.py (1)

97-164: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Consider adding a long-form EXPLANATIONS["DI001"] for parity. DI001 is emitted by the bridge and asserted in CI, but unlike DI002DI005 it has no long-form entry, so explain DI001 answers with only the one-line title via the fallback. Since DI001 is the umbrella "captive dependency" verdict a user is most likely to look up, a what/why/fix paragraph would round out the catalogue. Functional today thanks to the title fallback, so this is optional.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ownlang/diagnostics.py` around lines 97 - 164, Add a long-form EXPLANATIONS
entry for DI001 in diagnostics.py so it matches the DI002–DI005 pattern. Update
the EXPLANATIONS mapping with a descriptive multi-sentence explanation for the
DI001 captive-dependency verdict, including what it means and how to fix it, and
keep the existing DI001 title behavior intact so explain DI001 returns the
richer text instead of only the fallback.
.github/workflows/ci.yml (1)

1013-1015: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Minor: || true can mask a SARIF-generation failure. If ownir ... --format sarif fails, $RUNNER_TEMP/proj.sarif is left empty/partial and the failure is swallowed; the next grep still fails, but with a misleading "did not harvest OWN001" message rather than the real generation error. Consider dropping || true here (the previous proj-facts.json step already validated the facts), so a genuine SARIF emit regression surfaces directly.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 1013 - 1015, The SARIF generation step
is swallowing real failures because `ownir` is followed by `|| true`, which can
leave `proj.sarif` empty or partial and produce a misleading later `grep`
failure. Remove the `|| true` from the workflow command so a genuine emit
regression fails immediately, and keep the existing `ownir`/`explain --json`
check sequence to validate the SARIF output directly.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@docs/notes/roslyn-tools-and-cli.md`:
- Around line 127-130: The example for the `ownlang explain --json` command is
pointing at the wrong artifact; update the docs so `explain` uses checker output
such as findings or SARIF JSON rather than `facts.ownir.json` from
`ownsharp-extract`. Keep the surrounding examples (`ownsharp-extract`,
`scripts/own-check.sh`, and `python -m ownlang explain`) but replace the
`--json` target with the checker-produced JSON source that contains the
diagnostic codes `explain` reads.

In `@frontend/roslyn/README.md`:
- Around line 44-49: The README example for `ownlang explain --json` points at
`facts.json`, but `explain --json` expects diagnostic codes from findings or
SARIF JSON. Update the example under the `extract`/`explain` description to
reference the checker’s findings/SARIF artifact instead, and keep the guidance
aligned with the `ownlang explain` command so users are directed to a valid JSON
input.

---

Nitpick comments:
In @.github/workflows/ci.yml:
- Around line 1013-1015: The SARIF generation step is swallowing real failures
because `ownir` is followed by `|| true`, which can leave `proj.sarif` empty or
partial and produce a misleading later `grep` failure. Remove the `|| true` from
the workflow command so a genuine emit regression fails immediately, and keep
the existing `ownir`/`explain --json` check sequence to validate the SARIF
output directly.

In `@ownlang/diagnostics.py`:
- Around line 97-164: Add a long-form EXPLANATIONS entry for DI001 in
diagnostics.py so it matches the DI002–DI005 pattern. Update the EXPLANATIONS
mapping with a descriptive multi-sentence explanation for the DI001
captive-dependency verdict, including what it means and how to fix it, and keep
the existing DI001 title behavior intact so explain DI001 returns the richer
text instead of only the fallback.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: fb886c6c-f8bb-4b5c-aec8-a205dfad049a

📥 Commits

Reviewing files that changed from the base of the PR and between 4818e44 and a1b497d.

📒 Files selected for processing (8)
  • .github/workflows/ci.yml
  • docs/notes/roslyn-tools-and-cli.md
  • frontend/roslyn/OwnSharp.Extractor/Program.cs
  • frontend/roslyn/README.md
  • ownlang/__main__.py
  • ownlang/diagnostics.py
  • tests/run_tests.py
  • tests/test_explain.py

Comment thread docs/notes/roslyn-tools-and-cli.md Outdated
Comment thread frontend/roslyn/README.md Outdated
…m, CI rc-aware (CodeRabbit)

Three CodeRabbit review points on the explain/extract PR:

- docs (Minor, correctness): `explain --json` harvests diagnostic *codes* (code/
  ruleId), which `facts.ownir.json` (extractor facts) does not carry — the note +
  README examples now point `--json` at the checker's findings/SARIF output and
  call out the distinction.
- diagnostics.py (Trivial): add a long-form EXPLANATIONS["DI001"] so the umbrella
  captive-dependency verdict gets a what/why/fix paragraph like DI002-DI005,
  instead of only the title fallback.
- ci.yml (Trivial): the SARIF step's blanket `|| true` could mask a real emit
  regression. ownir exits 1 when findings exist (expected — the sample leaks), so
  capture the rc and tolerate <=1 while surfacing 2+ (a genuine error).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KzA6KGQVRzP52tBEKwJ3SW

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@ownlang/diagnostics.py`:
- Around line 104-105: The lifetime-fix guidance in diagnostics.py should not
present Func<T> as a default Microsoft.Extensions.DependencyInjection solution.
Update the relevant messages around the diagnostics text to recommend
IServiceScopeFactory and per-use scope creation as the primary fix, and only
mention Func<T> if it is clearly qualified as container-dependent or manually
registered. Make the wording consistent in the affected diagnostics entries so
it does not imply Func<T> is auto-resolved by the built-in container.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: c98850af-ab3f-4afe-abb1-a22292eb76f4

📥 Commits

Reviewing files that changed from the base of the PR and between a1b497d and ed83428.

📒 Files selected for processing (4)
  • .github/workflows/ci.yml
  • docs/notes/roslyn-tools-and-cli.md
  • frontend/roslyn/README.md
  • ownlang/diagnostics.py
✅ Files skipped from review due to trivial changes (1)
  • frontend/roslyn/README.md
🚧 Files skipped from review as they are similar to previous changes (2)
  • .github/workflows/ci.yml
  • docs/notes/roslyn-tools-and-cli.md

Comment thread ownlang/diagnostics.py Outdated
…nc<T> (CodeRabbit)

The built-in Microsoft.Extensions.DependencyInjection container does not
auto-resolve Func<T> (it needs manual registration or a third-party container),
so the DI001/DI002/DI003 explanation text no longer presents Func<T> as the
default fix. Primary guidance is now IServiceScopeFactory + per-use scope
creation; Func<T> is mentioned only as a registered/container-dependent option.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KzA6KGQVRzP52tBEKwJ3SW
@PhysShell PhysShell merged commit 98ebb51 into main Jun 27, 2026
28 checks passed
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