Explorer exception draft controls

Source contract verified against d5365a8db07951e3dfd1c34b16a9cb38c0a677e8 (CCP-077, 2026-10-15); the permission check and the create request were corrected on 2026-10-19 (CCP-077 scope extension, see Create request). Focused and adjacent source cases pass; production build/runtime guards and actual served-browser acceptance are recorded separately in the task receipt.

Supported controls

Graph Explorer at /security/sbom/graph retains graph reads, selection, filters, owner-denied/unavailable/empty states and inline exception drafting. Create Exception is offered only when the signed-in session carries policy:author, the scope the Policy Engine enforces on the exception create route the draft calls. Its direct draft handler applies the same check. Read-only users retain graph exploration and do not enter the draft form through that control. exceptions:write grants nothing here: no create route enforces it, and Console sessions do not carry it.

The inline form retains the selected vulnerability, component and asset context, validation, Create Draft owner request, visible request failures and Cancel. Cancel closes the form and returns to the same selected exploration context. No new permission, owner operation or exception workflow is introduced.

Create request

Create Draft and the Exception Dashboard wizard both send the Policy Engine create contract through the Console exception client, which checks policy:author before sending:

FieldValue
typeFrom the scope: vulnerability when a vulnerability is named, else policy for a policy rule, else component for a package URL or artifact digest
scopevulnerabilityId, purlPattern, artifactDigest, policyRuleId (one value each, combined with AND) and environments
ownerIdThe signed-in user
reasonCodeFrom the justification template: Risk Accepted accepted_risk, Compensating Control compensating_control, False Positive false_positive, Scheduled Fix scheduled_fix; any other template, or none, other
rationaleThe justification, at least 50 characters after trimming (both forms require the same minimum)
expiresAtThe end of the chosen duration
metadataConsole fields the owner has no field for: console.name, console.severity, console.type, console.justificationTemplate, console.label.<tag>

The client refuses a draft the owner cannot hold, before any request, and the inline form or the Exception Dashboard error state shows the reason: more than one vulnerability, component, artifact or policy rule; an asset ID or image reference that is not a sha256: digest; a package name that is not a package URL (pkg:); a license; a scope with none of the four constraints; a shorter justification; no expiry; no signed-in user. It never widens or truncates the scope to fit. A Graph node whose related nodes add a second vulnerability or component is therefore refused; create one exception per pair instead. The wizard does not carry its template choice into the draft, so its reason code is other.

Full Wizard is removed from the shared inline form and from both Explorer callers. The existing Exception Dashboard (/ops/policy/vex/exceptions, the same route Graph Explorer links to for a node the Graph service flags as excepted) opens its wizard locally. It exposes no supported initial-context route/query contract that accepts this Explorer context; the removed action therefore cannot promise a context-preserving navigation. Use the supported inline controls or cancel back to exploration.

Source and served-route distinction

The separate VulnerabilityExplorerComponent currently has no component mount in the application route inventory. Its old list entry redirects to Vulnerability Database. The dormant caller’s placeholder handler/binding is removed in source and its real template is rendered in focused specs. This does not create or claim a served Vulnerability Explorer route. The genuine Vulnerability Database and accepted lineage subject/pair/tenant/Back routes remain unchanged.

Existing source limits

The shared form’s Impact Simulation is local arithmetic, not an owner-evaluated impact result. Its existing post-create detail navigation still targets a root exception URL absent from the current route inventory. Vulnerability Explorer’s separate view-details handler remains toast-only. These defects are recorded in sprint031 and exceed CCP-077’s shared-output/full-wizard repair; they are not presented as verified workflows. The independent owner dashboard has its own route/scope requirements and does not receive a new Explorer handoff here.

Re-verify the source behavior with the Graph Explorer, Vulnerability Explorer and shared inline-draft component specs through vitest.codex.config.ts, adjacent route/access/client specs, app/spec typechecks and the production Console build. Unit/source checks do not prove a deployed bundle or real served-browser behavior.

Served state (verified 2026-10-19)

The Console served since 2026-10-19 (package source 3a83706af52cab1f80adb345bb8107c591cb3bca, which contains the 2026-10-19 correction) carries this contract. No served JavaScript file contains the Full Wizard control or its toast. The Graph Explorer chunk checks POLICY_AUTHOR for both the rendered control and the direct handler, and has no exceptions:write check. Re-verify by comparing the served chunk for /security/sbom/graph with the mounted Console manifest and searching it for Full Wizard, POLICY_AUTHOR and exceptions:write.

On that estate the Graph projection for tenant default holds two nodes (an asset and a component). An administrator session that carries policy:author selected the asset node, opened Create Exception and saw the inline draft name the selected node and its asset scope. Cancel closed the draft and kept the same node selected. No Full Wizard control appeared, and no exception request was sent (Create Draft was not submitted). The missing-permission case has no served persona; the component specs above cover it.