Cycles
Design · c54

Policy Enforcement Page

archivedcreated 2026-07-22· last activity 2026-09-11

Design-site mock of the v2 Policy & Enforcement page from Figma — tabbed policy-control table built on UxTableList

Why

The Figma file has a v2 design for the app's Policy & Enforcement page (v2/policy-and-enforcement, two modes: Enforcement Mode & Alerting, Compliance Framework Coverage). The existing design-site mock (/pages/policy-enforcement, from the pre-v2 layout) uses SecurityPolicyCards; the new design replaces that with a three-column policy-control table with an inheritance cascade (Global → section → policy, overridable at each level). Transcribing it onto the design site pressure-tests UxTableList with fully custom cell components — a first for a design-site page — and surfaces whatever new primitives the v2 direction needs (dashed override button, inherited-value cells, tab strip).

Objective

A new page mock at /pages/policy-enforcement-v2 faithfully reproducing both Figma modes as tabs, using UxTableList for the tables with custom components in row cells. Existing policy-enforcement.vue and compliance.vue mocks stay untouched. Done = both tabs render the Figma layouts with static inline data, page registered in page-registry.ts, and new-component candidates identified for possible layer extraction.

Scope

In

  • app/pages/pages/policy-enforcement-v2.vue — new page, both tabs
  • Local cell components (proposed app/components/policy/): PolicyControlCell, InheritedModeCell, AlertingCell
  • app/data/page-registry.ts — register the new page (security group)
  • Figma extraction: get_design_context on both frames; framework-coverage frame node-id still to capture (see research.md)

Out

  • Replacing/retiring the existing policy-enforcement.vue or compliance.vue mocks
  • Layer extraction of new components (identify candidates only)
  • Real interactivity beyond tab switching and cosmetic toggles — this is a static mock

Plan

  1. Extract design contextget_design_context on both frames for exact spacing, colors, text.
  2. Build the enforcement-mode tab — page skeleton (h1, source line, intro, flagged banner), tab strip (UxFilterGroup or custom), UxTableList with 3-column header, then row components: Global row, section rows, policy rows with toggle/description/Details-link/Beta pill, inherited-mode + dashed + Override cells, alerting cells with UxSelect overrides.
  3. Build the framework-coverage tab — framework filter tags (+ Add Framework), one 5-column UxTableList (Control + 4 framework columns) with completeness row, section rows, and Required/! Required/ coverage cells with ✔ Active. See research.md for anatomy.
  4. Register + polishpage-registry.ts entry, check against screenshots, note extraction candidates.

