Skip to content

✨ feat(ui): display-honesty batch — update-status vocabulary, pin glyph, one-clock maturity panel#566

Merged
scttbnsn merged 18 commits into
dev/v1.6from
feat/v1.6-display-honesty
Jul 21, 2026
Merged

✨ feat(ui): display-honesty batch — update-status vocabulary, pin glyph, one-clock maturity panel#566
scttbnsn merged 18 commits into
dev/v1.6from
feat/v1.6-display-honesty

Conversation

@scttbnsn

@scttbnsn scttbnsn commented Jul 20, 2026

Copy link
Copy Markdown
Contributor

What

The display-honesty batch: renames and removes the update-status vocabulary that #498 and #556 reported as unreadable, based on a peer-tool UX research pass (WUD, Diun, Watchtower, Cup, Dockge, Arcane, Portainer, Komodo, Home Assistant, Renovate/Dependabot) and an owner-approved design.

  • "Digest" → "Digest update" in the Update column (Cup's web-UI string, Arcane's popover title). A digest-only change now reads as what it is: an update.
  • NEW/MATURE badges removed. The freshness fact moves into the update-kind badge's tooltip ("Detected 3 days ago"). Idle state gets no chrome; UpdateMaturityBadge.vue deleted.
  • "Pinned" leaves the Update column. It was an update status pretending to be a tag property (only rendered when a newer family version existed — the exact inconsistency reported in Alert: Pinned tag "1.5.2-rc.1" is compared by digest only. Set dd.tag.family=loose or add a dd.tag.include filter to allow semver version climbing. #498). Pinned-ness is now a persistent pin glyph on the Tag cell (existing pin icon-map entry, not the padlock — that's a live action affordance), with a tooltip carrying the dd.tag.family=loose remedy. Up-to-date pinned rows read "Current" like everything else.
  • Maturity panel keeps exactly one clock. The policy sentence names the clock the gate actually measures against ("Candidate published {date} — {n} more days until the {minDays}-day minimum", detection-date fallback), and the duplicated countdown collapses to "{countdown} · unlocks {date}".
  • Backend rider: resolveMaturityClock() in app/model/maturity-policy.ts (behavior-preserving refactor of getMaturityStartMs) now exposes clockSource/clockStartAt as additive optional fields on the maturity blocker's details, and both UI call sites (container-mapper.ts, useContainerPolicy.ts) consume the backend verdict instead of re-deriving maturity-blocked from updateDetectedAt alone — three drifting formulas become one.
  • Column picker honesty: breakpoint-auto-hidden columns stay checked but get a muted "{column} — hidden to fit" annotation, and the "+1" toolbar badge tooltip names the hidden columns instead of a bare count.
  • Unknown-kind badge: the silent unknown-update-kind fallback now renders a neutral "Unknown" badge.
  • Bouncer "Blocked" → "Security hold" (display string only; the maturity/hard-block wording is untouched).

i18n: net 12 keys added / 7 removed / 12 values changed, English catalogs only per convention, plus stale values for the repurposed keys dropped from all 16 non-English locales so they fall back to English until Crowdin retranslates (otherwise German would render the old badge text "NEU" as a tooltip).

Why

Direct follow-up to the rc.2 field feedback in #498 (Cesc1986) and the #556 display cluster. Design researched and approved before implementation; targets v1.6.1/rc.3 — nothing here touches the rc.2 soak.

Tests

TDD gates green in full: app 100/100/100/100 (12,244 tests), ui 100/100/100/100 (4,236 tests). New coverage for the clock resolution (source precedence + tie-breaks), blocker serialization, pin glyph, unknown-kind fallback, column-picker annotation branches, collapsed countdown, and both useContainerPolicy paths (backend-verdict preferred, legacy fallback).

Notes for review

  • dashboard-container-metrics.ts kept its freshUpdates KPI via a local isFreshUpdate() helper (same 7-day threshold) after the badge helper's deletion — dashboard behavior unchanged.
  • The owner does a rendered visual pass in the QA environment before this merges.

Changelog

  • ✨ Added backend-resolved maturity clocks with source/start metadata and defensive eligibility parsing.
  • ✨ Added shared update-kind labeling with localized Unknown fallback.
  • ✨ Added pin-gate policy detection and tagPinGated propagation from backend to UI.
  • ✨ Added auto-hidden column annotations and accurate picker badge/tooltips.
  • 🔧 Changed update terminology: “Digest update” and “Security hold”.
  • 🔧 Consolidated maturity countdowns into update-kind/status tooltips and insight notes.
  • 🔧 Moved pinned status to persistent pin glyphs in version/tag cells.
  • 🔧 Preserved dashboard fresh-update KPI behavior with an independent seven-day threshold.
  • 🔧 Improved tooltip fallback synchronization and registry search by dotted instance ID.
  • 🔧 Updated pluralized legacy-configuration banner translations.
  • 🗑️ Removed UpdateMaturityBadge, getUpdateMaturity, separate NEW/MATURE badges, and the dedicated pinned update-status state.
  • 🗑️ Removed stale localized blockedByBouncer, updateAge, maturity-badge, and pinned-summary values.
  • ⚠️ Updated the Container contract: removed updateMaturity; added updateMaturityTooltip, updatePolicyState, and tagPinGated.

Concerns

  • Verify backend updateEligibility payloads expose sufficiently reliable maturity clock metadata.
  • Confirm Crowdin retranslation coverage for removed and repurposed locale keys.
  • Audit downstream consumers for the removed getUpdateMaturity, updateMaturity, and pinned APIs.

@vercel

vercel Bot commented Jul 20, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
drydock-website Ready Ready Preview, Comment Jul 21, 2026 1:15pm
drydockdemo-website Ready Ready Preview, Comment Jul 21, 2026 1:15pm

@coderabbitai

coderabbitai Bot commented Jul 20, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The changes centralize maturity-clock resolution, expose backend maturity metadata, and add shared pin-gate governance. Frontend container types, mapping, status panels, badges, update-kind labels, responsive column pickers, registry search, tooltips, and dashboard freshness calculations were updated. Localizations now reflect revised maturity, pinned-tag, security-hold, update-kind, and pluralized banner text. Tests cover backend decisions, frontend rendering, localization, and edge cases.

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/v1.6-display-honesty

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
ui/src/locales/ja/containerComponents.json (2)

199-203: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Restore or replace blockedByBouncer before removing it. ContainersGroupedViews.vue still requests this key.

  • ui/src/locales/ja/containerComponents.json#L199-L203: preserve or replace the grouped-view bouncer label.
  • ui/src/locales/ko/containerComponents.json#L201-L201: preserve or replace the grouped-view bouncer label.
  • ui/src/locales/nl/containerComponents.json#L200-L201: preserve or replace the grouped-view bouncer label.
  • ui/src/locales/pl/containerComponents.json#L198-L203: preserve or replace the grouped-view bouncer label.
  • ui/src/locales/pt-BR/containerComponents.json#L198-L203: preserve or replace the grouped-view bouncer label.
  • ui/src/locales/ru/containerComponents.json#L199-L204: preserve or replace the grouped-view bouncer label.
  • ui/src/locales/tr/containerComponents.json#L198-L201: preserve or replace the grouped-view bouncer label.
  • ui/src/locales/uk/containerComponents.json#L198-L201: preserve or replace the grouped-view bouncer label.
  • ui/src/locales/vi/containerComponents.json#L198-L203: preserve or replace the grouped-view bouncer label.
  • ui/src/locales/zh-CN/containerComponents.json#L201-L201: preserve or replace the grouped-view bouncer label.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ui/src/locales/ja/containerComponents.json` around lines 199 - 203, Restore
or replace the blockedByBouncer translation key used by
ContainersGroupedViews.vue in ui/src/locales/ja/containerComponents.json lines
199-203, ui/src/locales/ko/containerComponents.json lines 201-201,
ui/src/locales/nl/containerComponents.json lines 200-201,
ui/src/locales/pl/containerComponents.json lines 198-203,
ui/src/locales/pt-BR/containerComponents.json lines 198-203,
ui/src/locales/ru/containerComponents.json lines 199-204,
ui/src/locales/tr/containerComponents.json lines 198-201,
ui/src/locales/uk/containerComponents.json lines 198-201,
ui/src/locales/vi/containerComponents.json lines 198-203, and
ui/src/locales/zh-CN/containerComponents.json lines 201-201, preserving the
grouped-view bouncer label in each locale.

475-477: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Keep the updateAge contract synchronized with update-maturity.ts. The formatter still resolves containerComponents.updateAge.available* and justNow.

  • ui/src/locales/ja/containerComponents.json#L475-L477: restore the required age translations or update the formatter.
  • ui/src/locales/ko/containerComponents.json#L478-L478: restore the required age translations or update the formatter.
  • ui/src/locales/nl/containerComponents.json#L477-L478: restore the required age translations or update the formatter.
  • ui/src/locales/pl/containerComponents.json#L475-L478: restore the required age translations or update the formatter.
  • ui/src/locales/pt-BR/containerComponents.json#L470-L478: restore the required age translations or update the formatter.
  • ui/src/locales/ru/containerComponents.json#L477-L478: restore the required age translations or update the formatter.
  • ui/src/locales/tr/containerComponents.json#L474-L478: restore the required age translations or update the formatter.
  • ui/src/locales/uk/containerComponents.json#L477-L478: restore the required age translations or update the formatter.
  • ui/src/locales/vi/containerComponents.json#L476-L479: restore the required age translations or update the formatter.
  • ui/src/locales/zh-CN/containerComponents.json#L478-L478: restore the required age translations or update the formatter.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ui/src/locales/ja/containerComponents.json` around lines 475 - 477,
Synchronize the updateAge locale contract with the formatter in
update-maturity.ts by restoring translations for all required
containerComponents.updateAge.available* keys and justNow. Apply the same
correction in ui/src/locales/ja/containerComponents.json lines 475-477,
ui/src/locales/ko/containerComponents.json line 478,
ui/src/locales/nl/containerComponents.json lines 477-478,
ui/src/locales/pl/containerComponents.json lines 475-478,
ui/src/locales/pt-BR/containerComponents.json lines 470-478,
ui/src/locales/ru/containerComponents.json lines 477-478,
ui/src/locales/tr/containerComponents.json lines 474-478,
ui/src/locales/uk/containerComponents.json lines 477-478,
ui/src/locales/vi/containerComponents.json lines 476-479, and
ui/src/locales/zh-CN/containerComponents.json line 478; preserve the formatter’s
existing key expectations rather than removing or renaming them.
ui/src/utils/container-mapper.ts (1)

516-555: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Duplicate backend-verdict logic — see consolidated comment.

findBackendMaturityBlocked here is duplicated (with a gating divergence) in ui/src/views/containers/useContainerPolicy.ts. Addressed in the consolidated comment below.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ui/src/utils/container-mapper.ts` around lines 516 - 555, Remove the
duplicated findBackendMaturityBlocked logic from isMaturityBlocked and reuse the
consolidated backend-verdict helper shared with useContainerPolicy. Ensure both
callers apply identical gating and return undefined when eligibility blockers
are unavailable, preserving the fallback maturity heuristic only when no backend
verdict exists.
🧹 Nitpick comments (1)
ui/src/utils/update-maturity.ts (1)

31-55: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Triplicated singular/plural key selection — extract a helper.

The days/hours/minutes branches repeat the same t ? (count === 1 ? singular : plural) : fallback shape three times.

♻️ Proposed refactor
+function pluralDuration(
+  count: number,
+  singularKey: string,
+  pluralKey: string,
+  fallbackUnit: string,
+  t?: TranslateFn,
+): string {
+  if (t) {
+    return count === 1 ? t(singularKey) : t(pluralKey, { count });
+  }
+  return `${count} ${fallbackUnit}${count === 1 ? '' : 's'}`;
+}
+
   let duration: string | undefined;
   if (days > 0) {
-    duration = t
-      ? days === 1
-        ? t('containerComponents.updateAge.availableDaysSingular')
-        : t('containerComponents.updateAge.availableDaysPlural', { count: days })
-      : `${days} day${days === 1 ? '' : 's'}`;
+    duration = pluralDuration(
+      days,
+      'containerComponents.updateAge.availableDaysSingular',
+      'containerComponents.updateAge.availableDaysPlural',
+      'day',
+      t,
+    );
   } else if (hours > 0) {
-    duration = t
-      ? hours === 1
-        ? t('containerComponents.updateAge.availableHoursSingular')
-        : t('containerComponents.updateAge.availableHoursPlural', { count: hours })
-      : `${hours} hour${hours === 1 ? '' : 's'}`;
+    duration = pluralDuration(
+      hours,
+      'containerComponents.updateAge.availableHoursSingular',
+      'containerComponents.updateAge.availableHoursPlural',
+      'hour',
+      t,
+    );
   } else if (minutes > 0) {
-    duration = t
-      ? minutes === 1
-        ? t('containerComponents.updateAge.availableMinutesSingular')
-        : t('containerComponents.updateAge.availableMinutesPlural', { count: minutes })
-      : `${minutes} minute${minutes === 1 ? '' : 's'}`;
+    duration = pluralDuration(
+      minutes,
+      'containerComponents.updateAge.availableMinutesSingular',
+      'containerComponents.updateAge.availableMinutesPlural',
+      'minute',
+      t,
+    );
   }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ui/src/utils/update-maturity.ts` around lines 31 - 55, Extract the repeated
singular/plural translation and fallback selection from the duration branches
into a helper near the existing duration logic, parameterized by count, singular
key, plural key, and fallback text. Update the days, hours, and minutes branches
to use this helper while preserving their current translation keys, counts, and
singular/plural fallback behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@ui/src/components/containers/ContainersGroupedViews.vue`:
- Around line 372-397: Centralize UPDATE_KIND_LABEL_KEYS and the robust
getUpdateKindLabel implementation in useContainersViewTemplateContext or a
shared utility, then remove the duplicated definition from
ContainersGroupedViews.vue. In
ui/src/components/containers/ContainersGroupedViews.vue lines 372-397, use the
shared implementation; in
ui/src/components/containers/ContainerFullPageOverviewTab.vue lines 131-132,
replace the raw updateKind span with an AppBadge using the shared localized
label; in ui/src/components/containers/ContainerFullPageTabContent.vue lines
320-328 and ui/src/components/containers/ContainerSideTabContent.vue lines
313-319, replace each local resolver with the shared utility while preserving
the unknown-kind fallback.

In `@ui/src/locales/ja/containerComponents.json`:
- Line 539: Define the missing groupedViews.pinnedLabel key alongside
pinnedTooltip in each affected locale:
ui/src/locales/ja/containerComponents.json (539-539),
ui/src/locales/ko/containerComponents.json (539-539),
ui/src/locales/nl/containerComponents.json (539-539),
ui/src/locales/pl/containerComponents.json (539-540),
ui/src/locales/pt-BR/containerComponents.json (539-539),
ui/src/locales/ru/containerComponents.json (539-539),
ui/src/locales/tr/containerComponents.json (539-540),
ui/src/locales/uk/containerComponents.json (539-540),
ui/src/locales/vi/containerComponents.json (539-539), and
ui/src/locales/zh-CN/containerComponents.json (539-539). Use the established
translation structure and provide the appropriate localized pinned-summary label
in every file so useUpdateStatus.ts can resolve
containerComponents.groupedViews.pinnedLabel.

In `@ui/src/views/containers/useContainerPolicy.ts`:
- Around line 152-170: Remove the duplicate findBackendMaturityBlocked
implementation from useContainerPolicy.ts and reuse the existing shared utility
from container-mapper.ts instead. Update its callers/imports so backend maturity
verdict handling remains centralized and follows the consolidated gating
behavior.

---

Outside diff comments:
In `@ui/src/locales/ja/containerComponents.json`:
- Around line 199-203: Restore or replace the blockedByBouncer translation key
used by ContainersGroupedViews.vue in ui/src/locales/ja/containerComponents.json
lines 199-203, ui/src/locales/ko/containerComponents.json lines 201-201,
ui/src/locales/nl/containerComponents.json lines 200-201,
ui/src/locales/pl/containerComponents.json lines 198-203,
ui/src/locales/pt-BR/containerComponents.json lines 198-203,
ui/src/locales/ru/containerComponents.json lines 199-204,
ui/src/locales/tr/containerComponents.json lines 198-201,
ui/src/locales/uk/containerComponents.json lines 198-201,
ui/src/locales/vi/containerComponents.json lines 198-203, and
ui/src/locales/zh-CN/containerComponents.json lines 201-201, preserving the
grouped-view bouncer label in each locale.
- Around line 475-477: Synchronize the updateAge locale contract with the
formatter in update-maturity.ts by restoring translations for all required
containerComponents.updateAge.available* keys and justNow. Apply the same
correction in ui/src/locales/ja/containerComponents.json lines 475-477,
ui/src/locales/ko/containerComponents.json line 478,
ui/src/locales/nl/containerComponents.json lines 477-478,
ui/src/locales/pl/containerComponents.json lines 475-478,
ui/src/locales/pt-BR/containerComponents.json lines 470-478,
ui/src/locales/ru/containerComponents.json lines 477-478,
ui/src/locales/tr/containerComponents.json lines 474-478,
ui/src/locales/uk/containerComponents.json lines 477-478,
ui/src/locales/vi/containerComponents.json lines 476-479, and
ui/src/locales/zh-CN/containerComponents.json line 478; preserve the formatter’s
existing key expectations rather than removing or renaming them.

In `@ui/src/utils/container-mapper.ts`:
- Around line 516-555: Remove the duplicated findBackendMaturityBlocked logic
from isMaturityBlocked and reuse the consolidated backend-verdict helper shared
with useContainerPolicy. Ensure both callers apply identical gating and return
undefined when eligibility blockers are unavailable, preserving the fallback
maturity heuristic only when no backend verdict exists.

---

Nitpick comments:
In `@ui/src/utils/update-maturity.ts`:
- Around line 31-55: Extract the repeated singular/plural translation and
fallback selection from the duration branches into a helper near the existing
duration logic, parameterized by count, singular key, plural key, and fallback
text. Update the days, hours, and minutes branches to use this helper while
preserving their current translation keys, counts, and singular/plural fallback
behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 4b2a0783-a4e6-4903-ace5-bd5b21366c88

📥 Commits

Reviewing files that changed from the base of the PR and between 3f4821c and 0e43aa2.

📒 Files selected for processing (65)
  • app/model/maturity-policy.test.ts
  • app/model/maturity-policy.ts
  • app/model/update-eligibility.test.ts
  • app/model/update-eligibility.ts
  • ui/src/components/DataTableColumnPicker.vue
  • ui/src/components/containers/ContainerFullPageOverviewTab.vue
  • ui/src/components/containers/ContainerFullPageTabContent.vue
  • ui/src/components/containers/ContainerSideTabContent.vue
  • ui/src/components/containers/ContainersGroupedViews.vue
  • ui/src/components/containers/ContainersListContent.vue
  • ui/src/components/containers/UpdateMaturityBadge.vue
  • ui/src/components/containers/UpdateStatusPanel.vue
  • ui/src/components/containers/containersViewTemplateContext.ts
  • ui/src/composables/useUpdateStatus.ts
  • ui/src/locales/ar/containerComponents.json
  • ui/src/locales/de/containerComponents.json
  • ui/src/locales/en/containerComponents.json
  • ui/src/locales/en/sharedComponents.json
  • ui/src/locales/es/containerComponents.json
  • ui/src/locales/fr/containerComponents.json
  • ui/src/locales/it/containerComponents.json
  • ui/src/locales/ja/containerComponents.json
  • ui/src/locales/ko/containerComponents.json
  • ui/src/locales/nl/containerComponents.json
  • ui/src/locales/pl/containerComponents.json
  • ui/src/locales/pt-BR/containerComponents.json
  • ui/src/locales/ru/containerComponents.json
  • ui/src/locales/tr/containerComponents.json
  • ui/src/locales/uk/containerComponents.json
  • ui/src/locales/vi/containerComponents.json
  • ui/src/locales/zh-CN/containerComponents.json
  • ui/src/locales/zh-TW/containerComponents.json
  • ui/src/types/container.d.ts
  • ui/src/utils/container-mapper.ts
  • ui/src/utils/dashboard-container-metrics.ts
  • ui/src/utils/display.ts
  • ui/src/utils/update-maturity.ts
  • ui/src/views/ContainersView.vue
  • ui/src/views/containers/useContainerPolicy.ts
  • ui/tests/boot/i18n.spec.ts
  • ui/tests/components/ContainerSideTabContent.spec.ts
  • ui/tests/components/DataTableColumnPicker.spec.ts
  • ui/tests/components/containers/ContainersGroupedViews.button-states.spec.ts
  • ui/tests/components/containers/ContainersGroupedViews.spec.ts
  • ui/tests/components/containers/ContainersListContent.spec.ts
  • ui/tests/components/containers/UpdateMaturityBadge.spec.ts
  • ui/tests/components/containers/UpdateStatusPanel.spec.ts
  • ui/tests/composables/useContainerFilters.spec.ts
  • ui/tests/composables/useDetailPanel.spec.ts
  • ui/tests/composables/useOperationDisplayHold.spec.ts
  • ui/tests/composables/useUpdateStatus.spec.ts
  • ui/tests/composables/useVulnerabilities.spec.ts
  • ui/tests/utils/container-mapper.spec.ts
  • ui/tests/utils/dashboard-container-metrics.spec.ts
  • ui/tests/utils/display.spec.ts
  • ui/tests/utils/hide-pinned.spec.ts
  • ui/tests/utils/update-maturity.spec.ts
  • ui/tests/views/ContainersView.applyContainerPatch.spec.ts
  • ui/tests/views/ContainersView.spec.ts
  • ui/tests/views/DashboardView.spec.ts
  • ui/tests/views/containers/useContainerActions.spec.ts
  • ui/tests/views/containers/useContainerLogs.spec.ts
  • ui/tests/views/containers/useContainerPolicy.spec.ts
  • ui/tests/views/dashboard/useDashboardComputed.spec.ts
  • ui/tests/views/dashboard/useDashboardData.spec.ts
💤 Files with no reviewable changes (17)
  • ui/tests/composables/useDetailPanel.spec.ts
  • ui/tests/composables/useContainerFilters.spec.ts
  • ui/src/components/containers/UpdateMaturityBadge.vue
  • ui/tests/components/containers/UpdateMaturityBadge.spec.ts
  • ui/tests/components/containers/ContainersGroupedViews.button-states.spec.ts
  • ui/tests/views/dashboard/useDashboardComputed.spec.ts
  • ui/tests/components/ContainerSideTabContent.spec.ts
  • ui/tests/utils/hide-pinned.spec.ts
  • ui/tests/views/containers/useContainerLogs.spec.ts
  • ui/tests/views/dashboard/useDashboardData.spec.ts
  • ui/tests/views/containers/useContainerActions.spec.ts
  • ui/tests/views/ContainersView.applyContainerPatch.spec.ts
  • ui/tests/composables/useOperationDisplayHold.spec.ts
  • ui/tests/views/ContainersView.spec.ts
  • ui/tests/views/DashboardView.spec.ts
  • ui/src/types/container.d.ts
  • ui/tests/composables/useVulnerabilities.spec.ts

Comment thread ui/src/components/containers/ContainersGroupedViews.vue Outdated
Comment thread ui/src/locales/ja/containerComponents.json
Comment thread ui/src/views/containers/useContainerPolicy.ts Outdated
scttbnsn added a commit that referenced this pull request Jul 20, 2026
…erdict lookup

- Extract shared update-kind-labels.ts (map, localized resolver with unknown-kind
  fallback, hasUnresolvedUpdateKind) and consume it from ContainersGroupedViews,
  ContainerFullPageTabContent, ContainerSideTabContent, ContainerFullPageDetail, and
  ContainerFullPageOverviewTab — the last of which previously rendered the raw
  updateKind string with no localization at all
- Dedupe findBackendMaturityBlocked() into update-eligibility.ts; container-mapper.ts
  and useContainerPolicy.ts now import the shared function and keep their existing
  outer gating (maturityMode check / unconditional verdict use) unchanged
- Extract a private formatDurationUnit() helper in update-maturity.ts to collapse the
  triplicated day/hour/minute t()-vs-fallback branches

Addresses CodeRabbit review comments on PR #566.
@scttbnsn

Copy link
Copy Markdown
Contributor Author

Outside-diff and nitpick sweep:

  • blockedByBouncer / updateAge.* deleted from translated catalogs: intentional, same rationale as the pinnedLabel thread above. These keys were repurposed with new semantics; the stale translations would render old-meaning text in the new slots (e.g. "NEU" as a freshness tooltip, or old "Available for..." sentences composing into "Detected Available for 1 day ago"). vue-i18n falls back to the English strings until Crowdin retranslates, and the en catalog still defines every key the formatter resolves (updateAge.available*Singular/Plural and justNow all live in en with the new bare-duration values).
  • Triplicated singular/plural selection in update-maturity.ts: taken in cf91ade, extracted a private formatDurationUnit() helper with keys/counts/fallbacks unchanged.

While fixing the label duplication we also found and folded in a fourth local getUpdateKindLabel copy in ContainerFullPageDetail.vue that the review didn't flag.

scttbnsn added a commit that referenced this pull request Jul 20, 2026
…erdict lookup

- Extract shared update-kind-labels.ts (map, localized resolver with unknown-kind
  fallback, hasUnresolvedUpdateKind) and consume it from ContainersGroupedViews,
  ContainerFullPageTabContent, ContainerSideTabContent, ContainerFullPageDetail, and
  ContainerFullPageOverviewTab — the last of which previously rendered the raw
  updateKind string with no localization at all
- Dedupe findBackendMaturityBlocked() into update-eligibility.ts; container-mapper.ts
  and useContainerPolicy.ts now import the shared function and keep their existing
  outer gating (maturityMode check / unconditional verdict use) unchanged
- Extract a private formatDurationUnit() helper in update-maturity.ts to collapse the
  triplicated day/hour/minute t()-vs-fallback branches

Addresses CodeRabbit review comments on PR #566.
@scttbnsn
scttbnsn force-pushed the feat/v1.6-display-honesty branch from cf91ade to 3d86839 Compare July 20, 2026 22:52
scttbnsn added a commit that referenced this pull request Jul 20, 2026
…delta (#569)

## What

Docs/copy freshness pass for the v1.6.1 delta (the #567 security batch,
the #566 display batch, and the #568 maturity-clock fix). 30 findings
from an adversarially-verified audit workflow (8 surface auditors, every
finding independently re-verified against the shipped code before it
counted; 1 rejected in verification), applied as one coherent edit per
doc region:

- **Anonymous-auth fail-closed (A1)**: the authentications page
(callout, behavior matrix, and the "Upgrading from v1.3.x" section), the
security guide, the README banner + quickstart line, and DEPRECATIONS.md
all still promised upgraders a warn-and-serve grace period. Rewritten
everywhere: upgrades now fail closed exactly like fresh installs;
`DD_ANONYMOUS_AUTH_CONFIRM=true` is the opt-in.
- **Session cookie rename (A4)**: `connect.sid` → `drydock.sid` one-time
sign-out documented in DEPRECATIONS.md, the upgrading section, and the
README banner.
- **SSRF hardening (A2)**: the HTTP trigger page now documents the
guarded DNS lookup, redirect refusal, and the `ALLOWMETADATA` escape
hatch.
- **WebSocket origin checks (A3)**: server config, FAQ, quickstart
reverse-proxy warning, `api/log.mdx`, and `api/container.mdx` now state
that origin validation covers scheme/host/port and that forwarded
headers are honored only with `DD_SERVER_TRUSTPROXY`.
- **UI vocabulary (B3/B4/B5)**: watchers page and FAQ updated for the
pin-glyph/"Current" pinned treatment; eligibility + API docs document
the new additive `clockSource`/`clockStartAt` blocker fields; the UI
page covers the column-picker auto-hidden annotation. Wording grounded
in the actual #566 locale strings, not paraphrased.
- **Website quickstarts actually start**: the shared `DockerRunSnippet`
(all 7 comparison pages) and the FAQ quickstart now include the auth
opt-in env var — without it the one-liner produces a container that
refuses to start on the v1.6 line.

Also rides along: `protobufjs` pinned to 7.6.5 (CVE-2026-59877 /
GHSA-j3f2-48v5-ccww, third fresh advisory today) in app (new override)
and e2e (existing override bumped) — it began failing the qlty
osv-scanner gate mid-push.

Deliberately skipped: `content/docs/current/updates/index.mdx` (strictly
per-release curated notes; rc.3 highlights belong to the actual cut) and
`UPGRADE-NOTES.md` (version-gated to 1.4.6–1.5.x by
`scripts/append-upgrade-notes.mjs`; v1.6 content there would leak into
1.5.x release notes).

## Why

The security batch merged with zero docs changes, leaving the auth docs
actively contradicting shipped behavior. Docs on `dev/v1.6` publish at
the GA dev→main merge, so these corrections go live exactly when the
behavior does.

## Tests

Full pre-push gate green. apps/web script tests 43/43; MDX parse
validated via sync-docs + fumadocs-mdx; biome clean.


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Changelog

- 🔒 Documented fail-closed anonymous authentication for both fresh
installs and upgrades; require `DD_ANONYMOUS_AUTH_CONFIRM=true` (and
clarified `/health` vs API response behavior).
- ⚠️ Security documented “no deprecation window” enforcement: removed
upgrade grandfather path when anonymous auth isn’t configured/confirmed;
upgrades stop serving/fail closed.
- ⚠️ 🔒 Documented `connect.sid` → `drydock.sid` session cookie rename
and one-time sign-out on upgrade.
- 🔒 Documented SSRF protections for guarded DNS lookup, redirect refusal
(3xx never followed), and `ALLOWMETADATA` escape hatch.
- 🔒 Documented WebSocket `Origin` validation (scheme/host/port) and
clarified reverse-proxy requirements via `DD_SERVER_TRUSTPROXY` +
forwarded headers (`X-Forwarded-Proto`/`X-Forwarded-Host`), including
log-stream connection failures when misconfigured.
- ✨ Documented maturity-clock details for `maturity-not-reached` (clock
source + ISO-8601 start timestamp payload) and UI countdown rendering
behavior.
- ✨ Updated UI copy/annotations for column visibility and “hidden to
fit” behavior.
- ✨ Updated watcher UI description for fully-pinned tags to reflect
`updateInsight` chip styling + persistent pin glyph/tooltips.
- 🔧 Added `DD_ANONYMOUS_AUTH_CONFIRM=true` to website quickstarts and
updated Docker run snippet wording.
- 🔧 Pinned `protobufjs` to `7.6.5` in both app and e2e dependencies.
- 🔧 Updated doc identity/release tests to assert current fail-closed
semantics for `content/docs/current` while preserving legacy v1.5
behavior.

## Concerns

- Confirm `DD_SERVER_TRUSTPROXY` docs explicitly require forwarding
`X-Forwarded-Proto` and `X-Forwarded-Host` for scheme/host matching.
- Validate that the documented fail-closed behavior matches the actual
endpoints: API 401 behavior vs `/health` 503 reporting.
- Ensure the “redirects never followed” documentation aligns with the
actual redirect-following disablement and how `ALLOWMETADATA` is
evaluated.
- Verify cookie rename handling is documented consistently across all
relevant upgrade/install docs (including one-time sign-out semantics).
- Check that WebSocket origin validation requirements cover both
dashboard log viewer and container log streaming endpoints (upgrade vs
steady-state behavior).
<!-- end of auto-generated comment: release notes by coderabbit.ai -->

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@ui/src/locales/ar/appShell.json`:
- Around line 160-161: Remove the manually seeded legacyApiPathTitleSingular and
legacyApiPathTitlePlural keys from ui/src/locales/ar/appShell.json lines
160-161, ui/src/locales/de/appShell.json lines 160-161,
ui/src/locales/es/appShell.json lines 160-161,
ui/src/locales/zh-CN/appShell.json lines 160-161, and
ui/src/locales/zh-TW/appShell.json lines 160-161; rely on fallbackLocale to
provide the English values.

In `@ui/src/locales/fr/appShell.json`:
- Around line 160-161: Remove the legacyApiPathTitleSingular and
legacyApiPathTitlePlural entries from ui/src/locales/fr/appShell.json (160-161),
ui/src/locales/it/appShell.json (160-161), ui/src/locales/ja/appShell.json
(160-161), ui/src/locales/ko/appShell.json (160-161),
ui/src/locales/nl/appShell.json (160-161), ui/src/locales/pl/appShell.json
(160-161), ui/src/locales/pt-BR/appShell.json (160-161),
ui/src/locales/ru/appShell.json (160-161), and ui/src/locales/tr/appShell.json
(160-161). Leave these keys absent so fallbackLocale supplies the English values
until Crowdin provides translations.

In `@ui/src/locales/uk/appShell.json`:
- Around line 160-161: Remove the manually seeded English keys
legacyApiPathTitleSingular and legacyApiPathTitlePlural from
ui/src/locales/uk/appShell.json lines 160-161 and
ui/src/locales/vi/appShell.json lines 160-161; rely on the runtime English
fallback so Crowdin can manage these translations.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 62344200-47d9-4dec-bc77-f143cd6a67ea

📥 Commits

Reviewing files that changed from the base of the PR and between 0e43aa2 and 51a3ed5.

⛔ Files ignored due to path filters (3)
  • CHANGELOG.md is excluded by !CHANGELOG.md
  • app/package-lock.json is excluded by !**/package-lock.json, !**/package-lock.json
  • e2e/package-lock.json is excluded by !**/package-lock.json, !**/package-lock.json
📒 Files selected for processing (102)
  • app/model/container.test.ts
  • app/model/container.ts
  • app/model/maturity-policy.test.ts
  • app/model/maturity-policy.ts
  • app/model/update-eligibility.test.ts
  • app/model/update-eligibility.ts
  • app/package.json
  • app/tag/family.test.ts
  • app/tag/family.ts
  • app/watchers/providers/docker/tag-candidates.ts
  • e2e/package.json
  • ui/src/components/DataTableColumnPicker.vue
  • ui/src/components/containers/ContainerFullPageDetail.vue
  • ui/src/components/containers/ContainerFullPageOverviewTab.vue
  • ui/src/components/containers/ContainerFullPageTabContent.vue
  • ui/src/components/containers/ContainerSideTabContent.vue
  • ui/src/components/containers/ContainersGroupedViews.vue
  • ui/src/components/containers/ContainersListContent.vue
  • ui/src/components/containers/UpdateMaturityBadge.vue
  • ui/src/components/containers/UpdateStatusPanel.vue
  • ui/src/components/containers/containersViewTemplateContext.ts
  • ui/src/composables/useUpdateStatus.ts
  • ui/src/directives/tooltip.ts
  • ui/src/layouts/AppLayout.vue
  • ui/src/locales/ar/appShell.json
  • ui/src/locales/ar/containerComponents.json
  • ui/src/locales/de/appShell.json
  • ui/src/locales/de/containerComponents.json
  • ui/src/locales/en/appShell.json
  • ui/src/locales/en/containerComponents.json
  • ui/src/locales/en/sharedComponents.json
  • ui/src/locales/es/appShell.json
  • ui/src/locales/es/containerComponents.json
  • ui/src/locales/fr/appShell.json
  • ui/src/locales/fr/containerComponents.json
  • ui/src/locales/it/appShell.json
  • ui/src/locales/it/containerComponents.json
  • ui/src/locales/ja/appShell.json
  • ui/src/locales/ja/containerComponents.json
  • ui/src/locales/ko/appShell.json
  • ui/src/locales/ko/containerComponents.json
  • ui/src/locales/nl/appShell.json
  • ui/src/locales/nl/containerComponents.json
  • ui/src/locales/pl/appShell.json
  • ui/src/locales/pl/containerComponents.json
  • ui/src/locales/pt-BR/appShell.json
  • ui/src/locales/pt-BR/containerComponents.json
  • ui/src/locales/ru/appShell.json
  • ui/src/locales/ru/containerComponents.json
  • ui/src/locales/tr/appShell.json
  • ui/src/locales/tr/containerComponents.json
  • ui/src/locales/uk/appShell.json
  • ui/src/locales/uk/containerComponents.json
  • ui/src/locales/vi/appShell.json
  • ui/src/locales/vi/containerComponents.json
  • ui/src/locales/zh-CN/appShell.json
  • ui/src/locales/zh-CN/containerComponents.json
  • ui/src/locales/zh-TW/appShell.json
  • ui/src/locales/zh-TW/containerComponents.json
  • ui/src/types/container.d.ts
  • ui/src/utils/container-mapper.ts
  • ui/src/utils/dashboard-container-metrics.ts
  • ui/src/utils/display.ts
  • ui/src/utils/update-eligibility.ts
  • ui/src/utils/update-kind-labels.ts
  • ui/src/utils/update-maturity.ts
  • ui/src/views/ContainersView.vue
  • ui/src/views/RegistriesView.vue
  • ui/src/views/containers/useContainerPolicy.ts
  • ui/tests/boot/i18n.spec.ts
  • ui/tests/components/ContainerSideTabContent.spec.ts
  • ui/tests/components/DataTableColumnPicker.spec.ts
  • ui/tests/components/containers/ContainerFullPageDetail.spec.ts
  • ui/tests/components/containers/ContainerFullPageOverviewTab.spec.ts
  • ui/tests/components/containers/ContainerFullPageTabContent.spec.ts
  • ui/tests/components/containers/ContainersGroupedViews.button-states.spec.ts
  • ui/tests/components/containers/ContainersGroupedViews.spec.ts
  • ui/tests/components/containers/ContainersListContent.spec.ts
  • ui/tests/components/containers/UpdateMaturityBadge.spec.ts
  • ui/tests/components/containers/UpdateStatusPanel.spec.ts
  • ui/tests/composables/useContainerFilters.spec.ts
  • ui/tests/composables/useDetailPanel.spec.ts
  • ui/tests/composables/useOperationDisplayHold.spec.ts
  • ui/tests/composables/useUpdateStatus.spec.ts
  • ui/tests/composables/useVulnerabilities.spec.ts
  • ui/tests/directives/tooltip.spec.ts
  • ui/tests/utils/container-mapper.spec.ts
  • ui/tests/utils/dashboard-container-metrics.spec.ts
  • ui/tests/utils/display.spec.ts
  • ui/tests/utils/hide-pinned.spec.ts
  • ui/tests/utils/update-eligibility.spec.ts
  • ui/tests/utils/update-kind-labels.spec.ts
  • ui/tests/utils/update-maturity.spec.ts
  • ui/tests/views/ContainersView.applyContainerPatch.spec.ts
  • ui/tests/views/ContainersView.spec.ts
  • ui/tests/views/DashboardView.spec.ts
  • ui/tests/views/RegistriesView.spec.ts
  • ui/tests/views/containers/useContainerActions.spec.ts
  • ui/tests/views/containers/useContainerLogs.spec.ts
  • ui/tests/views/containers/useContainerPolicy.spec.ts
  • ui/tests/views/dashboard/useDashboardComputed.spec.ts
  • ui/tests/views/dashboard/useDashboardData.spec.ts
💤 Files with no reviewable changes (15)
  • ui/tests/views/ContainersView.applyContainerPatch.spec.ts
  • ui/tests/views/containers/useContainerLogs.spec.ts
  • ui/tests/utils/hide-pinned.spec.ts
  • ui/tests/composables/useDetailPanel.spec.ts
  • ui/src/components/containers/UpdateMaturityBadge.vue
  • ui/tests/composables/useContainerFilters.spec.ts
  • ui/tests/components/containers/UpdateMaturityBadge.spec.ts
  • ui/tests/composables/useOperationDisplayHold.spec.ts
  • ui/tests/views/dashboard/useDashboardData.spec.ts
  • ui/tests/views/ContainersView.spec.ts
  • ui/tests/views/dashboard/useDashboardComputed.spec.ts
  • ui/tests/components/containers/ContainersGroupedViews.button-states.spec.ts
  • ui/tests/views/DashboardView.spec.ts
  • ui/tests/views/containers/useContainerActions.spec.ts
  • ui/tests/composables/useVulnerabilities.spec.ts
🚧 Files skipped from review as they are similar to previous changes (41)
  • ui/src/locales/en/sharedComponents.json
  • ui/tests/boot/i18n.spec.ts
  • ui/src/components/containers/containersViewTemplateContext.ts
  • ui/src/components/DataTableColumnPicker.vue
  • ui/src/components/containers/UpdateStatusPanel.vue
  • ui/tests/composables/useUpdateStatus.spec.ts
  • ui/src/composables/useUpdateStatus.ts
  • app/model/maturity-policy.ts
  • ui/tests/utils/display.spec.ts
  • ui/src/utils/display.ts
  • ui/tests/components/containers/UpdateStatusPanel.spec.ts
  • ui/src/views/ContainersView.vue
  • app/model/maturity-policy.test.ts
  • ui/src/locales/ko/containerComponents.json
  • ui/src/locales/fr/containerComponents.json
  • ui/src/components/containers/ContainerFullPageOverviewTab.vue
  • ui/src/components/containers/ContainersListContent.vue
  • ui/tests/components/DataTableColumnPicker.spec.ts
  • ui/src/utils/dashboard-container-metrics.ts
  • ui/tests/utils/update-maturity.spec.ts
  • ui/src/components/containers/ContainerSideTabContent.vue
  • ui/src/locales/en/containerComponents.json
  • ui/tests/utils/dashboard-container-metrics.spec.ts
  • ui/src/locales/de/containerComponents.json
  • ui/src/locales/pl/containerComponents.json
  • ui/src/locales/uk/containerComponents.json
  • ui/src/locales/ar/containerComponents.json
  • ui/src/utils/update-maturity.ts
  • ui/src/locales/tr/containerComponents.json
  • ui/tests/views/containers/useContainerPolicy.spec.ts
  • ui/src/locales/zh-CN/containerComponents.json
  • ui/src/locales/pt-BR/containerComponents.json
  • ui/src/locales/ja/containerComponents.json
  • ui/src/locales/es/containerComponents.json
  • ui/src/locales/ru/containerComponents.json
  • ui/src/locales/vi/containerComponents.json
  • ui/src/locales/zh-TW/containerComponents.json
  • ui/src/locales/nl/containerComponents.json
  • ui/src/locales/it/containerComponents.json
  • ui/tests/components/containers/ContainersGroupedViews.spec.ts
  • app/model/update-eligibility.ts

Comment thread ui/src/locales/ar/appShell.json Outdated
Comment thread ui/src/locales/fr/appShell.json Outdated
Comment thread ui/src/locales/uk/appShell.json Outdated
scttbnsn added a commit that referenced this pull request Jul 21, 2026
The new legacyApiPathTitleSingular/Plural keys were seeded verbatim into all
16 translated catalogs, which Crowdin would treat as done translations.
Missing keys fall back to en via fallbackLocale until Crowdin fills them —
same as every other new key. (CodeRabbit #566 round 4.)
scttbnsn added 7 commits July 21, 2026 09:07
computeUpdateEligibility() picked between a trusted registry publishedAt
and updateDetectedAt internally (getMaturityStartMs), but never told
callers which clock it used or when it started — the UI had no choice
but to re-derive "is this maturity-blocked?" from updateDetectedAt alone
in two separate places, drifting from the server's actual verdict.

- Extract resolveMaturityClock() alongside the existing getMaturityStartMs()
  (now a thin wrapper), returning both the resolved startMs and which
  clock ('publishedAt' | 'detectedAt') won
- Add clockSource/clockStartAt to the maturity-not-reached blocker's
  `details` (additive only — /api/v1 stays frozen for breaking changes)
- Drop the redundant "(N days remaining)" clause from the blocker message
  now that the UI composes its own countdown from `details.remainingMs`
Display-honesty batch, part 1 — the container table/card and detail
panels stop lying about update state:

- Remove the separate NEW/MATURE freshness badge and Container.updateMaturity
  field entirely; the same freshness fact now lives in a tooltip on the
  update-type badge (formatUpdateAge() -> maturityBadge.new template)
- "Digest" -> "Digest update" in the update-kind label (listContent.digest),
  fixing both the table and card render branches via the shared
  getUpdateKindLabel() helper
- Drop the dedicated "Pinned" state + its own insight-kind badge from the
  Update column; a pinned row with a newer same-family version now shows
  a plain Major/Minor/Patch chip like any other update, and up-to-date
  pinned rows show "Current" — no more separate pinned vocabulary there
- Add a persistent pin glyph (existing `pin` icon) to the Tag cell for
  tagPinned rows, with a tooltip naming the remedy (dd.tag.family=loose /
  dd.tag.include)
- Give the previously-silent unknown-update-kind fallback a visible
  neutral "Unknown" badge instead of an invisible transparent one
  (updateKindColor's fallback branch)
- Bouncer-blocked pill now reads "Security hold" (groupedViews.blockedByBouncer,
  listContent.blocked) instead of the generic "Blocked" — scoped to the
  vulnerability-bouncer state only; the maturity blocker's "Blocked" text
  is untouched
- Collapse updateInsight.tooltipMajor/Minor/Patch into one updateInsight.tooltip
  template

dashboard-container-metrics.ts keeps its own small isFreshUpdate() helper
(7-day threshold) for the dashboard KPI, now independent of the removed
badge classification it used to borrow from update-maturity.ts.
Display-honesty batch, part 2 — the maturity panel stops showing two
different clocks for the same wait:

- useContainerPolicy.ts's buildContainerListPolicyStateFromPolicy() now
  prefers the backend's maturity-not-reached blocker verdict (mirrors
  container-mapper.ts's findBackendMaturityBlocked from part 1) instead
  of re-deriving "maturity-blocked" from updateDetectedAt alone; falls
  back to the old computation only when no eligibility payload is present
- The maturity condition's body names its clock explicitly — "Candidate
  published {date} — {n} more day(s) until the {days}-day minimum" for a
  trusted registry publishedAt, with a "Candidate detected {date} …"
  fallback when the clock is updateDetectedAt (maturitySentence(), using
  the clockSource/clockStartAt/minAgeDays/remainingMs details added
  server-side in part 0)
- Collapse the duplicated countdown ("(N days remaining)" in the message
  + "2d 0h 18m · Lifts at …" below it) into one liftCountdown line:
  "{countdown} · unlocks {date}"
Display-honesty batch, part 3 — a column the user checked "on" but that
responsive sizing pushed off the table for width used to render as
plainly checked in the picker, with no hint it isn't actually showing.

- Thread useColumnVisibility's autoHiddenColumns through
  containersViewTemplateContext.ts and ContainersView.vue's provide()
- DataTableColumnPicker.vue gains an optional autoHiddenKeys prop
  (defaults to none — a no-op for the other 5 consumers: Agents,
  Security, Audit, Watchers, Servers); a checked-but-auto-hidden row
  renders muted "{column} — hidden to fit" instead of the plain label
- The "+N" badge count now includes checked-but-auto-hidden columns, and
  its tooltip names them ("Host hidden to fit your screen.") instead of
  just showing a generic count
- ContainersListContent.vue's pickerHiddenColumnKeys (checkbox state)
  is unchanged — still user-driven only; the new
  pickerAutoHiddenColumnKeys is a separate signal for the annotation
…erdict lookup

- Extract shared update-kind-labels.ts (map, localized resolver with unknown-kind
  fallback, hasUnresolvedUpdateKind) and consume it from ContainersGroupedViews,
  ContainerFullPageTabContent, ContainerSideTabContent, ContainerFullPageDetail, and
  ContainerFullPageOverviewTab — the last of which previously rendered the raw
  updateKind string with no localization at all
- Dedupe findBackendMaturityBlocked() into update-eligibility.ts; container-mapper.ts
  and useContainerPolicy.ts now import the shared function and keep their existing
  outer gating (maturityMode check / unconditional verdict use) unchanged
- Extract a private formatDurationUnit() helper in update-maturity.ts to collapse the
  triplicated day/hour/minute t()-vs-fallback branches

Addresses CodeRabbit review comments on PR #566.
scttbnsn added 11 commits July 21, 2026 09:07
The tag-candidates pin gate (specific-precision tag, no dd.tag.include,
non-loose dd.tag.family) now shares its entry condition with the container
model via app/tag/family.ts isPinGateGoverned(), and validate() materializes
the verdict as a tagPinGated data property. Distinct from shape-based
tagPinned, which keeps feeding the hide-pinned filter.
…rdict

The glyph was 9px and absolutely positioned at the version cell's top-left
corner, clipping to a near-invisible sliver, and it keyed on shape-based
tagPinned — true for every exact-version tag, including rows with actionable
updates, making its "won't climb to newer versions" tooltip false. It now
renders inline at 12px grouped with the current tag (surviving the narrow-cell
column stack) on tagPinGated rows only, and insight-only rows read "Current"
in the update-state column instead of the insight's kind styled as actionable
(#498 pinned-chip inconsistency; matches the approved display-honesty spec).
Tooltip remedy now says "add a dd.tag.include filter" — gated rows have none
to relax.
"1 requests to the removed API path detected" — the title used a single
interpolated string for every count. Split into Singular/Plural keys (the
catalog's established idiom, e.g. legacyConfigTitle*) across all 17 locales,
selected by total in AppLayout.
updated() adopted whatever title the element currently carried as the new
component-owned fallback, but that title was usually one our own syncTitle
wrote, so the fallback froze at first render and assistive tech read stale
text (e.g. the column-picker +N badge announcing only its first hidden
column). Only adopt a title that differs from the last-synced fallback.
The #556 deep-link (?q=gcr.public) matched nothing because the filter only
checked type and name separately. Also match the combined type.name form so
provider deep-links land on the intended row.
- 🐛 pin glyph now gate-driven (tagPinGated) and inline beside the tag
- 🐛 registries ?q= deep-link matches dotted type.name (#556)
- 🐛 banner pluralization + tooltip native-title freshness
- 📝 correct the auth fail-closed wording to the 401/503 behavior
The new legacyApiPathTitleSingular/Plural keys were seeded verbatim into all
16 translated catalogs, which Crowdin would treat as done translations.
Missing keys fall back to en via fallbackLocale until Crowdin fills them —
same as every other new key. (CodeRabbit #566 round 4.)
The panel still derived a dedicated 'pinned' state (info tone, "Pinned — {tag}
is available…") for insight-only containers, so the same container read
"Current" in the table but "Pinned" in its own detail view. The state is
retired: insight-only containers read up to date like everywhere else, and the
held-back newer tag surfaces as an informational detail row reusing the
table's updateInsight.tooltip vocabulary. The now-unused groupedViews
.pinnedLabel key is removed (en was its only catalog).
The tighter current-tag max-width keyed on :first-child inside the version
flow; the glyph wrapper made the tag the wrapper's second child on pinned
rows, silently widening them to the target-tag budget. The budget is now
class-scoped to the wrapper (container-version-current) so it applies
regardless of glyph presence.
…ned state

watchers and FAQ pages still told readers to look for a neutral "Pinned"
update state with a Major/Minor/Patch chip; the UI now keeps insight-only
rows at Current with the pin glyph beside the tag. CHANGELOG notes the
detail-panel alignment.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
ui/src/locales/tr/containerComponents.json (1)

198-200: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Do not hand-seed new keys in the Turkish catalog.

Remove emptyState, currentLabel, latestLabel, allBouncer, safe, and unsafe here. These catalogs are Crowdin-managed; missing keys intentionally fall back to English until retranslated.

Based on learnings, non-English catalogs must not manually add new translation keys.

Proposed diff
-      "emptyState": "Filtrelerinizle eşleşen kapsayıcı yok",
-      "currentLabel": "Mevcut",
-      "latestLabel": "En son",
...
-      "allBouncer": "Tüm Bouncer",
-      "safe": "Güvenli",
-      "unsafe": "Güvenli değil",

Also applies to: 258-260

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@ui/src/locales/tr/containerComponents.json` around lines 198 - 200, Remove
the newly added emptyState, currentLabel, latestLabel, allBouncer, safe, and
unsafe keys from the Turkish locale catalog, leaving missing entries to fall
back to English under Crowdin management.

Source: Learnings

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@ui/src/locales/tr/containerComponents.json`:
- Around line 198-200: Remove the newly added emptyState, currentLabel,
latestLabel, allBouncer, safe, and unsafe keys from the Turkish locale catalog,
leaving missing entries to fall back to English under Crowdin management.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: efd9c7af-b120-4a4a-aaef-6013f5e159ea

📥 Commits

Reviewing files that changed from the base of the PR and between a99cf0d and 43804ec.

⛔ Files ignored due to path filters (1)
  • CHANGELOG.md is excluded by !CHANGELOG.md
📒 Files selected for processing (100)
  • app/model/container.test.ts
  • app/model/container.ts
  • app/model/maturity-policy.test.ts
  • app/model/maturity-policy.ts
  • app/model/update-eligibility.test.ts
  • app/model/update-eligibility.ts
  • app/tag/family.test.ts
  • app/tag/family.ts
  • app/watchers/providers/docker/tag-candidates.ts
  • ui/src/components/DataTableColumnPicker.vue
  • ui/src/components/containers/ContainerFullPageDetail.vue
  • ui/src/components/containers/ContainerFullPageOverviewTab.vue
  • ui/src/components/containers/ContainerFullPageTabContent.vue
  • ui/src/components/containers/ContainerSideTabContent.vue
  • ui/src/components/containers/ContainersGroupedViews.vue
  • ui/src/components/containers/ContainersListContent.vue
  • ui/src/components/containers/UpdateMaturityBadge.vue
  • ui/src/components/containers/UpdateStatusPanel.vue
  • ui/src/components/containers/containersViewTemplateContext.ts
  • ui/src/composables/useUpdateStatus.ts
  • ui/src/directives/tooltip.ts
  • ui/src/layouts/AppLayout.vue
  • ui/src/locales/ar/appShell.json
  • ui/src/locales/ar/containerComponents.json
  • ui/src/locales/de/appShell.json
  • ui/src/locales/de/containerComponents.json
  • ui/src/locales/en/appShell.json
  • ui/src/locales/en/containerComponents.json
  • ui/src/locales/en/sharedComponents.json
  • ui/src/locales/es/appShell.json
  • ui/src/locales/es/containerComponents.json
  • ui/src/locales/fr/appShell.json
  • ui/src/locales/fr/containerComponents.json
  • ui/src/locales/it/appShell.json
  • ui/src/locales/it/containerComponents.json
  • ui/src/locales/ja/appShell.json
  • ui/src/locales/ja/containerComponents.json
  • ui/src/locales/ko/appShell.json
  • ui/src/locales/ko/containerComponents.json
  • ui/src/locales/nl/appShell.json
  • ui/src/locales/nl/containerComponents.json
  • ui/src/locales/pl/appShell.json
  • ui/src/locales/pl/containerComponents.json
  • ui/src/locales/pt-BR/appShell.json
  • ui/src/locales/pt-BR/containerComponents.json
  • ui/src/locales/ru/appShell.json
  • ui/src/locales/ru/containerComponents.json
  • ui/src/locales/tr/appShell.json
  • ui/src/locales/tr/containerComponents.json
  • ui/src/locales/uk/appShell.json
  • ui/src/locales/uk/containerComponents.json
  • ui/src/locales/vi/appShell.json
  • ui/src/locales/vi/containerComponents.json
  • ui/src/locales/zh-CN/appShell.json
  • ui/src/locales/zh-CN/containerComponents.json
  • ui/src/locales/zh-TW/appShell.json
  • ui/src/locales/zh-TW/containerComponents.json
  • ui/src/types/container.d.ts
  • ui/src/utils/container-mapper.ts
  • ui/src/utils/dashboard-container-metrics.ts
  • ui/src/utils/display.ts
  • ui/src/utils/update-eligibility.ts
  • ui/src/utils/update-kind-labels.ts
  • ui/src/utils/update-maturity.ts
  • ui/src/views/ContainersView.vue
  • ui/src/views/RegistriesView.vue
  • ui/src/views/containers/useContainerPolicy.ts
  • ui/tests/boot/i18n.spec.ts
  • ui/tests/components/ContainerSideTabContent.spec.ts
  • ui/tests/components/DataTableColumnPicker.spec.ts
  • ui/tests/components/containers/ContainerFullPageDetail.spec.ts
  • ui/tests/components/containers/ContainerFullPageOverviewTab.spec.ts
  • ui/tests/components/containers/ContainerFullPageTabContent.spec.ts
  • ui/tests/components/containers/ContainersGroupedViews.button-states.spec.ts
  • ui/tests/components/containers/ContainersGroupedViews.spec.ts
  • ui/tests/components/containers/ContainersListContent.spec.ts
  • ui/tests/components/containers/UpdateMaturityBadge.spec.ts
  • ui/tests/components/containers/UpdateStatusPanel.spec.ts
  • ui/tests/composables/useContainerFilters.spec.ts
  • ui/tests/composables/useDetailPanel.spec.ts
  • ui/tests/composables/useOperationDisplayHold.spec.ts
  • ui/tests/composables/useUpdateStatus.spec.ts
  • ui/tests/composables/useVulnerabilities.spec.ts
  • ui/tests/directives/tooltip.spec.ts
  • ui/tests/utils/container-mapper.spec.ts
  • ui/tests/utils/dashboard-container-metrics.spec.ts
  • ui/tests/utils/display.spec.ts
  • ui/tests/utils/hide-pinned.spec.ts
  • ui/tests/utils/update-eligibility.spec.ts
  • ui/tests/utils/update-kind-labels.spec.ts
  • ui/tests/utils/update-maturity.spec.ts
  • ui/tests/views/ContainersView.applyContainerPatch.spec.ts
  • ui/tests/views/ContainersView.spec.ts
  • ui/tests/views/DashboardView.spec.ts
  • ui/tests/views/RegistriesView.spec.ts
  • ui/tests/views/containers/useContainerActions.spec.ts
  • ui/tests/views/containers/useContainerLogs.spec.ts
  • ui/tests/views/containers/useContainerPolicy.spec.ts
  • ui/tests/views/dashboard/useDashboardComputed.spec.ts
  • ui/tests/views/dashboard/useDashboardData.spec.ts
💤 Files with no reviewable changes (31)
  • ui/tests/composables/useDetailPanel.spec.ts
  • ui/tests/views/dashboard/useDashboardData.spec.ts
  • ui/tests/components/containers/UpdateMaturityBadge.spec.ts
  • ui/src/locales/ja/appShell.json
  • ui/tests/utils/hide-pinned.spec.ts
  • ui/tests/components/containers/ContainersGroupedViews.button-states.spec.ts
  • ui/src/locales/pl/appShell.json
  • ui/tests/composables/useContainerFilters.spec.ts
  • ui/tests/views/containers/useContainerActions.spec.ts
  • ui/src/locales/zh-TW/appShell.json
  • ui/src/locales/pt-BR/appShell.json
  • ui/tests/views/ContainersView.applyContainerPatch.spec.ts
  • ui/src/locales/ko/appShell.json
  • ui/src/locales/uk/appShell.json
  • ui/src/locales/vi/appShell.json
  • ui/src/locales/zh-CN/appShell.json
  • ui/tests/views/ContainersView.spec.ts
  • ui/tests/composables/useOperationDisplayHold.spec.ts
  • ui/tests/views/containers/useContainerLogs.spec.ts
  • ui/src/locales/fr/appShell.json
  • ui/src/components/containers/UpdateMaturityBadge.vue
  • ui/src/locales/de/appShell.json
  • ui/src/locales/es/appShell.json
  • ui/src/locales/nl/appShell.json
  • ui/tests/views/dashboard/useDashboardComputed.spec.ts
  • ui/src/locales/ru/appShell.json
  • ui/src/locales/tr/appShell.json
  • ui/src/locales/ar/appShell.json
  • ui/tests/views/DashboardView.spec.ts
  • ui/src/locales/it/appShell.json
  • ui/tests/composables/useVulnerabilities.spec.ts
🚧 Files skipped from review as they are similar to previous changes (67)
  • ui/src/locales/en/appShell.json
  • ui/tests/views/RegistriesView.spec.ts
  • ui/src/components/containers/UpdateStatusPanel.vue
  • ui/src/utils/update-kind-labels.ts
  • app/tag/family.test.ts
  • ui/src/components/containers/containersViewTemplateContext.ts
  • ui/src/layouts/AppLayout.vue
  • ui/tests/components/containers/ContainerFullPageTabContent.spec.ts
  • ui/src/locales/en/sharedComponents.json
  • ui/tests/directives/tooltip.spec.ts
  • ui/src/components/DataTableColumnPicker.vue
  • ui/src/components/containers/ContainerFullPageOverviewTab.vue
  • ui/tests/components/containers/ContainerFullPageDetail.spec.ts
  • app/tag/family.ts
  • ui/src/components/containers/ContainersListContent.vue
  • ui/tests/utils/update-eligibility.spec.ts
  • ui/tests/utils/dashboard-container-metrics.spec.ts
  • app/model/maturity-policy.test.ts
  • ui/src/locales/uk/containerComponents.json
  • ui/src/types/container.d.ts
  • ui/tests/components/containers/ContainersListContent.spec.ts
  • ui/src/views/containers/useContainerPolicy.ts
  • ui/src/views/RegistriesView.vue
  • ui/tests/views/containers/useContainerPolicy.spec.ts
  • ui/tests/composables/useUpdateStatus.spec.ts
  • ui/src/utils/dashboard-container-metrics.ts
  • ui/src/utils/display.ts
  • ui/src/utils/update-maturity.ts
  • ui/tests/boot/i18n.spec.ts
  • ui/tests/utils/update-kind-labels.spec.ts
  • ui/src/locales/ko/containerComponents.json
  • ui/src/locales/zh-TW/containerComponents.json
  • ui/tests/utils/display.spec.ts
  • app/model/container.ts
  • ui/tests/components/DataTableColumnPicker.spec.ts
  • ui/src/locales/ar/containerComponents.json
  • app/watchers/providers/docker/tag-candidates.ts
  • app/model/maturity-policy.ts
  • ui/tests/components/containers/ContainerFullPageOverviewTab.spec.ts
  • ui/tests/components/containers/UpdateStatusPanel.spec.ts
  • ui/src/locales/vi/containerComponents.json
  • ui/src/locales/pl/containerComponents.json
  • ui/src/utils/update-eligibility.ts
  • ui/src/locales/en/containerComponents.json
  • app/model/container.test.ts
  • app/model/update-eligibility.test.ts
  • ui/tests/components/ContainerSideTabContent.spec.ts
  • ui/src/locales/ru/containerComponents.json
  • ui/src/locales/it/containerComponents.json
  • ui/src/directives/tooltip.ts
  • ui/src/components/containers/ContainerFullPageDetail.vue
  • ui/src/components/containers/ContainerFullPageTabContent.vue
  • ui/src/utils/container-mapper.ts
  • ui/src/locales/zh-CN/containerComponents.json
  • ui/src/locales/nl/containerComponents.json
  • ui/src/locales/de/containerComponents.json
  • ui/tests/utils/update-maturity.spec.ts
  • ui/src/locales/pt-BR/containerComponents.json
  • app/model/update-eligibility.ts
  • ui/src/locales/ja/containerComponents.json
  • ui/src/locales/fr/containerComponents.json
  • ui/src/components/containers/ContainerSideTabContent.vue
  • ui/tests/components/containers/ContainersGroupedViews.spec.ts
  • ui/src/composables/useUpdateStatus.ts
  • ui/src/locales/es/containerComponents.json
  • ui/src/views/ContainersView.vue
  • ui/src/components/containers/ContainersGroupedViews.vue

@scttbnsn
scttbnsn merged commit 8887c03 into dev/v1.6 Jul 21, 2026
5 checks passed
@scttbnsn
scttbnsn deleted the feat/v1.6-display-honesty branch July 21, 2026 13:29
scttbnsn added a commit that referenced this pull request Jul 21, 2026
Release plumbing for the v1.6.0-rc.3 cut — the full identity bump the
guard tests enforce.

- Dated `[1.6.0-rc.3] — 2026-07-21` CHANGELOG heading, fresh empty
`[Unreleased]` section, matching link-reference definitions
- rc.3 across README badge/highlights summary, website site-config +
timeline, updates page (new rc.3 highlights section), API doc example
payloads, quickstart tag matrix, and the demo runtime fixtures
- Guard tests updated in lockstep: release-identity,
release-docs-identity (compare base now `v1.6.0-rc.2...v1.6.0-rc.3`),
changelog-links

rc.3 delta over rc.2: #566 display-honesty batch, #567 security
hardening, #568 maturity-clock restart fix (#565), #569 docs freshness.

Verified: 105/105 script tests, 43/43 web-script tests, full pre-push
gate green.

Last dev-side change before the release head freeze.

<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->

## Changelog

🔧 **Changed**
- Updated release identity from `v1.6.0-rc.2` to `v1.6.0-rc.3` across
README, website metadata, roadmap, docs, API examples, quickstart tags,
and demo fixtures.
- Added the dated `v1.6.0-rc.3` updates section and refreshed changelog
link references.
- Updated release identity and documentation guard tests, including rc
comparison ranges and candidate fixtures.

✨ **Added**
- Documented the rc.3 changes covering issues `#566`, `#567`,
`#568/`#565, and `#569`.

🔒 **Security**
- Documented the rc.3 security hardening batch.

## Verification

- Script tests: 105/105 passed
- Web-script tests: 43/43 passed
- Full pre-push gate: passed

## Concerns

- Verify the release date `July 21, 2026` is correct.
- Confirm all remaining `1.6.0-rc.2` references are intentional.

<!-- end of auto-generated comment: release notes by coderabbit.ai -->
scttbnsn added a commit that referenced this pull request Jul 21, 2026
Promotes the v1.6.0-rc.3 content from `dev/v1.6` to `main` for the
release cut.

rc.3 delta over rc.2:
- **#566 display-honesty batch** — update-status vocabulary ("Digest
update", Unknown badge, "Security hold"), pin glyph driven by the real
pin-gate verdict (`tagPinGated`), insight-only pinned rows read Current
everywhere (table + detail panel), one-clock maturity panel,
column-picker auto-hide annotations, registries deep-link fix (#556),
banner pluralization, tooltip freshness.
- **#567 security hardening** — anonymous auth fails closed on upgrades,
HTTP trigger SSRF guards, full WebSocket origin validation,
`drydock.sid` session cookie, owner-only store perms, pinned icon CDN
revisions, CI secret scans.
- **#568 maturity-clock restart fix** (#565) — the soak clock restarts
only when the update candidate identity changes.
- **#569 docs freshness** — auth/SSRF/WS/UI copy corrected to shipped
v1.6 behavior.
- **#571 release identity bump** — dated CHANGELOG heading, rc.3 across
all public version surfaces, guard tests in lockstep.

After merge: dispatch `release-cut.yml` from `main` with
`release_tag=v1.6.0-rc.3`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant