Surface the model's cut-set in finding detail; realign shadow would-act counts to the typed decision - #311
Merged
thejefflarson merged 1 commit intoAug 1, 2026
Conversation
8 tasks
…shadow would-act counts to the typed decision Finding detail (ADR-0034): the single opaque cut-signature string is replaced with a per-node cut-set list (fenced node key + fixed mechanism string + entry-vs-downstream role + advisory blast-radius note), sourced from the engine's per-pass IncidentDecision map rather than the deterministic containment_for fallback. Honest empty states: "attack" with no rows reads as "attack, no cut warranted"; "uncertain" never renders as a green all-clear; no decision yet reads as "awaiting". report.rs (JEF-143 aggregation): would-act classification now reads the typed Decision::Incident (assessment + cuts) in effect at each Breach timestamp, never the verdict prose match it used before — a Breach line with "exploitable" prose but no backing Incident line no longer inflates the count. "attack, no cut warranted" is split into its own AttackNoCutEntry class, distinct from would_act and left_alone. would_act_count() now counts distinct contained NODES (a decision can name the entry plus a downstream workload), not distinct entries. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01VtjoJttCvBY4dzCoE4f9vP
thejefflarson
force-pushed
the
thejefflarson/jef-674-surface-the-models-cut-set-in-finding-detail-realign-shadow
branch
from
August 1, 2026 10:13
1793549 to
9a15af6
Compare
thejefflarson
enabled auto-merge (squash)
August 1, 2026 10:13
Owner
Author
|
Integration note (architect): rebased onto main after #308/#309/#310 landed — clean rebase, no conflicts (git's 3-way merge absorbed the additive |
thejefflarson
deleted the
thejefflarson/jef-674-surface-the-models-cut-set-in-finding-detail-realign-shadow
branch
August 1, 2026 10:20
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.
Summary
Finding detail (ADR-0034):
CutBlock(engine/web/src/findings/detail.jsx) no longer renders the proposed cut as a single opaque signature string. It now renders the model's actual cut-set — one line per contained node: the fenced node key, a fixed mechanism string (ProposedAction::describe, never model/untrusted text), the entry-vs-downstream role, and an advisory blast-radius note (recomputed the same way the model's own menu line did, via a new sharedincident::cut_blast_notehelper). Honest empty states:attackwith an empty cut-set → explicit "attack, no cut warranted" (a valid ADR-0034 D1 decision, not an error).uncertain→ NEVER a green all-clear; a distinct caution line.report.rs(JEF-143 aggregation, JEF-674): would-act classification is now keyed on the TYPEDDecision::Incident(assessment+cuts) in effect at eachBreachline's timestamp, never on theBreachline's own verdict PROSE (the oldverdict_would_actstring-prefix match is deleted). ABreachline reading "exploitable" with no backingIncidentline (a pre-ADR-0034 journal) no longer inflates the would-act count — it replays display-only into the honestleft_alonetail.attack+ emptycontainis split into its ownAttackNoCutEntryclass, distinct from bothwould_actandleft_alone. The headlineReport::would_act_count()now counts DISTINCT contained nodes across the window (a decision can name the entry plus a downstream workload), not distinct entries.View-model shape (new prop)
FindingPropsgained a new, non-optionalcuts: CutSetPropsfield (the oldcut: Option<String>stays — it still drives the chain-diagram's cut-hop marker):Sourced from a new
Finding.incident: Option<IncidentSummary>field, populated inFinding::from_chainfrom the engine's per-passBTreeMap<String, IncidentDecision>(threaded throughFindings::publish_chains, called at bothprocess()call sites — the pre-adjudication publish passes the carried-forward prior decision map, the post-adjudication re-publish passes this pass's fresh one).Testing
engine/src/engine/state/report_tests.rs(new, split out to keepreport.rsunder the 1,000-line cap) — 6 new tests covering: would-act requires the typedAttack+ non-empty cuts (not prose); prose alone with noIncidentline never inflates the count (the JEF-674 bug fix, directly asserted); short-lived episode closing via a typedNoAttack;attack+ empty cuts as its own honest class;would_act_count()counting distinct nodes (not entries) across two entries with one shared node; the empty-journal case.cargo nextest run --workspace: 1122 passed, 2 skipped (pre-existing, unrelated).engine/web/test/cut-set.test.jsx(new) — 5 tests: per-node row rendering (fenced key/role/mechanism/blast-note); "attack, no cut warranted"; uncertain never renders green; awaiting state; XSS-safe node-key escaping.npm test: 116 passed (18 files).npm run build/typecheck/lint: clean.Checks
cargo fmt,cargo clippy --all-targets -- -D warnings: clean.npm run build && npm run typecheck && npm run lint && npm test(engine/web): clean./soundcheck:pr-review: no Critical/High findings (pure view/report data-shape change — Preact JSX text interpolation only, nodangerouslySetInnerHTML, no new endpoints/egress/secrets)./simplify: single-pass manual review (Agent tool unavailable in this context) — extracted a sharedincident::cut_blast_notehelper to remove a duplicatedMitigation-for-blast-radius construction betweenmenu.rsand the new finding-detail resolver, and removed a redundant would-act pre-check inreport.rs's episode loop (the inner loop's first iteration already does the identical lookup).Scope / decisions
Finding.cut: Option<String>(the deterministiccontainment_forfallback) unchanged — it still drives the proven-path chain diagram's cut-hop marker (HopProps::is_cut), which this ticket doesn't touch.coverage_gapclassification inreport.rsstill reads structuredEnrichmentCoverageoff theBreachtimeline (unchanged) —Decision::Incidentlines don't carry it, and theBreachtimeline stays the aggregation's cadence backbone for that reason. Only the would-act BOOLEAN and contained-node set now come from the typedIncidentstate in effect at eachBreachpoint.reasoninsideCutBlock— the existingVerdictBlockalready renders it verbatim viaVerdict::summary()("exploitable — {reason}"), unchanged by this PR.Report::attack_no_cutis new data the Action view (engine/src/engine/dashboard/view_model/action.rs, a sibling surface) doesn't yet render — it's available for a follow-up ticket to surface in the Action view's "proposed cuts" lifecycle story.Closes JEF-674