Stop repeating evidence-role qualifiers on the default view - #80
Merged
Conversation
Every human-facing service and provider label carried the record role that established it, inline and in full prose, once per item and often several times per line: "Slack (public TXT account indicator)", "Okta (CNAME endpoint binding)", "Microsoft 365 (MX delivery path)". The answer to "what do they run" arrived buried under the answer to "how do we know", which the operator had not asked for yet. The default panel, --plain, --md, and the --chain tree now render those labels bare and close the section with one dim line naming the flag that restores the detail. --explain, --verbose, and --full are unchanged byte for byte, as are the --json record (including the stable provider field), --csv, and every MCP payload. Compaction never upgrades a claim. A match whose role could not be established is omitted from the default view and counted in that line rather than rendered as a bare vendor name; the provider row re-hedges as "(no supporting record)" instead of dropping, since it is the panel's only answer to who handles the mail; and the two qualifiers that bound the claim rather than name a record type survive in shortened form as "(likely downstream)" and "(downstream unobserved)". ADR-0012 records the claim-discipline invariant change this required, and the renderer obligations on runtime.service-label.v1 and runtime.email-topology.v1 are restated to match. Two defects surfaced while wiring this up and are fixed here: the subdomain summary was disappearing along with the compacted apex rows, and compaction was shrinking the category count that decides whether the passive-DNS sparseness note fires, so that count is now taken before compaction. Insight curation moves to formatter/insight_curation.py. It is editorial policy over insight strings rather than rendering, and the move ratchets the panel module's file-size ceiling down from 1719 to 1615 lines.
There was a problem hiding this comment.
Pull request overview
This PR updates recon’s default human-facing outputs to stop repeating evidence-role qualifiers inline on every provider/service label, while keeping evidence/detail surfaces (--explain / --verbose / --full) and machine surfaces (JSON/CSV) consistent with the existing claim-discipline model. It also extracts insight-curation policy into a dedicated module and refreshes goldens/docs to reflect the new default rendering.
Changes:
- Compact evidence-role qualifiers out of default panel/plain/markdown/service label rendering, and omit unattributed matches from default views with a single pointer line to detail surfaces.
- Refactor insight curation out of
panel.pyintoformatter/insight_curation.py. - Update tests and golden renders to reflect the new default vs detailed rendering split (ADR-0012), plus documentation/ADR/changelog updates.
Reviewed changes
Copilot reviewed 36 out of 37 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/test_services_label_width.py | Adds retained evidence to fixtures so default-view alignment tests still render observable rows. |
| tests/test_service_disclosure.py | Updates assertions to match compacted default labels and retained-detail behavior. |
| tests/test_panel_assembly.py | Adjusts provider-line assertion to compare compacted panel output against structured provider identity. |
| tests/test_multi_cloud_rollup.py | Updates rollup expectations for omission of unattributed legacy matches in default view. |
| tests/test_merger_error_surfacing.py | Adds minimal retained evidence to partial fixtures so default view doesn’t omit the only surviving labels. |
| tests/test_insights.py | Updates fixtures to include retained evidence so Services rows still appear under ADR-0012. |
| tests/test_ingestion_sanitization.py | Switches markdown sanitization tests to the detailed markdown surface to keep hostile labels renderable. |
| tests/test_formatter.py | Adds explicit default vs detailed plain-output expectations, including the new evidence-roles pointer line. |
| tests/test_formatter_intel_extensions.py | Switches grouping-only markdown tests to the detailed markdown surface (fixtures lack evidence). |
| tests/test_collection_view.py | Updates panel provider-line expectations to reflect default compaction vs JSON stability. |
| tests/test_cache_cross_version_compatibility.py | Updates expectations for pre-lineage caches (default omits unattributed; detail retains role-unavailable labels). |
| tests/test_agentic_ux_compatibility.py | Updates helper to forward kwargs and adjusts expectations to default omission + detailed retention. |
| tests/golden_renders/panel_surface_full.txt | Updates golden to reflect default omission/pointer behavior when roles are unavailable. |
| tests/golden_renders/panel_surface_default.txt | Updates golden to reflect compacted labels and evidence-role pointer line. |
| tests/golden_renders/panel_sparse_default.txt | Updates sparse golden to show unattributed omission note. |
| tests/golden_renders/panel_dense_default.txt | Updates dense golden to show compacted provider/services and evidence-role pointer. |
| tests/golden_renders/markdown_sparse.txt | Updates markdown golden to emit omission note instead of role-unavailable bullets. |
| tests/golden_renders/markdown_rich.txt | Updates markdown golden to compact service bullets and add evidence-role pointer line. |
| tests/golden_renders/markdown_dense.txt | Updates markdown golden to compact service bullets and add evidence-role pointer line. |
| src/recon_tool/formatter/serialize.py | Adds detailed flag to plain output; compacts labels by default and emits a single evidence-role/unattributed note. |
| src/recon_tool/formatter/panel.py | Threads “detailed vs default” through key-facts/services rendering; compacts provider/services in default view and adds pointer notes. |
| src/recon_tool/formatter/markdown.py | Adds detailed flag; compacts labels by default and emits a single italic pointer/note line. |
| src/recon_tool/formatter/insight_curation.py | New module holding insight curation policy previously embedded in panel.py. |
| src/recon_tool/formatter/classify.py | Adds compaction helpers for service labels and provider lines (ADR-0012). |
| src/recon_tool/cli/lookup.py | Threads “detailed roles” into --plain/--md output paths based on display options. |
| scripts/check_file_size.py | Updates file-size ratchet baseline for formatter/panel.py. |
| ROADMAP.md | Documents the new default-vs-detail evidence-role visibility rule with ADR reference. |
| README.md | Regenerates demo output to reflect compacted default provider/services lines. |
| docs/stability.md | Documents evidence-role wording/visibility differences between default and detailed surfaces. |
| docs/limitations.md | Updates dual-provider wording to reflect compacted default view vs detailed/JSON behavior. |
| docs/default-claim-audit.json | Updates renderer obligations/producer paths for the new compaction behavior. |
| docs/assets/terminal-demo.svg | Updates terminal demo asset to match the new default output rendering. |
| docs/adr/README.md | Adds ADR-0012 to the ADR index. |
| docs/adr/0012-default-view-evidence-role-visibility.md | New ADR defining default vs detailed evidence-role visibility and omission rules. |
| CHANGELOG.md | Adds release notes describing the default-view compaction and the insight-curation module split. |
| agents/claude-code/skills/recon/SKILL.md | Updates skill documentation snippet to match compacted default provider line. |
| AGENTS.md | Updates agent guidance snippet to match compacted default provider line. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Comment on lines
777
to
778
| if not info.services: | ||
| return None, 0 |
Comment on lines
+273
to
+277
| # The MX role is established, so the label is compacted rather than dropped | ||
| # and no unattributed-match note appears (ADR-0012). | ||
| assert "Email Symantec/Broadcom" in collapsed | ||
| assert "role unavailable" not in collapsed | ||
| assert "unattributed" not in collapsed |
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.
What
Every human-facing service and provider label carried the record role that established it, inline and in full prose, once per item and often several times per line. The default view now renders those labels bare:
Scope
--plain,--md,--chaintree--explain,--verbose,--fullpanel_dense_explain/full/verbosegoldens do not move)--json,--csv, MCPprovideris a stable JSON field and keeps the full proseCompaction never upgrades a claim
(no supporting record)instead of dropping — it is the panel's only answer to who handles the mail.(likely downstream)and(downstream unobserved).Consequence, recorded in the ADR: the default view is no longer a complete enumeration of catalog matches. Consumers counting rows must use the JSON record.
Governance
ADR-0012 records the claim-discipline invariant change this required. The renderer obligations on
runtime.service-label.v1andruntime.email-topology.v1are restated to match, andROADMAP.md/docs/stability.md/docs/limitations.mdupdated.Two defects fixed along the way
Both surfaced from the gate, not from the diff:
Also
Insight curation moves to
formatter/insight_curation.py— editorial policy over insight strings rather than rendering. The file-size ratchet demanded a split rather than a raised cap;panel.pyratchets 1719 → 1615 lines.Verification
uv run python scripts/check.py— all 27 stages pass, coverage 91.16%. README terminal demo regenerated.