Key Changes

  • Built app/pages/pages/policy-enforcement-v2.vue — both tabs, static data inlined, first design-site page to compose UxTableList directly (custom tab strip; UxFilterGroup didn't match the joined-segment Figma look)
  • Added 5 local cell components in app/components/policy/: PolicySummaryCell (toggle + name + Details link + Beta pill), PolicyValueSelect (grape select + Configure.. + ⊕/spacer), PolicyOverrideStack (inherited label + dashed Override), PolicyCoverageCell (Required/! Required/dash + ✔ Active), PolicyCompletenessCell (mini bar + 84% · 14/18 · Controls)
  • Registered policy-enforcement-v2 in app/data/page-registry.ts (security group)
  • Live search filter wired through UxTableListColumnHeader's metric.search on the Policy Control column
  • Layer finding: UxTableList puts its row slot directly inside <table> with no <tbody> — SSR hydration mismatch (browser inserts implicit tbody), which corrupted patching and broke v-if tab switching. Worked around by wrapping page rows in an explicit <tbody>; a proper fix belongs in the layer (wrap the default slot in <tbody>), likely alongside SecurityViolationsTable et al.
  • Verified via puppeteer DOM assertions: tab 1 = 13 Override buttons / 5 selects / 2 Beta pills / 1 toggle off; tab 2 = 14 Required / 4 ! Required / 4 completeness cells / 4 framework tags; zero console warnings or errors
  • Extended the mock from the Figma's 2 sections to 4 c17-family categories: added Data Protection (Credential Redaction, Egress Allow-List, TLS Floor) and Input & Output Integrity (Prompt Inspection, Output Validation, Provenance Pinning — Beta), per the taxonomy prior-art mapping in research.md. Replaced the Figma's repeated placeholder description with real per-policy one-liners. Re-verified: 12 policies, 27 Override buttons / 7 selects / 4 Beta pills on tab 1; 28 Required / 8 ! Required on tab 2; console clean
  • Extracted all 5 components to the layer as the new policy group (q-nuxt-layer/components/policy/{SummaryCell,ValueSelect,OverrideStack,CoverageCell,CompletenessCell}.vue, auto-import names unchanged); deleted the local copies. Fixed the UxTableList tbody bug in the layer (default slot now wrapped in <tbody>) and removed the page's workaround. Design dev server now runs with NUXT_LOCAL_LAYER=1. Regression-checked /pages/policy-detail and the UxTableList demo — clean.
  • Documented all 5 on the design site: new policy group (order 3.25, after Security) in component-registry.ts with full props/coverage entries + 5 interactive demos in _demos/ (adminProps + scenarios). Verified all detail pages and the coverage dashboard render. Added the group to the layer's CLAUDE.md catalog. Goes out with the pending 0.6.0 release.
  • Enforcement tab: moved "Details + Configuration" into its own 4th Details column (Alerting narrowed 432→300px). PolicySummaryCell gained a detailsLink prop (default true) to suppress the in-cell link; coverage tab unchanged. Demo + registry updated.
  • Layer: UxTableListSorter width now defaults to '1%' (shrink-to-content); PolicySummaryCell description wraps with the cell (whitespace-normal, dropped fixed max-width). Mock drops all manual column widths — sorter columns hug content, Policy Control absorbs the rest. Checked ViolationsTable (explicit widths, unaffected) and the Sorter demo.
  • Extracted the tab strip as UxTabGroup (ux/TabGroup.vue): options + v-model, always-one-selected, #right action slot. Page swapped over; documented (interactive group + demo + layer CLAUDE.md). Mark redesigned PolicySummaryCell (toggle-label name, right-aligned Beta, no in-cell link) and renamed the Details link to "Manage" — cleaned up the orphaned detailsLink prop across component/page/registry/demo.

Outcome

/pages/policy-enforcement-v2 shipped with both Figma modes as tabs — the first design-site page composing UxTableList directly with fully custom cells. Five policy cell components extracted to the layer as the new policy group (SummaryCell, ValueSelect, OverrideStack, CoverageCell, CompletenessCell) plus the tab strip as UxTabGroup, all documented with demos. Also fixed a real layer bug the cycle uncovered: UxTableList rendered its row slot without <tbody>, causing an SSR hydration mismatch that broke v-if tab switching. Mock extended to four c17-family categories with real per-policy copy; verified via puppeteer DOM assertions.

Figma

  • File: AGxdHnJ1cf847C7WRuybch (app.qpoint.io — 1.1 — Agent Transition)
  • v2/policy-and-enforcement -- enforcement-mode: node 12556:29387 (screenshot in cycle dir)
  • v2/policy-and-enforcement -- framework coverage: node 12447:23368, page 6135:20195 (screenshot in cycle dir)

Visual record

4 captures — heroes, page slices, and full-page renders. Click to view.

Audit-mode enforcement dry-run — egress, tool/MCP, human-in-the-loop, kill switch
Audit-mode enforcement dry-run — egress, tool/MCP, human-in-the-loop, kill switch
c54 hero
c54 full
c54 full

In the archive

Artifacts

  • initial-prompt.md
  • build-spec.md
  • initial-prompt.md
  • research.md

Qpoint Brand Style Guide