Commit 46d7e83
fix(treatment-plan): cap linked-work lists and treatment plan body height
* fix(treatment-plan): cap linked-work lists and treatment plan body height
Long Tasks/Controls lists or AI-generated treatment plans were stretching
the right (Linked Work) and middle (Treatment plan) columns past the
Strategy column, leaving the row visually unbalanced and forcing the
whole page to scroll just to see the bottom controls.
- LinkedWork: each list (Tasks, Controls) now caps at max-h-80 with
internal overflow-y-auto. The progress bar / counts stay pinned.
- DescriptionEditor: markdown preview and the auto-growing textarea
share a TEXTAREA_MAX_PX (480) cap. Past the cap, internal scroll
takes over instead of pushing the row down.
Net effect: the three columns stay roughly aligned regardless of how
many tasks/controls are linked or how long the AI's plan runs.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(treatment-plan): paginate linked tasks and controls (4 per page) + seed fix
LinkedWork
- Each list (Tasks, Controls) now paginates at 4 items per page with
prev/next + "X–Y of N" controls. Replaces the prior height cap +
internal scroll. Long linked sets stay visually compact and the
Linked Work column no longer pushes past the Strategy / Treatment
plan columns regardless of list size.
- Pagination state resets defensively when the underlying list
shrinks past the current page (e.g. after an unlink).
Seed fix (FrameworkEditorControlTemplate.json)
- `documentTypes: null` → `documentTypes: []` for all 204 rows. The
schema requires a non-nullable `EvidenceFormType[]` (default `[]`)
but the export was writing nulls, which Prisma rejects on upsert.
- Mapped enum values from kebab-case (`"infrastructure-inventory"`)
to the TS-side snake_case (`"infrastructure_inventory"`). Prisma's
client expects the enum identifier, not the `@map`'d DB string.
Together these unblock `bun run db:seed` from a clean DB.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(seed): refresh framework editor templates + finding templates
Updates the seed JSON exports to the latest snapshot. Touches:
- FindingTemplate: regenerated with current cuid-style ids and the
current title/content set the audit team is using.
- FrameworkEditorFramework / PolicyTemplate / Requirement /
TaskTemplate: refreshed primitives to match the live framework
editor state.
- Three Control↔(Task|Policy|Requirement) join files: refreshed
to match the new template ids.
bun.lock: dedup of @jridgewell/trace-mapping pulled in by `bun install`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(treatment-plan): hide linked work for non-mitigate + drop Avoid as a new selection
Reverts two changes I introduced for Cubic findings #36 / #37 — they
were technically correct readings of "ENG-221 keeps controls/tasks
visible" but the user-intent was the opposite: Linked Work and the
Avoid strategy are Mitigate-shaped concerns and shouldn't surface for
treatments that aren't operational reductions.
- TreatmentPlanTab: `showLinkedWorkColumn` is gated on `isMitigate &&
hasLinkedWork` again. Accept ("live with the risk"), Transfer
(insurance / contractual instruments), and Avoid (discontinue the
activity) are not control-driven mitigations, so the column adds
noise and a misread when shown.
- StrategyPicker: Avoid is back to legacy-only — never offered as a
new selection, but rendered for risks that are already set to
Avoid so existing state isn't dropped silently.
- ScoreExplainer: dropped the Avoid bullet and the Avoid mention in
the coverage-gate paragraph, matching what the picker now offers.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(treatment-plan): url tabs, regen refresh, scroll caps, 4-per-page
UX fixes layered into a single commit so they ship together.
- RiskPageClient + VendorDetailTabs: active tab is now URL-backed via
useQueryState('tab'), and only the active panel is mounted. This
bookmarks the current tab in `?tab=...` (refresh keeps the view)
and eliminates the visible "both panels stacked" flash on switch
(base-ui keeps the outgoing panel at full opacity for the duration
of the incoming `fade-in-0 duration-200` animation).
- TreatmentPlanTab: drop the AutoMitigationPlaceholder ("AI is
preparing your treatment plan…"). Its heuristic stayed true
forever whenever the AI mitigation never wrote a description, so
users sat on an indefinite spinner. Falling through to
DescriptionEditor gives them the explicit "Generate treatment
plan" button.
- DescriptionEditor: regen-completion now bypasses the in-edit
draft-resync guard. When the user clicks "Regenerate with AI"
while in edit mode, they're explicitly opting into an overwrite,
but the existing guard kept the textarea showing the old draft
until refresh. Tracking regenRun's prev state and forcing
preview + setDraft(value) on transition fixes it.
- ScoreExplainer: cap the popover body at max-h-[70vh] with
overflow-y-auto. The narrative + formulas + references could
exceed the viewport on shorter screens with no way to reach the
bottom.
- AutoLinkSuggestions.sections: PAGE_SIZE 10 → 4 to match the new
LinkedWork pagination. Selection UI now feels consistent with
the post-apply view.
- LinkedWork: explicit list-none + pl-0 on the Tasks/Controls
`<ul>` to defeat an inherited list-style coming from somewhere
(random bullet + ~40px left padding on each row).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(treatment-plan): drop orphan dot + checkbox-spacer indent on control rows
Two visual bugs in the Controls list of AutoLinkSuggestions:
- When `c.code` is empty (some frameworks ship controls without a
code, e.g. "PCI"), the row rendered "· PCI" because the prefix
format was unconditional `${c.code} · ${c.name}`. Now we only
prepend the "code · " segment when a code exists.
- Each row had a 16x16 invisible spacer <span> meant to align the
control text with the task text below the task-row checkbox. In
practice it just pushed controls ~24px to the right (spacer +
gap), making them look indented relative to the section header
and the Tasks rows above. Dropped the spacer so controls now
align to the left edge of the column.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(onboarding): calibrate vendor inherent-risk prompt so well-known SaaS isn't 10/10
The previous system prompt for the AI vendor extraction had zero
calibration guidance — just "return inherent_probability and
inherent_impact". Without a rubric, gpt-4.1-mini defaulted every
vendor to (very_likely × severe) = 25 → 10/10 CRITICAL. GitHub
landed at 10/10 even though the same vendor card shows it carries
SOC 2 + ISO 27001 + ISO 42001 + four other certifications.
Adds explicit calibration:
- Per-bucket definitions for inherent_probability (very_unlikely
through very_likely), each anchored to concrete vendor
archetypes (hyperscaler / SaaS-with-SOC-2 / no-attestation / etc).
- Per-bucket definitions for inherent_impact, anchored to data
classes (none / metadata / PII / auth-or-source-or-payments /
production-infrastructure).
- Explicit DEFAULT for residual: "leave residual = inherent unless
the user's answers describe their OWN compensating controls."
The vendor's certifications already feed into inherent — they
shouldn't be double-counted as residual reductions.
- Sanity-check sentence naming the well-known vendors that should
land at (unlikely, moderate) ≈ 3/10, not 10/10.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(onboarding): score vendor inherent risk from user signals, not name lookups
Drop the hardcoded list of "well-known SaaS" vendor names from the
inherent-risk calibration prompt. Maintaining a list inside an LLM
prompt is brittle (every new well-known vendor is a prompt edit) and
asks the LLM to reason from its prior knowledge rather than from the
data we actually have.
The new prompt makes two things explicit:
1. The model is scoring from the USER'S answers only — it does NOT
have access to the vendor's public posture (SOC 2, ISO 27001,
incident history). A separate research step (research-vendor →
GlobalVendors) fills that in later, and a follow-up will use the
researched data to refine the per-org Vendor row's score.
2. Default to MIDDLE (possible × moderate ≈ 5/10) when no signal
exists. Only deviate when the user's answers contain explicit
signals — listed by category (lowers probability / raises
probability / lowers impact / raises impact). When the user
simply NAMES the vendor with no further context, return
(possible, moderate) and let the research step refine.
This fixes the "GitHub got 10/10" pathology without coupling the
prompt to a vendor name registry.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Mariano <marfuen98@gmail.com>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>1 parent 3c96a1d commit 46d7e83
19 files changed
Lines changed: 12787 additions & 1663 deletions
File tree
- apps/app/src
- app/(app)/[orgId]
- risk/[riskId]/components
- vendors/[vendorId]/components
- components/risks/treatment-plan
- trigger/tasks/onboarding
- packages/db/prisma/seed
- primitives
- relations
Lines changed: 91 additions & 69 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
24 | 24 | | |
25 | 25 | | |
26 | 26 | | |
27 | | - | |
| 27 | + | |
28 | 28 | | |
29 | 29 | | |
30 | 30 | | |
| |||
71 | 71 | | |
72 | 72 | | |
73 | 73 | | |
74 | | - | |
75 | | - | |
| 74 | + | |
| 75 | + | |
| 76 | + | |
| 77 | + | |
| 78 | + | |
| 79 | + | |
| 80 | + | |
| 81 | + | |
| 82 | + | |
| 83 | + | |
76 | 84 | | |
77 | 85 | | |
78 | 86 | | |
| |||
307 | 315 | | |
308 | 316 | | |
309 | 317 | | |
310 | | - | |
| 318 | + | |
311 | 319 | | |
312 | 320 | | |
313 | 321 | | |
| |||
319 | 327 | | |
320 | 328 | | |
321 | 329 | | |
322 | | - | |
323 | | - | |
324 | | - | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
325 | 335 | | |
326 | | - | |
327 | | - | |
328 | | - | |
329 | | - | |
330 | | - | |
331 | | - | |
332 | | - | |
333 | | - | |
334 | | - | |
335 | | - | |
336 | | - | |
337 | | - | |
338 | | - | |
339 | | - | |
340 | | - | |
341 | | - | |
342 | | - | |
343 | | - | |
344 | | - | |
345 | | - | |
346 | | - | |
347 | | - | |
348 | | - | |
349 | | - | |
350 | | - | |
351 | | - | |
352 | | - | |
353 | | - | |
354 | | - | |
355 | | - | |
356 | | - | |
357 | | - | |
358 | | - | |
359 | | - | |
360 | | - | |
361 | | - | |
362 | | - | |
363 | | - | |
364 | | - | |
365 | | - | |
366 | | - | |
367 | | - | |
368 | | - | |
369 | | - | |
| 336 | + | |
| 337 | + | |
| 338 | + | |
| 339 | + | |
| 340 | + | |
| 341 | + | |
| 342 | + | |
| 343 | + | |
| 344 | + | |
| 345 | + | |
| 346 | + | |
| 347 | + | |
| 348 | + | |
| 349 | + | |
| 350 | + | |
| 351 | + | |
| 352 | + | |
| 353 | + | |
| 354 | + | |
| 355 | + | |
| 356 | + | |
| 357 | + | |
| 358 | + | |
| 359 | + | |
| 360 | + | |
| 361 | + | |
| 362 | + | |
| 363 | + | |
| 364 | + | |
| 365 | + | |
| 366 | + | |
| 367 | + | |
| 368 | + | |
| 369 | + | |
| 370 | + | |
| 371 | + | |
| 372 | + | |
| 373 | + | |
| 374 | + | |
| 375 | + | |
| 376 | + | |
| 377 | + | |
| 378 | + | |
| 379 | + | |
| 380 | + | |
| 381 | + | |
370 | 382 | | |
371 | | - | |
372 | | - | |
373 | | - | |
374 | | - | |
375 | | - | |
376 | | - | |
| 383 | + | |
| 384 | + | |
| 385 | + | |
| 386 | + | |
| 387 | + | |
| 388 | + | |
| 389 | + | |
| 390 | + | |
377 | 391 | | |
378 | | - | |
379 | | - | |
380 | | - | |
| 392 | + | |
| 393 | + | |
| 394 | + | |
| 395 | + | |
| 396 | + | |
381 | 397 | | |
382 | | - | |
383 | | - | |
384 | | - | |
| 398 | + | |
| 399 | + | |
| 400 | + | |
| 401 | + | |
| 402 | + | |
385 | 403 | | |
386 | | - | |
387 | | - | |
388 | | - | |
| 404 | + | |
| 405 | + | |
| 406 | + | |
| 407 | + | |
| 408 | + | |
389 | 409 | | |
390 | | - | |
391 | | - | |
392 | | - | |
| 410 | + | |
| 411 | + | |
| 412 | + | |
| 413 | + | |
| 414 | + | |
393 | 415 | | |
394 | 416 | | |
395 | 417 | | |
| |||
Lines changed: 43 additions & 20 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
36 | 36 | | |
37 | 37 | | |
38 | 38 | | |
| 39 | + | |
39 | 40 | | |
40 | 41 | | |
41 | 42 | | |
| |||
76 | 77 | | |
77 | 78 | | |
78 | 79 | | |
79 | | - | |
80 | 80 | | |
81 | 81 | | |
82 | 82 | | |
| |||
107 | 107 | | |
108 | 108 | | |
109 | 109 | | |
110 | | - | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
111 | 118 | | |
112 | 119 | | |
113 | 120 | | |
| |||
338 | 345 | | |
339 | 346 | | |
340 | 347 | | |
341 | | - | |
| 348 | + | |
342 | 349 | | |
343 | 350 | | |
344 | 351 | | |
| |||
472 | 479 | | |
473 | 480 | | |
474 | 481 | | |
475 | | - | |
| 482 | + | |
476 | 483 | | |
477 | 484 | | |
478 | 485 | | |
| |||
485 | 492 | | |
486 | 493 | | |
487 | 494 | | |
488 | | - | |
489 | | - | |
490 | | - | |
| 495 | + | |
| 496 | + | |
| 497 | + | |
| 498 | + | |
| 499 | + | |
491 | 500 | | |
| 501 | + | |
492 | 502 | | |
493 | 503 | | |
494 | 504 | | |
| |||
525 | 535 | | |
526 | 536 | | |
527 | 537 | | |
| 538 | + | |
528 | 539 | | |
| 540 | + | |
529 | 541 | | |
530 | 542 | | |
531 | 543 | | |
532 | 544 | | |
533 | 545 | | |
534 | 546 | | |
| 547 | + | |
535 | 548 | | |
| 549 | + | |
536 | 550 | | |
537 | 551 | | |
538 | 552 | | |
| |||
580 | 594 | | |
581 | 595 | | |
582 | 596 | | |
583 | | - | |
584 | | - | |
585 | | - | |
586 | | - | |
587 | | - | |
588 | | - | |
589 | | - | |
590 | | - | |
591 | | - | |
592 | | - | |
593 | | - | |
594 | | - | |
595 | | - | |
| 597 | + | |
| 598 | + | |
| 599 | + | |
| 600 | + | |
| 601 | + | |
| 602 | + | |
| 603 | + | |
| 604 | + | |
| 605 | + | |
| 606 | + | |
| 607 | + | |
| 608 | + | |
| 609 | + | |
| 610 | + | |
| 611 | + | |
| 612 | + | |
| 613 | + | |
| 614 | + | |
| 615 | + | |
| 616 | + | |
| 617 | + | |
596 | 618 | | |
597 | 619 | | |
598 | 620 | | |
| |||
616 | 638 | | |
617 | 639 | | |
618 | 640 | | |
| 641 | + | |
619 | 642 | | |
620 | 643 | | |
621 | 644 | | |
| |||
Lines changed: 9 additions & 5 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
10 | 10 | | |
11 | 11 | | |
12 | 12 | | |
13 | | - | |
| 13 | + | |
| 14 | + | |
| 15 | + | |
14 | 16 | | |
15 | 17 | | |
16 | 18 | | |
| |||
192 | 194 | | |
193 | 195 | | |
194 | 196 | | |
| 197 | + | |
| 198 | + | |
| 199 | + | |
| 200 | + | |
| 201 | + | |
195 | 202 | | |
196 | 203 | | |
197 | 204 | | |
| |||
200 | 207 | | |
201 | 208 | | |
202 | 209 | | |
203 | | - | |
204 | 210 | | |
205 | | - | |
206 | | - | |
207 | | - | |
| 211 | + | |
208 | 212 | | |
209 | 213 | | |
210 | 214 | | |
| |||
0 commit comments