Surface inherited (cross-SSP leverage) controls in compliance + lineage - #285
Surface inherited (cross-SSP leverage) controls in compliance + lineage#285ccf-lisa[bot] wants to merge 2 commits into
Conversation
|
Automated status: CI is green (test + type-check pass) and there are no open review threads. This PR is ready for human review whenever a maintainer has a moment. lisa will keep watching and address any review comments or CI failures automatically. |
gusfcarvalho
left a comment
There was a problem hiding this comment.
Non-blocking review — solid, contract-faithful, well-tested. Verified: CI green (test + type-check) at head; 80 affected unit tests pass locally; eslint clean on changed files; types mirror Contracts A/B/C exactly. Two low-severity consistency items inline; everything else below is optional.
Low — the node drawer's compliance rollup header omits inherited (LineageNodeDrawer.vue ~L345–357, unchanged code so noting here rather than inline). The structural header renders {satisfied} sat · {notSatisfied} not · {unknown} unknown / {total}. Now that LineageCompliance has an inherited bucket, when inherited > 0 these three no longer sum to totalControls — confusing on exactly the surface this PR makes inheritance first-class. LineageNodeRow's pill got a purple inherited count; this sibling header didn't. Suggest a purple {inherited} inh segment shown when > 0, mirroring the row pill.
Nits (no action required):
- The drawer's
camelcaseStopPaths: ['data.links.responsibilityPosture']fencing isn't covered by an integration test —LineageNodeDrawer.spec.tsmocks the whole axios instance, so it asserts the config value is passed but can't prove the real interceptor honors that nested-array path. Rests on the empirical verification in your notes + theuseLeveragedControls['data.responsibilityPosture']precedent. Fine, just flagging the coverage gap since a regression in the interceptor's path-matching wouldn't be caught here. - Minor duplication: the drawer's local
responsibilityRowsre-implements the union-dedupe-by-responsibilityUuidlogic already ininheritedResponsibilityRows; defensible given the differing types (LineageLeverageLinkvsLeveragedControl).
Nice touches: faithful ?? 0 / optional-chaining discipline, the out-of-order race-guard test, and clean reuse of the posture/badge helpers.
…ope; render inherited segment in dashboard posture bar
gusfcarvalho
left a comment
There was a problem hiding this comment.
LGTM — approving. ✅
Both findings from the prior COMMENT round are resolved in d9aafc8a and verified:
- Zero "Inherited" tile in profile scope —
showInheritedTilenow gates the tile and switches the gridmd:grid-cols-6 ↔ md:grid-cols-5; kept in SSP scope even at 0. New tests cover both scopes, and the pre-existing tile assertion was tightened to the tile-uniqueborder-purple-200. - Dashboard posture bar rendered only 3 of 4 buckets — the purple
inheritedsegment is now rendered (zero-width today, gap-free if repointed at SSP-scoped data), with the profile-scope assumption pinned in a comment.
Verified: CI green (test + type-check) at d9aafc8a; locally the panel specs pass and eslint is clean on the changed files. Implementation mirrors Contracts A/B/C faithfully with consistent defensive ?? 0 / optional-chaining.
One optional, non-blocking follow-up (fine for a separate change): the lineage node drawer's structural compliance header (LineageNodeDrawer.vue ~L345–357) still renders {sat} · {not} · {unknown} / {total} and omits inherited, so those three won't sum to the total when inherited > 0 — mirroring the purple count already added to the row pill would close the last consistency gap.
|
PR approved. Marking ready for e2e. |
What
Surfaces cross-SSP inherited (leveraged-authorization) controls in the two places that previously showed nothing about inheritance: the SSP compliance page and the lineage tree/graph + node drawer. An inherited control now reads as a first-class purple
inheritedstatus instead of renderingunknown/attention ⚠, and its upstream source + outstanding customer responsibilities are visible in context.This is the UI half; the API half is built in a parallel session against the same pinned JSON contract (
docs/inherited-compliance-lineage-plan.md).Changes
inheritedstatus bucket rendered purple across the compliance panel (summary tile, overall bar, group breakdown, per-control pill) and lineage (posture chip, posture-bar segment, compliance-pill count). Distinct from evidence-backedsatisfied; the API folds it intocompliancePercent/assessedPercent.GET /api/lineage/nodes/:key/leverage.Contracts A/B/C from the plan are mirrored exactly in
src/types/compliance.tsandsrc/composables/useLineage/types.ts.Design notes (also in
git notes/lisa-design)inheritedcount fields are modeled optional (inherited?: number) rather than the plan's literal required form:type-check(vue-tsc --build) includes the vitest tsconfig, ~14 files build these types without the field, and the plan's own defensive rule is?? 0/ tolerate absence (matching the file's existingimplementedControls?convention). The wire contract is unchanged — the API always emits it; every read is?? 0/ optional-chained./leverageloader fences the UUID-keyedresponsibilityPosturemap viacamelcaseStopPaths: ['data.links.responsibilityPosture']. Verified empirically that this preserves the UUID keys through both array hops (data[] → links[]) while still camelCasing siblings.loadSSPRows, and the existingresponsibility-posture+ voltBadgerendering helpers.Testing
npm run type-check,npm run lint,npm run test:unitall green (135 files / 1082 tests). New/extended specs cover the compliance helpers, panel (6 tiles, inherited pill, badge + tooltip, RouterLink vs plain span, drifted variant), lineagenodeMeta(inherited posture/badge/segments),LineageNodeRow(purple chip, drifted badge, pill count), and the drawer (fetch config + stop-paths, section render, empty state, fixtures skip, stale-response race guard).Known trade-off
ProfileComplianceView(profile scope, nosspId) shows an Inherited tile pinned at 0 — accepted per the plan; can optionally be hidden when 0 and unscoped.🤖 Generated with Claude Code