control-center-v2
Shift the control center from infinitely-configurable controls to a compliance-driven system of pre-defined Policies — research the framework↔policy connection, then mock the policy catalog and per-policy config pages
Why
c64 consolidated qdash around the flexible control model and shipped the V5 expand-card authoring flow — and in doing so made the cost of that flexibility visible: every control is a hand-built rule (signal + clauses + decisions + actions + packs), which is more complexity and manual configuration than the product wants to put in front of an operator. c65 deliberately reverses the emphasis: the infinite configurability moves behind a curated set of pre-defined Policies — narrow in scope, activated/deactivated with a toggle, each with focused context-specific configuration (e.g. the egress allow-list policy gets a page for managing the list). The /policy page had already started down this path; we veer back toward it, minus its enforcement-mode/violation-handling cascade (that becomes a global function, out of scope here). Compliance frameworks are the driver: frameworks are toggled globally (as on /compliance today) and determine global health and security posture; policies exist to satisfy them.
Vocabulary decision (2026-08-12): the pre-defined units are Policies, a distinct concept sitting above the flexible Controls engine — reintroducing the word c64's IA pass retired, now with a real distinction to carry (policy = curated, narrow, toggleable unit; control = underlying executable rule).
Objective
Four deliverables: (1) research-frameworks.md — in-depth analysis of the 12-framework union catalog (rules.rs PACKS ∪ frontend catalog): what each framework demands that is runtime-addressable, provable/attestation/out-of-scope split, and the policy mappings it implies; (2) policy-definitions.md — the canonical policy roster with stable P-ids: purpose, underlying control rule, config shape + fields, framework mappings, provenance; (3) /pages/policy-catalog — the main mock: global framework toggles + posture rollup over a categorized policy table with toggles and framework-gap emphasis; (4) first-pass config pages — one page per policy, built almost entirely from ~5 shared config-shape template components (list editor, threshold, level select, detector list, toggle + exceptions), egress allow-list as the deep exemplar. Explicitly NOT solving enforcement modes, violation handling/alerting, or qdash implementation.
Scope
cycles/c65.control-center-v2/— research-frameworks.md, policy-definitions.mdapp/pages/pages/policy-catalog.vue+app/pages/pages/policy-config-*.vue+ page-registry entriesapp/components/policy/— site-local config shell + shape components (layer extraction is a later cycle)- Read-only inputs: qcontrol
crates/qdash/ui(policy.vue,compliance.vue,framework.vue,controls.vue,GovernSheet.vue,utils/frameworks.ts),crates/qdash/src/rules.rs+db.rsseeds,docs/plan/qdash-information-architecture.md§7.5; bob-wirec17-governance.ts+c17-frameworks.ts; design c54/c56/c64 cycles - Out: qdash code changes, enforcement/violation-handling design, layer changes beyond necessity
Key Changes
- Kick-off + decisions (2026-08-12): vocabulary = Policies above Controls; framework catalog = union of 12 (rules.rs PACKS ∪ frontend catalog — adds HIPAA/GDPR and NIST CSF/ISO 27001 to one list); config pages = one page per policy leaning on ~5 shared shape templates; custom-control engine kept as a de-emphasized escape hatch.
research-frameworks.md(2026-08-12): per-framework runtime-addressability analysis for all 12 (provable/attestation/out-of-scope per IA doc §7.5,ok/partial/outfits following the OWASP LLM01–10 exemplar), a reverse view ranking policies by compliance weight (P8 Credential Protection carries 9 frameworks; P10/P1/P2/P9 next), findings R1–R6 (framework-list drift, hipaa/gdpr missing fromvalid_pack(), casing drift, crosswalk too coarse, only OWASP LLM has a requirements mapping, framework kind should shape the UI).policy-definitions.md(2026-08-12): the canonical roster — 17 policies (P1–P17) in 5 categories (adds Cost & Consumption to /policy's 4), each with purpose, executable underlying rule (rules.rs vocabulary), config shape + fields, defaults (seeded-backed → on), severity, remediation, 12-framework matrix, provenance. Config-shape taxonomy: list editor (P5/P6/P10/P14), threshold (P15/P16/P17), level select (P7/P11), detector list (P8/P9/P12/P13), toggle + exceptions (P1–P4). Exceptions replace enforcement modes — scoped, reasoned, expiring carve-outs instead of the Audit/Enforce cascade. Capability gaps fed back to qcontrol: G1 no negated set operator (allow-lists need the egressallowlistedderived-flag pattern), G2 no injection/PII detector families oncontent, G3 hipaa/gdpr unrecognized packs. Not carried forward: targets/campaigns, the /policy cascade, per-framework control toggles./pages/policy-catalog(2026-08-12): the main mock (app/pages/pages/policy-catalog.vue, registered) — 12-framework toggle grid (kind icon, live completeness bar, per-framework "N gaps — enable" bulk close), 4-tile posture rollup (posture % = active ÷ required across active frameworks), categorized policy table in the /policy toggle idiom with Enforcement/Alerting columns replaced by compliance chips (active-framework requirements, red when the policy is off = gap), mono underlying-rule line per row, Configure→ links to the config pages, quiet custom-controls footer. One shared reactive model — framework toggles, policy toggles, gaps, and posture all recompute live.- Config shell + 5 shape components (2026-08-12):
app/components/policy-config/(site-local; layer extraction deferred) —Shell(back-link, name + activation toggle, meta strip with SecuritySeverity + satisfies chips,<details>underlying-control disclosure, body dims when inactive, remediation footer),Exceptions(shared section on every policy — scope/value/reason/expiry with add/remove),ListEditor(mutates the passed reactive array so page-level flows can push into it),Threshold(ceiling/floor viadir, observed-value breach state),LevelSelect(ordered floor, below-floor blast-radius preview),DetectorList(families, per-detector toggle + severity + 24h hits, enable-all). - 17 config pages (2026-08-12):
policy-config-<slug>.vuefor P1–P17, all registered — thin shell+shape instantiations from the P-id specs. Deep exemplar =policy-config-egress-allowlist(the prompt's named case): pending non-allowlisted destinations (83-match c64 snapshot framing) with a live approve→list flow, scoped entries (global/host/agent per EgressDecisionModal). Toggle-shaped pages carry a live status band (shadow installs, static-key runs, root runs, never-ask runs). Verified: all 18 routes 200 on the running dev server, SSR content present, no stale classes. Two vocabulary fixes along the way:error-surface/warning-surfacedon't exist in the layer tokens (swapped forerror/10-style tints) and there is noarrow-lefticon (back-link uses the precedent's‹character). - Catalog iteration (2026-08-12): "Show inactive (N)" toggle on the framework strip header — hides non-activated framework cards, live count, defaults to showing all. Later same-day passes: description into its own column (4-col table), shrink-to-fit Policy/Config columns (width:1% + nowrap), align-middle rows, bare-dash empty state replaced with "N mapped frameworks — none active" text, Compliance column header renamed by Mark.
/pages/compliance-frameworks(2026-08-12): visual-semantics iteration page (compliance-frameworks.vue, registered) — the policy-catalog framework grid duplicated as baseline with the kind label dropped and ComplianceFrameworkIcon replaced by the new compliance-shield art (via<img>, own brand purples). Inactive state got its own grey-variant svg (compliance-shield-grey.svg, light half #d4d4d4 — a filter can't split the two halves). Iteration same-day: V2 "surface semantics" set added (shared state with V1) — active = white card + grape-400 border, inactive = dashed stroke-strong border onsurface-sunken-subtle(one step lighter than the page'ssurface-sunken); gaps tag → "Enable Required Policies" outline button; progress bar de-semanticized (grey-400 fill via inline style — arbitrary Tailwind classes aren't generated on this page — grape-500 only at 100%); toggle micro→tiny with-mr-3flush-right fix (UxToggle keeps its label wrapper's pl-2 + root gap-1 even with an empty label slot — layer nit worth fixing upstream).- Layer fix — UxToggle empty-label gap (2026-08-12):
q-nuxt-layer/components/ux/Toggle.vue— the label wrapper div rendered unconditionally, so a label-less toggle carried a 12px phantom on the right (wrapperpl-2+ rootgap-1as an empty flex item). Root cause of the V2-card alignment issue; the root'sinline-flexis load-bearing (track/label row, items-center, gap, reverse mode) and must stay. Fix:v-if="$slots.label"on the wrapper. The-mr-3page hack removed; labeled toggles verified unaffected. Rides along with ComplianceTag in the next layer release. /pages/policy-catalog-v2(2026-08-12): expand-row variation (policy-catalog-v2.vue, registered, non-destructive) — Config column dropped to a 3-column table; each policy row is aUxTableListExpandRowwhose card carries the underlying rule (mono, sunken box + custom-control escape hatch), the FULL framework mapping as small ComplianceTags (inactive frameworks dimmed at 40%, error state only when an active framework is unmet), severity + P-id + maturity + remediation, and the "Open config page →" link. Activation toggle wrapped in@click.stopso it doesn't fight the row expand. Framework strip/posture rollup identical to v1./pages/policy-catalog-v3(2026-08-12): inline-config variation (policy-catalog-v3.vue, registered) — direct duplicate of v2 with the expand row hosting the policy's ACTUAL config controls: shape components (<component :is>over a shape→component map — explicit imports, since resolveComponent can't see auto-imports) + the shared Exceptions section, fed by the same config snapshots the policy-config-* pages carry (CONFIGS/EXCEPTIONS keyed by P-id). Identity strip (rule · severity · P-id · maturity · escape hatch) above the config; config dims at 50% when the policy is off (the config-shell idiom). Poses the open question: do the standalone config pages survive, or is the catalog the config surface?- Gap-cluster + pill polish (2026-08-12): exclaim circle sized up to h-5 with an italic muted "Required" label after it (gap rows only); Beta pill iterated warning-amber → grey → light blue → grey body with a
#318bc4/60% blue border (Roadmap = full grey) — blue applied via style binding since one-off arbitrary color classes don't generate on pages;#318bc4is a token candidate if a layer blue ramp ever lands. - ComplianceTag gap variant (2026-08-12): third state on the tag —
gap: muted grey pill + grey shield for required-but-off, with ONE page-side exclaim circle (UxIconexclaimin a red-tinted rounded-full span) placed inline after the tag cluster rather than marking every tag. All three catalog pages swapped from:errorto:gap+ cluster exclaim; the rederrorvariant remains for loud failure contexts. Demo + registry docs updated. - ComplianceToggleCard extraction + component docs (2026-08-12): the settled framework card extracted to the layer as
compliance/ToggleCard.vue(props name/active/percent/satisfied/required/gaps/gapTitle, emits toggle + enable-gaps; shield art from layer assets, grey bar fill inline to dodge consumer-scan issues); both grids (policy-catalog,compliance-frameworksV2) now consume it — V1 stays inline as the baseline artifact. ComplianceTag gainederror(red two-tone shieldcompliance-shield-error.svg+ error border/text) andsize('small' | 'medium', UxToggle convention); small got a py bump; catalog table gap chips →<ComplianceTag size="small" error>. Both components documented: newcompliancegroup in component-registry (also fixes ComplianceFrameworkIcon's dangling group id), registry entries with coverage, interactive demos (_demos/ComplianceTag.vue,_demos/ComplianceToggleCard.vue), and a Compliance section on the /components visual index. - Layer component — ComplianceTag (2026-08-12):
q-nuxt-layer/components/compliance/Tag.vue— small reusable compliance tag: shield mark + framework name in a grape-50 pill (grape-400 border, grape-600 bold label), optionalremovable✕ emitting@remove, label via default slot. Shield asset resolved from the layer's own assets at build (?urlimport, AgentPawn pattern). Demo strip added to/pages/compliance-frameworks(static, removable, and live-wired tags that deactivate frameworks on remove). Confirmed the design dev server runs NUXT_LOCAL_LAYER (picked the component up without a release). Not yet in a layer release — run /release-layer (→ v0.9.8) before tarball consumers can use it; component not yet added to the design-site component docs. - qcontrol application (2026-08-12):
qcontrol-application.mdwritten (reuse model, hybrid persistence map, backend backlog B1–B8, supersession map). The six policy-config components extracted to the layer as controlled components (emit contracts: add/remove/change/toggle/toggle-all/floor; arbitrary classes → inline styles; Shell gained the white bg-surface content box + backTo/backLabel props + ComplianceTag satisfies chips) — design locals deleted, all 17 config pages + v3 rewired to the new contracts (v3 imports via#components), registrypolicy-configgroup + 6 entries + 6 demos added. Layer v0.9.8 released (commit 4708cf2, tag-driven CI publish, all 5 consumer lockfiles re-resolved — qcontrol's git#main ref included). qdash c13 executed:/controlsreplaced by the policy catalog (framework strip + 17-policy expand-row table + custom-controls escape section),/policydeleted, onecontrols/detail.vue(?policyId=) serving all policies, hybrid persistence (egress via/api/egress-decisions, P15/P17 thresholds via real control rule rewrites with dry-run, rest viausePolicyStatelocalStorage). Verified: 11/11 headless DOM checks, live API round-trips, 137 vitest, dist regenerated. Seeqcontrol crates/qdash/ui/cycles/c13.control-policies/CYCLE.md.
Outcome
The full arc shipped in one cycle: research (12-framework analysis + P1–P17 policy definitions with stable IDs) → design-site mocks (policy-catalog v1/v2/v3, 17 config pages, compliance-frameworks iteration surface) → layer components (ComplianceTag, ComplianceToggleCard, the policy-config six, UxToggle fix — released as v0.9.8) → qcontrol application (qdash c13: /controls IS the policy catalog, /policy retired). Backend follow-ons specified in qcontrol-application.md (B1–B8). Design-site mocks remain as the iteration surface; qdash work sits uncommitted on feat/control-center for review.
Visual record
27 captures — heroes, page slices, and full-page renders. Click to view.



























In the archive
Artifacts
- initial-prompt.md
- initial-prompt.md
- policy-definitions.md
- qcontrol-application.md
- research-frameworks.md