Implement the v3 output schemas: structured why + doctor reports#51
Conversation
|
Important Review skippedAuto reviews are limited based on label configuration. 🏷️ Required labels (at least one) (1)
🚫 Excluded labels (none allowed) (2)
Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: ASSERTIVE Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
📝 WalkthroughWalkthroughThis PR rolls out JSON schema version 3 as the new default output format for Sequence Diagram(s)sequenceDiagram
participant User as User CLI
participant Dispatch as lib.rs<br/>Dispatch
participant Validator as schema::validate_*<br/>schema_version
participant DoctorCmd as cmd::doctor<br/>why()
participant Builder as DoctorReportV3<br/>WhyReportV3
participant JSON as serde_json<br/>serialize
User->>Dispatch: doctor --json --schema-version 3
Dispatch->>Validator: doctor_schema_version_for_json(3)
Validator-->>Dispatch: Ok(3)
Dispatch->>DoctorCmd: cmd::doctor(..., schema_version=3)
DoctorCmd->>Builder: if schema_version >= 3: build_report_v3(ctx)
Builder-->>DoctorCmd: DoctorReportV3 with expanded metadata
DoctorCmd->>JSON: serde(report_v3)
JSON-->>DoctorCmd: pretty JSON string
DoctorCmd-->>User: print to stdout
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
🚥 Pre-merge checks | ✅ 7 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (7 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
`why --json` gains `{task, match}` candidate pairs plus a decision block;
`doctor --json` becomes a diagnostic inventory (provenance, per-ecosystem
decisions, sources, fqn-keyed tasks, tools, conflicts, diagnostics).
Shims modeled generically (keyed by tool, manager as data). doctor/why
default to v3; list stays v2. v3 examples validate against the schemas.
The #50 examples carried real home paths, Volta layout, and exact tool versions. Replace with fabricated values describing no real host.
Keep Node in the v3 ecosystems/tools inventory when package.json tasks resolve via npm but no Node PM was detected, so the report stays internally consistent. Pin the human doctor renderer to an explicit v2 contract instead of CURRENT_VERSION, so a future `list` version bump can't silently change human output. Also trim overly verbose comments across the crate: war stories, issue/version trivia, and micro-optimization rationale, keeping the concise WHY a mature codebase wants.
Switch the v3 fqn delimiter from `<scope>:<kind>:<name>` to `<scope>:<kind>#<name>`, so a task name that itself contains `:` (e.g. an npm script `fmt:update`) stays unambiguous — consumers split once on `#`. Centralise the format in `labels::fqn` so `why` and `doctor` can't drift apart on it. doctor: probe real tool versions by running `<binary> --version` when detection didn't already supply one, instead of always emitting null. The first dotted, digit-led token wins across the varied `--version` formats; `v` prefixes stripped. doctor: add `is_alias` to task entries so consumers can tell an alias from a real target without reverse-engineering `definition`.
Run deno.json tasks in-process via deno_task_shell so a deno project works without the `deno` binary: the default policy self-execs only when deno is absent, the `unstable-deno-exec` feature makes it primary. Tasks that invoke `deno` or declare `dependencies` still need it. Parse deno task descriptions (string and object form) and surface them in list/why/doctor. Factor the shell engine into a reusable `tool::shell`; `deno_exec` is the thin deno-specific layer over it. doctor: add per-task `self_executable` and derive the deno tool's `required` from it; probe package-manager/runner versions via `--version` instead of always reporting null.
Surface the canonical cargo subcommand a built-in alias renames (`test`, `build`, …) as the task, with the short form (`t`, `b`) folded under it instead of standing alone — `t` is an alias of `test`, not a peer. Aliases carrying extra args (`bb`, `cl`) keep their own rows; only bare renames fold. `runner list` shows `test (t)`; both `runner run test` and `runner run t` still dispatch `cargo test`. Note `cargo run` now coexists with a `just run` task as a duplicate-name conflict.
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
runner | 1766c0a | Commit Preview URL Branch Preview URL |
Jun 14 2026, 09:20 PM |
|
Review the following changes in direct dependencies. Learn more about Socket for GitHub.
|
|
Warning Review the following alerts detected in dependencies. According to your organization's Security Policy, it is recommended to resolve "Warn" alerts. Learn more about Socket for GitHub.
|
Cross-source name collisions (e.g. `cargo run` shadowed by `just run`) were invisible in `runner list` / bare `runner` — both tasks just showed as separate rows, hiding that the bare-name lookup silently runs only one. Add a conflict footer that names the winning source and the shadowed ones, resolved with the same precedence as `runner run` so the verdict is accurate. Shown in both the list and the info glance.
|
Did a deep review pass. Implementation quality is high — Three things stood out that I think are implicit decisions worth making explicit before merge, since they all point the same direction — the output contract is looser / more breaking than the diff suggests:
None of these are code-quality blockers — the Rust is solid. They're contract/release-hygiene decisions that are currently unstated. |
Add `deny_unknown_fields` to the doctor/why v3 types so the generated schemas set `additionalProperties: false` again (the draft had it; the schemars-generated finals had dropped it). Stray or misspelled fields in real output now fail validation instead of passing silently. Release prep for 0.13.1 (the v3 default-output move warrants a bump; patch per 0.x semantics — consumers pin to avoid the shift): - bump Cargo.toml/lock to 0.13.1 - fix the stale `.zed` schema mapping (drop the deleted v3-draft entry, add doctor.v3 / why.v3 example->schema mappings) - move the Unreleased v3 work into a [0.13.1] section and document the deno self-exec, task descriptions, cargo-alias fold, and conflict footer
* feat(schema): structured v3 reports for why and doctor
`why --json` gains `{task, match}` candidate pairs plus a decision block;
`doctor --json` becomes a diagnostic inventory (provenance, per-ecosystem
decisions, sources, fqn-keyed tasks, tools, conflicts, diagnostics).
Shims modeled generically (keyed by tool, manager as data). doctor/why
default to v3; list stays v2. v3 examples validate against the schemas.
* chore(schemas): replace host data in v1/v2 example fixtures
The #50 examples carried real home paths, Volta layout, and exact tool
versions. Replace with fabricated values describing no real host.
* fix(ci): pages sparse-checkout needs non-cone mode for globs
* fix(doctor): keep Node in v3 report; pin human v2
Keep Node in the v3 ecosystems/tools inventory when package.json
tasks resolve via npm but no Node PM was detected, so the report
stays internally consistent. Pin the human doctor renderer to an
explicit v2 contract instead of CURRENT_VERSION, so a future
`list` version bump can't silently change human output.
Also trim overly verbose comments across the crate: war stories,
issue/version trivia, and micro-optimization rationale, keeping
the concise WHY a mature codebase wants.
* style(zed): trailing commas + multiline json schema list
* feat(schema): disambiguate fqn, probe tool versions
Switch the v3 fqn delimiter from `<scope>:<kind>:<name>` to
`<scope>:<kind>#<name>`, so a task name that itself contains `:`
(e.g. an npm script `fmt:update`) stays unambiguous — consumers
split once on `#`. Centralise the format in `labels::fqn` so `why`
and `doctor` can't drift apart on it.
doctor: probe real tool versions by running `<binary> --version`
when detection didn't already supply one, instead of always
emitting null. The first dotted, digit-led token wins across the
varied `--version` formats; `v` prefixes stripped.
doctor: add `is_alias` to task entries so consumers can tell an
alias from a real target without reverse-engineering `definition`.
* feat(deno): self-exec tasks via embedded shell
Run deno.json tasks in-process via deno_task_shell so a deno project
works without the `deno` binary: the default policy self-execs only
when deno is absent, the `unstable-deno-exec` feature makes it primary.
Tasks that invoke `deno` or declare `dependencies` still need it.
Parse deno task descriptions (string and object form) and surface them
in list/why/doctor. Factor the shell engine into a reusable
`tool::shell`; `deno_exec` is the thin deno-specific layer over it.
doctor: add per-task `self_executable` and derive the deno tool's
`required` from it; probe package-manager/runner versions via
`--version` instead of always reporting null.
* feat(cargo): fold short aliases under subcommands
Surface the canonical cargo subcommand a built-in alias renames
(`test`, `build`, …) as the task, with the short form (`t`, `b`)
folded under it instead of standing alone — `t` is an alias of
`test`, not a peer. Aliases carrying extra args (`bb`, `cl`) keep
their own rows; only bare renames fold.
`runner list` shows `test (t)`; both `runner run test` and
`runner run t` still dispatch `cargo test`. Note `cargo run` now
coexists with a `just run` task as a duplicate-name conflict.
* feat(list): surface duplicate-name conflicts as a footer
Cross-source name collisions (e.g. `cargo run` shadowed by `just
run`) were invisible in `runner list` / bare `runner` — both tasks
just showed as separate rows, hiding that the bare-name lookup
silently runs only one. Add a conflict footer that names the
winning source and the shadowed ones, resolved with the same
precedence as `runner run` so the verdict is accurate. Shown in
both the list and the info glance.
* fix(schema): reject unknown fields in v3 output
Add `deny_unknown_fields` to the doctor/why v3 types so the generated
schemas set `additionalProperties: false` again (the draft had it; the
schemars-generated finals had dropped it). Stray or misspelled fields in
real output now fail validation instead of passing silently.
Release prep for 0.13.1 (the v3 default-output move warrants a bump;
patch per 0.x semantics — consumers pin to avoid the shift):
- bump Cargo.toml/lock to 0.13.1
- fix the stale `.zed` schema mapping (drop the deleted v3-draft entry,
add doctor.v3 / why.v3 example->schema mappings)
- move the Unreleased v3 work into a [0.13.1] section and document the
deno self-exec, task descriptions, cargo-alias fold, and conflict footer
What
Implements the schema v3 data models that shipped in #50 as unreviewed
drafts. Both drafts got the same treatment: compare against the actual
codebase, implement, validate the real output against the draft, retire the
draft.
why --jsonv3 (default forwhy)Report restructured around
{task, match}candidate pairs plus adecisionblock. Tasks carry identity (fqn=root:<kind>:<name>,provider,kind— cargo aliases labeledcargo-alias), origin(
sourcefile,source_pointerkey path), and resolution data(
definition,resolvedpreview,cwd, siblingaliases). Thematchhalf exposes the exact run-time selection key;
decision.strategynamesthe branch taken (
single-candidate/ranked/filtered/exec-fallback).doctor --jsonv3 (default fordoctor)Flat v2 dump becomes a structured diagnostic inventory:
invocation/environment/runnerprovenance, per-ecosystemsdecisionsgraded by
confidence(typedResolutionStep→ override/manifest/lockfile= high, PATH probe = medium, legacy npm fallback = low, failure = none),
first-class
sources,fqn-keyedtaskswith effectiveresolvedcommands, PATH-probed
tools, duplicate-task-nameconflicts(winner,shadowed, ranking), flattened
diagnostics, and a self-describingresolutionpolicy block.Verification
whyv3 real output reproduces the former draft example verbatim(semantic diff: identical); promoted to
schemas/why.v3.example.json.doctorv3 real output validates against the original draft schemaand the committed generated one (ajv, draft 2020-12); example committed.
runnertask isdefined by both the justfile and cargo-aliases; the report names
root:just:runnerthe winner with the ranking that decided it.clippy --all-targets --all-featuresclean, zero lint suppressions,gen-schemadrift-stable.Review deltas from the drafts (documented in module docs)
tasks[].resolved/sourceare nullable — PM resolution can fail;the draft would have forced lying.
sources[].kindaligned to the why-v3 label convention(
cargo-alias), fixing the drafts' cross-surface inconsistency.identity, probe-error status, unused severities/tool kinds) are
deferred, not declared — contracts describe output, not ambition.
Versioning
Surfaces version independently:
doctorandwhyare at v3,liststays at v2 and rejects
--schema-version 3rather than mislabel anunchanged payload. v1/v2 output unchanged and reachable via
--schema-version. Human output untouched (doctor human path pins to thev2 builder).