Expose the build model as JSON via --describe and --plan --json (#642) - #662
Open
phmatray wants to merge 9 commits into
Open
Expose the build model as JSON via --describe and --plan --json (#642)#662phmatray wants to merge 9 commits into
phmatray wants to merge 9 commits into
Conversation
phmatray
marked this pull request as ready for review
August 27, 2026 11:20
ChrisonSimtian
requested changes
Aug 28, 2026
ChrisonSimtian
left a comment
Collaborator
There was a problem hiding this comment.
a lot of this code is static methods and static classes. I think this one needs a bit more hands on from a human, the code quality unfortunately trips AI to think all-static is a good and preferred pattern.
might be worth to nudge your agent into the right direction and have it have another go 😊
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements #642.
Closes #642.
Executing the implementation plan task-by-task; the checklist below — and the plan on the issue — are ticked as each task lands. Opened as a draft — will be marked ready after the final task and a code-review pass.
Two constraints shaped the design, both verified in the tree:
BuildGraphUtilityalready projects targets and all four relation kinds into a schema-versioned document, so this extends that model additively rather than inventing a second one.SchemaVersionstays1, which also delivers Emit declared build parameters into build-graph.json #499'sparameters[].BuildManager.ExecuterunsToolRequirementService.EnsureToolRequirements(writesnuget.csproj, shells out todotnet restore) before theIOnBuildInitializedhooks. So the "runs no external tool" criterion rules out another extension; the emission short-circuits above that line.The commands land as the build-side parameters
--describeand--plan --json, becauseCommandDispatcherroutes bare arguments to the build and reserves:-prefixed tokens for CLI commands.Plan
--describeemits the model before tool requirements run--plan --jsonemits the resolved execution plan--helpfrom the shared model--helpis byte-identical tomainVerified by building
mainin a scratch worktree and diffing the output: the only difference is the version string.--helptakes its displayed fields from the shared model (so the two views cannot drift) but keeps ordering — of targets, of each dependency line, and of parameters — from the declarations, because declaration order carries the pipeline reading a human wants.Code review
A review of the branch surfaced 15 findings; all are addressed. The ones worth calling out:
BuildExecutor.MarkTargetSkippedonly skips when!target.Invoked, so--skipnever stops an explicitly invoked target. The projection had no such guard and reported"skip": "via parameter"for it — a consumer driving CI off the plan would conclude nothing runs. Fixed, with a spec that pins the parity.IOnBuildCreatedextensions, so each misbehaving extension needed its own opt-out — andUpdateNotificationAttributecallsConsole.ReadKey(), which would have deadlocked a piped--describe. The gate now fires before any extension runs, and the per-extension opt-out is gone.IsRequestedreads each flag from the injected property or the raw arguments, becauseInjectParameterValuesAttributeis itself anIOnBuildCreatedextension.[Requires<T>]was invisible. That attribute targets Class/Interface only, so class-level requirements could never appear on a target and were missing entirely. They now project to a root-leveltoolRequirements[].Type.FullNameleaked the runtime version into the contract for constructed generics (List\1[[System.String, …, Version=10.0.0.0, …]]`). Generic arguments are now rendered recursively.listfield that could never be false, gave the plan and error documents their own schema-version constants, and de-duplicated the serializer options, version lookup, and skip-reason string that had been copy-pasted.Making standard output actually parseable
Running the feature revealed that the document was correct but unreachable through any shipped entry point — everything upstream of the build wrote to stdout too, so
--describecould not be piped into a parser. Three fixes:Fallout.Clicompiled the build project with its output inherited on stdout. For an introspection request that step now goes to standard error. Measured through the locally built CLI: 12 123 bytes of JSON on stdout, 8 582 bytes of build noise on stderr. Ordinary runs are unchanged — build output stays on stdout.build.ps1/build.sh(and the shipped templates, kept in sync) printed their banner anddotnet tool restoreoutput to stdout. That is provisioning diagnostics, so it moves to standard error.--helpno longer aborts when stdout has no console.Console.BufferWidththrowsIOExceptionbehind a pipe or on a console-less agent, which killed--helpafter the target list and before the parameters. The wrap width is cosmetic, so it now falls back to the existing 90-column cap. This closes--helpcrashes with IOException when stdout has no console handle #616.Also: the emitted documents are newline-terminated, matching
SchemaUtility's JSON../build.ps1 --describestays unparseable until a Fallout release carrying the CLI fix is installed, because the script delegates to whateverdotnet falloutis on the machine. Nothing in this PR can change an already-installed tool.Follow-ups
[Requires]resolves its version over the network insideNuGetPackageRequirement's constructor, before any short-circuit. Pre-existing, affects--helptoo; "runs no external tool" is scoped to this code path.IOnBuildPlannedextension hook this deliberately avoided is worth revisiting once FT-7 ([Foundation] FT-7: Formalize the build extension pipeline (deterministic ordering + typed phases) #312) formalizes the extension pipeline; Declare target effects and gate them at plan time #644 would use it too.🤖 Generated with Claude Code