Skip to content

Surface inherited (cross-SSP leverage) controls in compliance + lineage - #285

Open
ccf-lisa[bot] wants to merge 2 commits into
mainfrom
gc-feat-compliance-for-exports
Open

Surface inherited (cross-SSP leverage) controls in compliance + lineage#285
ccf-lisa[bot] wants to merge 2 commits into
mainfrom
gc-feat-compliance-for-exports

Conversation

@ccf-lisa

@ccf-lisa ccf-lisa Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

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 inherited status instead of rendering unknown / 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

  • New inherited status 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-backed satisfied; the API folds it into compliancePercent/assessedPercent.
  • "Inherited" badge on compliance rows with a tooltip (upstream SSP, offering, outstanding responsibilities, drifted variant), linking to the existing Inherited Capabilities tab when SSP-scoped.
  • Lineage: non-credit "Inherited · drifted/revoked/superseded" badge on control rows + per-SSP drawer table, and a new "Inherited capabilities" section in the node drawer (per downstream SSP: upstream source, satisfaction, and per-responsibility posture) fed by GET /api/lineage/nodes/:key/leverage.

Contracts A/B/C from the plan are mirrored exactly in src/types/compliance.ts and src/composables/useLineage/types.ts.

Design notes (also in git notes / lisa-design)

  • New inherited count 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 existing implementedControls? convention). The wire contract is unchanged — the API always emits it; every read is ?? 0 / optional-chained.
  • The drawer's /leverage loader fences the UUID-keyed responsibilityPosture map via camelcaseStopPaths: ['data.links.responsibilityPosture']. Verified empirically that this preserves the UUID keys through both array hops (data[] → links[]) while still camelCasing siblings.
  • Drawer reuses the monotonic request-token race guard from loadSSPRows, and the existing responsibility-posture + volt Badge rendering helpers.

Testing

npm run type-check, npm run lint, npm run test:unit all 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), lineage nodeMeta (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, no sspId) shows an Inherited tile pinned at 0 — accepted per the plan; can optionally be hidden when 0 and unscoped.

🤖 Generated with Claude Code

@ccf-lisa

ccf-lisa Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

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 gusfcarvalho left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.ts mocks 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 + the useLeveragedControls ['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 responsibilityRows re-implements the union-dedupe-by-responsibilityUuid logic already in inheritedResponsibilityRows; defensible given the differing types (LineageLeverageLink vs LeveragedControl).

Nice touches: faithful ?? 0 / optional-chaining discipline, the out-of-order race-guard test, and clean reuse of the posture/badge helpers.

Comment thread src/components/ComplianceProgressPanel.vue
Comment thread src/utils/compliance.ts
…ope; render inherited segment in dashboard posture bar

@gusfcarvalho gusfcarvalho left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM — approving. ✅

Both findings from the prior COMMENT round are resolved in d9aafc8a and verified:

  • Zero "Inherited" tile in profile scopeshowInheritedTile now gates the tile and switches the grid md: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-unique border-purple-200.
  • Dashboard posture bar rendered only 3 of 4 buckets — the purple inherited segment 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.

@ccf-lisa

ccf-lisa Bot commented Jul 22, 2026

Copy link
Copy Markdown
Contributor Author

PR approved. Marking ready for e2e.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant