Repository navigation
Suppress individual findings by query hash in .planview.json (#651) - #652
Merged
Merged
Conversation
Add a "suppressions" list beside "rules". Each entry names a statement by query hash plus a rule number or warning type, an optional operator label substring, and a required reason. Matching findings are flagged, never removed, and drop out of total_warnings, critical_warnings and warning_types. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018MrD9SGvLFx4kJaeGZyXG9
Link OperatorLabel.cs into the Web project. Catch loader failures in the REPL and the desktop app. Exclude suppressed warnings from the grid, root badge, advice chip and comparison counts. Name the real config file in validation errors. Add CLI, grouped text, advice and comparison tests. Docs follow-ups. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018MrD9SGvLFx4kJaeGZyXG9
ConfigLoader.Load read the file unguarded, so a locked or permission-denied config threw IOException or UnauthorizedAccessException past every caller that catches InvalidDataException: the CLI commands, the REPL entry and the desktop app's LoadPlan. Wrap the read the same way the parse is wrapped, so all four sites print one line naming the file. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018MrD9SGvLFx4kJaeGZyXG9
|
Reviewed the config loader, suppression applier, Web csproj link, CLI/REPL/app error handling, and the count/list changes. I found nothing that needs changing.
One small gap: the statement-grid and root-node-badge count changes in the desktop app have no tests, as the PR description says. That's acceptable given the one-line changes. |
This was referenced Oct 14, 2026
Merged
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.
Claude posting for Erik Darling.
Closes #651.
Adds per-finding suppressions to
.planview.json, keyed on the statement query hash, as designed and approved on 2026-10-14. This body describes the final state, including the review follow-up.What changed
Config and loader (
AnalyzerConfig.cs,ConfigLoader.cs)suppressionslist withSuppressionEntry(query_hash,rule,type,operator,reason). Hash normalization (trim, strip0x/0X, OrdinalIgnoreCase) lives in one helper used by both sides.Suppression 2 in C:\work\team.json has no "reason".,... needs "rule" or "type".,... has an empty "query_hash".The path is the realconfigPath, so--config team.jsonand the current-directory or home copy are told apart.InvalidDataException(bad suppression, or malformed JSON, which used to escape as a rawJsonException).Where a loader failure is handled
analyzeandquery-store: one line to stderr, exit code 1.planview repl:Program.cscatchesFileNotFoundExceptionandInvalidDataExceptionaroundPlanReplApp.Create()(thePlanOperationsconstructor reads the config), prints the message to stderr, returns 1. Checked by hand with a bad.planview.jsonin the current directory: one line, exit 1.LoadPlancatchesInvalidDataExceptionandFileNotFoundException, analyzes withAnalyzerConfig.Default, and showsConfig ignored: <message>in a new toolbar notice (ConfigNoticeText, warning colour, full message as tooltip). The plan still opens and there is no dialog. All five callers ofLoadPlanare covered. The notice hides again after a good load or when the plan is cleared. The app had no existing notice area (CostTextis cleared on every statement render), so I added one small text block beside it.Core
PlanWarninggainsIsSuppressedandSuppressionReason.SuppressionApplier(new) is the last step ofPlanAnalysisPipeline.AnalyzeParsed, after the scorer. It walksPlanStatements.EnumerateAlllikeApplySeverityOverrides. It flags and never removes, and never touches severity.OperatorLabel.Format(new, internal) is the one home for the operator label, used byResultMapperand the suppression pass. It is linked intoPlanViewer.Web.csprojbeside the other Services files (the Web project links Core files one by one; this fixes the CS0103 CI failure).SuppressionApplier.csneeds no link because Web does not compilePlanAnalysisPipeline.cs.JSON and MCP
WarningResultgainsrule(omitted when null, set at both mapper sites),suppressed(always written) andsuppression_reason(omitted when null).AnalysisSummarygainssuppressed_warnings;total_warnings,critical_warningsandwarning_typesexclude suppressed findings. Suppressed warnings stay instatements[].warningsand the operator tree.PlanSummaryResult.suppressed_warnings;PlanWarningItem.rule,suppressed,suppression_reason. The MCP host itself still passesAnalyzerConfig.Default, so MCP sessions never apply a config file (out of scope, and the README now says so).Counts versus lists. Rule: a count excludes suppressed warnings; a list keeps them with the tag. Counts now excluding suppressed: the JSON summary, the desktop app's MCP session counts, the HTML severity badges, the statement grid Critical and Warnings columns (
PlanViewerControl.Statements.cs), the "N warnings" badge on the root node (PlanViewerControl.Rendering.cs), the advice statement chips for critical, warning and spill counts (AdviceContentBuilder.WaitStats.cs) and the compare "Warnings" metric (ComparisonFormatter.cs). Lists that keep suppressed warnings with the tag: text, HTML, advice cards, properties panel, and the MCP warning list.Renderers:
[suppressed: <reason>]in the text output (all three sites, plus aSuppressed warnings: Nsummary line when above zero), asuppressed: <reason>badge in HTML (reason encoded), and the tag in the advice cards and the three properties-panel sites.Docs: README
### Configuration file (.planview.json)section (search order, which tools read the file,rules.disabled,severity_overridesvalues, every suppression field, matching rules, one example, where to findquery_hash, the error format with a real path).--config <path>added to theplanview analyzeusage block; the Analysis Rules one-liner links to the section.llms.txtlists no CLI options, so it is unchanged. The section passes the plain-English checker.Judgment calls
HistoricalCliContractTestspins the SHA-256 of compact CLI output. The new keys (suppressed,rule,suppressed_warnings) change it, as they must. I rolled the constant and documented it. Stripping exactly those keys from the new output gives the old hash back. The test now writes a temp{}config and passes--config, so a developer's own~/.planview.jsoncannot change the bytes. The hash did not change by that.get_plan_warnings(MCP) lists and counts every warning, each flaggedsuppressed.operator, anullentry, and a hash that is only0xare rejected. When several entries match one warning, the first in file order sets the reason. Statements with noQueryHashare never matched.Tests
New:
SuppressionTests,ConfigLoaderTests, a CLI test inHistoricalCliContractTests(analyze --configwith a missingreason: one stderr line naming the file, empty stdout, exit 1), a text test for the grouped and single operator-warning paths,TheStatementChipsDoNotCountSuppressedWarnings(advice chips) andTheWarningsMetricDoesNotCountSuppressedWarnings(compare). The xUnit1051 warnings inSuppressionTestsare fixed. No existing App test drives the statement grid or the root-node badge, so I added no App test for those two counts; the one-line change at each site is covered by review only.Results:
dotnet build PlanViewer.sln: 0 errors, includingPlanViewer.Web.tests/PlanViewer.Core.Tests: 1498 total, 1467 passed, 0 failed, 31 skipped.Out of scope, not built: unused-suppression reporting,
--show-suppressed, the MCP host passing a loaded config.Review round 2 follow-up
An unreadable
.planview.json(locked, or permission denied) threwIOExceptionorUnauthorizedAccessExceptionout ofConfigLoader.Load, past every caller that catchesInvalidDataException: the two CLI commands, the REPL entry and the desktop app'sLoadPlan. The loader now wraps the file read the same way it wraps the parse, so all four sites print one line naming the file.ConfigLoaderTestsgainsAnUnreadableFileFailsWithAMessageNotAStackTrace, which holds the file open withFileShare.Noneand asserts the one-line message.🤖 Generated with Claude Code
https://claude.ai/code/session_018MrD9SGvLFx4kJaeGZyXG9