Skip to content

feat(lineage): refresh button and data age on the Compliance Map - #358

Merged
gusfcarvalho merged 5 commits into
poc/menu-reorgfrom
perf/lineage-refresh
Oct 16, 2026
Merged

gusfcarvalho merged 5 commits into
poc/menu-reorgfrom
perf/lineage-refresh

Conversation

@ccf-lisa

@ccf-lisa ccf-lisa Bot commented Oct 16, 2026

Copy link
Copy Markdown
Contributor

Stacked on #357 (poc/menu-reorg). Depends on compliance-framework/api#513.

Why

Heavy Compliance Map users click a lot, and every click used to rebuild the whole lineage graph on the API: 320–400 ms per expand or drill-down. api#513 shares built engines across requests behind a TTL, so warm clicks take about 1 ms. Cached data can now be a few minutes old, so the UI has to say how old it is and let people rebuild it.

What changed

  • useLineage (src/composables/useLineage/index.ts)
    • generatedAt: the oldest X-Lineage-Generated-At seen since the client cache was last cleared, so the label never claims fresher data than some node on screen. Missing or malformed headers are ignored.
    • refresh(): clears the client cache and sends refresh=true on the next roots fetch. Later requests reuse that rebuild.
    • takeUnscopedRefresh(): the drawer's per-SSP table always reads the unscoped engine. After a refresh in an SSP- or component-scoped view, the drawer's next /ssps request sends refresh=true so that engine is rebuilt too. An unscoped refresh already rebuilt it, so the flag is cleared.
  • LineageScopeBar gets a Refresh button and an "Updated X ago" label (useTimeAgo; the full time is in the tooltip).
  • Tree and graph views pass generatedAt and the busy state, and handle refresh. The tree keeps its expanded nodes and selection, and the graph replays its drill-down path. The button stays disabled until the whole reload is done, roots plus replay, so a second click can't start an overlapping replay.

Without api#513 it degrades gracefully: no header means no label, and the API ignores the unknown refresh param, so Refresh just refetches.

Testing

  • make reviewable (formatcheck, typecheck, lint, unit tests): 1,832 tests pass.
  • New and extended specs:
    • useLineage.spec: refresh param on the next roots fetch only, oldest generated-at, bad headers, scoped vs unscoped follow-up.
    • LineageScopeBar.spec: emit, disabled state, label.
    • LineageTreeView.spec: refresh keeps the expanded nodes.
    • LineageGraphView.spec: refresh replays the path and stays busy until the replay is done.
    • LineageNodeDrawer.spec: a refreshed node object reloads the drawer's panels.

Open questions for reviewers

None.

🤖 Generated with Claude Code

@coderabbitai

coderabbitai Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Important

Review skipped

Bot user detected.

To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 3965a6be-2048-42f1-8a40-91e49360d8e2

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@gusfcarvalho
gusfcarvalho added this pull request to stack #359 October 16, 2026 15:14

@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.

ccf-review: REQUEST_CHANGES

3 Must-fix, 1 Nit.

Stack (gh stack 359): #357 → #358

Outside the diff

  • [Must-fix] src/composables/useLineage/index.ts:186 A request in flight when Refresh is clicked writes pre-refresh data back into the cleared cache: refresh() clears the module cache, but a getChildren (or getRoots) call already awaiting the API still runs cache.set(ck, nodes) and noteGeneratedAt(...) when it resolves. Because Refresh doesn't change the scope, the cache key is the same, so the stale result lands in the fresh cache. Reachable: expand a large node, then click Refresh before its children arrive. The node-expand fetch doesn't disable the button. The old response then fills the cache under the same key. When the view replays its expanded nodes after the refresh, it gets those pre-refresh children from the cache, and the label moves back to the old time. Before this PR the only cache reset was a scope change, and the key includes the scope, so a late response could never land in the new cache. (2af4f09a72f0)

Comment thread src/composables/useLineage/index.ts Resolved
@ccf-lisa

ccf-lisa Bot commented Oct 16, 2026

Copy link
Copy Markdown
Contributor Author

Review body item (Outside the diff, Must-fix, src/composables/useLineage/index.ts): fixed in 3676dd6. clearCache() now bumps a module-level generation counter; getRoots/getChildren capture it before the API request and, if it changed by the time the response (or the fixture fallback after a failure) arrives, return the nodes without cache.set, noteGeneratedAt or touching the pending refresh flags. New spec in useLineage.spec: a deferred fetchChildren, then refresh(), then the old fetch resolves; generatedAt stays null and a new fetchChildren for that key calls the API again (fails without the fix).

@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.

ccf-review: APPROVE

no findings.

Stack (gh stack 359): #357 → #358

@ccf-lisa

ccf-lisa Bot commented Oct 16, 2026

Copy link
Copy Markdown
Contributor Author

PR approved. Marking ready for e2e.

@gusfcarvalho gusfcarvalho added the preview Publish a :pr-<n> preview build label Oct 16, 2026
gusfcarvalho and others added 5 commits October 16, 2026 14:01
The API now shares built Compliance Map data across requests for a few
minutes (CCF_LINEAGE_CACHE_TTL), so expanding nodes and opening the drawer
no longer rebuild everything. This adds the controls for that cache:
- The scope bar shows "Updated <time> ago", from the X-Lineage-Generated-At
  header (the oldest response since the cache was cleared).
- A Refresh button clears the client cache and sends refresh=true on the next
  roots fetch. The tree keeps its expanded nodes and the graph replays its
  drill-down path.
- After a refresh in an SSP- or component-scoped view, the drawer's per-SSP
  table rebuilds the unscoped data it reads, so it is not left stale.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Self-review pass 1. The scope bar's refreshing state followed loading, which
clears once the roots land, so a second click during the graph's path replay
started a second replay pushing onto the same columns. Both views now hold a
refreshing flag across the whole reload. Adds a graph refresh test and a drawer
test pinning that a refreshed node object reloads the drawer's panels.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A getRoots/getChildren request in flight when refresh() cleared the cache
used to store its pre-refresh nodes under the same key and move the data
age back. clearCache() now bumps a generation; a response from an older
generation is returned to its caller but not cached, and does not touch
the data age or the pending refresh flags.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
takeUnscopedRefresh() cleared the flag before the /ssps request, so a
failed request lost the pending rebuild and the next open read stale
unscoped data. The drawer now puts it back with restoreUnscopedRefresh()
when its request fails.

Specs cover the one-time refresh=true after a view refresh and the
retry after a failed request.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@gusfcarvalho
gusfcarvalho removed this pull request from stack #359 October 16, 2026 17:57
@gusfcarvalho
gusfcarvalho merged commit 9832e42 into poc/menu-reorg Oct 16, 2026
20 of 21 checks passed
@gusfcarvalho
gusfcarvalho deleted the perf/lineage-refresh branch October 16, 2026 17:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

preview Publish a :pr-<n> preview build ready-for-e2e

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant