Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 55 additions & 0 deletions .github/workflows/hr-workspace-validation-dashboard.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: HR Workspace Validation Dashboard State Quality

on:
pull_request:
branches:
- develop
- feat/hr-workspace-protected-read-state
paths:
- "apps/hr-workspace/validation-dashboard-state.js"
- "apps/hr-workspace/validation-dashboard-state.css"
- "apps/hr-workspace/validation-dashboard-state.stories.js"
- "tests/hr-workspace-validation-dashboard.test.mjs"
- "docs/traceability/hr-workspace-validation-dashboard.md"
- "docs/doctoring/hr-workspace-validation-dashboard-accessibility-references.md"
- ".github/workflows/hr-workspace-validation-dashboard.yml"
workflow_dispatch:

permissions:
contents: read

concurrency:
group: hr-workspace-validation-dashboard-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
validation-dashboard:
name: Validation dashboard state contract
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout exact candidate
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ github.event.pull_request.head.sha || github.sha }}
persist-credentials: false
- name: Prove exact candidate checkout
env:
ORGMETRA_EXPECTED_HEAD_SHA: ${{ github.event.pull_request.head.sha || github.sha }}
run: test "$(git rev-parse HEAD)" = "$ORGMETRA_EXPECTED_HEAD_SHA"
- name: Set up Node.js LTS
uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version: "24"
check-latest: false
- name: Run validation-dashboard accessibility contract with exact coverage
run: >-
node --test --experimental-test-coverage
--test-coverage-lines=100
--test-coverage-branches=100
--test-coverage-functions=100
tests/hr-workspace-validation-dashboard.test.mjs
- name: Require clean checkout
run: |
git diff --exit-code
test -z "$(git status --porcelain)"
64 changes: 64 additions & 0 deletions apps/hr-workspace/validation-dashboard-state.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
.validation-dashboard-state {
display: grid;
gap: var(--orgmetra-space-md);
max-width: 42rem;
padding: var(--orgmetra-space-lg);
border: 1px solid var(--orgmetra-border-subtle);
border-radius: var(--orgmetra-radius-md);
background: var(--orgmetra-surface-card);
color: var(--orgmetra-text-primary);
}

.validation-dashboard-status,
.validation-dashboard-exact-values,
.validation-dashboard-next-action {
display: grid;
gap: var(--orgmetra-space-xs);
margin: 0;
}

.validation-dashboard-status span,
.validation-dashboard-exact-values span,
.validation-dashboard-next-action span {
color: var(--orgmetra-text-muted);
}

.validation-dashboard-state[data-interaction-state="read-only"] {
border-color: var(--orgmetra-border-subtle);
}

.validation-dashboard-state[data-interaction-state="permission-denied"],
.validation-dashboard-state[data-interaction-state="validation-error"],
.validation-dashboard-state[data-interaction-state="error"] {
border-color: var(--orgmetra-danger);
}

.validation-dashboard-state[aria-busy="true"] {
cursor: progress;
}

.validation-dashboard-action {
justify-self: start;
min-height: 44px;
padding: var(--orgmetra-space-sm) var(--orgmetra-space-md);
border: 0;
border-radius: var(--orgmetra-radius-sm);
background: var(--orgmetra-action-review);
color: #fff;
font: inherit;
cursor: pointer;
}

.validation-dashboard-action:hover:not(:disabled) {
opacity: 0.88;
}

.validation-dashboard-action:disabled {
cursor: not-allowed;
opacity: 0.62;
}

.validation-dashboard-action:focus-visible {
outline: 3px solid var(--orgmetra-focus-ring);
outline-offset: 3px;
}
83 changes: 83 additions & 0 deletions apps/hr-workspace/validation-dashboard-state.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
const STATE_MODELS = Object.freeze({
idle: Object.freeze({
ariaBusy: 'false', ariaLive: 'polite', role: 'status', actionDisabled: false,
exactValueTableRequired: false, interactionState: 'default', actionLabel: 'Load validation evidence',
label: 'Review validation dashboard',
message: 'Load fresh purpose-authorized governed validation evidence for the current validation scope.',
nextAction: 'Load the current governed validation evidence before relying on this dashboard.',
}),
loading: Object.freeze({
ariaBusy: 'true', ariaLive: 'polite', role: 'status', actionDisabled: true,
exactValueTableRequired: false, interactionState: 'loading', actionLabel: 'Loading validation evidence',
label: 'Loading validation evidence',
message: 'Orgmetra is resolving the authorized study, criterion, metric, and provenance references for this purpose-bound read.',
nextAction: 'Wait for the governed validation-evidence read to finish.',
}),
ready: Object.freeze({
ariaBusy: 'false', ariaLive: 'polite', role: 'status', actionDisabled: true,
exactValueTableRequired: true, interactionState: 'read-only', actionLabel: 'Validation evidence loaded',
label: 'Validation dashboard ready',
message: 'This is read-only governed validation evidence. It does not establish causality and does not rank, reject, advance, or authorize an employment decision.',
nextAction: 'Read the exact-value table alongside every chart, then open the separately governed study evidence needed for accountable human interpretation.',
}),
empty: Object.freeze({
ariaBusy: 'false', ariaLive: 'polite', role: 'status', actionDisabled: false,
exactValueTableRequired: false, interactionState: 'read-only', actionLabel: 'Reload validation evidence',
label: 'No validation evidence is visible here',
message: 'No governed validation evidence is visible in this authorized scope. This is not evidence that no governed validation evidence exists elsewhere.',
nextAction: 'Check the authorized validation scope and reload if another permitted evidence view is required.',
}),
denied: Object.freeze({
ariaBusy: 'false', ariaLive: 'assertive', role: 'alert', actionDisabled: false,
exactValueTableRequired: false, interactionState: 'permission-denied', actionLabel: 'Review access',
label: 'Validation evidence access denied',
message: 'The current actor or HR purpose does not permit this governed validation-evidence read.',
nextAction: 'Check the HR purpose and validation-evidence access authority before requesting validation evidence again.',
}),
stale: Object.freeze({
ariaBusy: 'false', ariaLive: 'assertive', role: 'alert', actionDisabled: false,
exactValueTableRequired: false, interactionState: 'validation-error', actionLabel: 'Reload validation evidence',
label: 'Validation evidence is stale',
message: 'The governed study, criterion, metric, or provenance evidence changed before this dashboard could be relied on.',
nextAction: 'Reload the current governed validation evidence and its evidence version before continuing interpretation.',
}),
scopeBlocked: Object.freeze({
ariaBusy: 'false', ariaLive: 'assertive', role: 'alert', actionDisabled: false,
exactValueTableRequired: false, interactionState: 'validation-error', actionLabel: 'Narrow validation scope',
label: 'Validation evidence scope is not authorized',
message: 'One or more requested validation fields or study scopes fall outside the current purpose-bound authorization.',
nextAction: 'Narrow the requested validation scope to the authorized evidence set or obtain the required HR access before retrying.',
}),
error: Object.freeze({
ariaBusy: 'false', ariaLive: 'assertive', role: 'alert', actionDisabled: false,
exactValueTableRequired: false, interactionState: 'error', actionLabel: 'Retry governed read',
label: 'Validation evidence unavailable',
message: 'The governed validation-evidence read did not return usable authoritative evidence.',
nextAction: 'Do not infer selection validity or workforce impact from cached or partial data; verify the governed validation evidence source and authorization before retrying.',
}),
});

function requireExactState(value) {
if (typeof value !== 'string') {
throw new TypeError('validation-dashboard state must be an exact built-in string');
}
if (!Object.hasOwn(STATE_MODELS, value)) {
throw new TypeError(`unsupported validation-dashboard state: ${value}`);
}
return STATE_MODELS[value];
}

/** Return immutable accessibility semantics for one purpose-bound validation-dashboard state. */
export function validationDashboardViewModel(state) {
return requireExactState(state);
}

/** Render static Storybook evidence without accepting caller-controlled HR, candidate, or metric values. */
export function validationDashboardMarkup(state) {
const model = requireExactState(state);
const disabled = model.actionDisabled ? ' disabled' : '';
const exactValues = model.exactValueTableRequired
? 'Required alongside every chart before interpretation.'
: 'No chart is authorized in this state.';
return `<section class="validation-dashboard-state" data-figma-node-id="1:64" data-figma-component="ValidationMetric" data-interaction-state="${model.interactionState}" data-exact-value-table-required="${model.exactValueTableRequired}" aria-busy="${model.ariaBusy}">\n <p class="validation-dashboard-status" role="${model.role}" aria-live="${model.ariaLive}"><strong>${model.label}</strong><span>${model.message}</span></p>\n <p class="validation-dashboard-exact-values"><strong>Exact values</strong><span>${exactValues}</span></p>\n <p class="validation-dashboard-next-action"><strong>Next action</strong><span>${model.nextAction}</span></p>\n <button class="validation-dashboard-action" type="button"${disabled}>${model.actionLabel}</button>\n</section>`;
}
25 changes: 25 additions & 0 deletions apps/hr-workspace/validation-dashboard-state.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { validationDashboardMarkup } from './validation-dashboard-state.js';
import './validation-dashboard-state.css';

export default {
title: 'HR Workspace/Validation Dashboard States',
parameters: {
design: {
type: 'figma',
url: 'Orgmetra Baseline — Storybook Inventory node 1:64 / ValidationMetric',
},
},
};

function story(state) {
return () => validationDashboardMarkup(state);
}

export const Idle = story('idle');
export const Loading = story('loading');
export const ReadyReadOnly = story('ready');
export const EmptyReadOnly = story('empty');
export const PermissionDenied = story('denied');
export const StaleEvidence = story('stale');
export const EvidenceScopeBlocked = story('scopeBlocked');
export const Error = story('error');
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Validation dashboard interaction and evidence references

Reviewed: 2026-08-28 (Asia/Seoul).

This note records primary standards used by the active Validation dashboard presentation slice. It is engineering/design evidence only. It does not claim WCAG certification, professional validation of any selection procedure, or that a dashboard summary establishes causal evidence.

## Primary standards

American Educational Research Association, American Psychological Association, & National Council on Measurement in Education. (2014). *Standards for educational and psychological testing*. American Educational Research Association. https://www.testingstandards.net/open-access-files.html

Society for Industrial and Organizational Psychology. (2018). Principles for the validation and use of personnel selection procedures. *Industrial and Organizational Psychology, 11*(S1), 1–97. https://doi.org/10.1017/iop.2018.195

World Wide Web Consortium. (2023, October 5). *Web Content Accessibility Guidelines (WCAG) 2.2*. https://www.w3.org/TR/WCAG22/

World Wide Web Consortium. (2023, June 6). *Accessible Rich Internet Applications (WAI-ARIA) 1.2*. https://www.w3.org/TR/wai-aria-1.2/

## Design consequences

- Validation evidence is presented as read-only evidence for accountable human interpretation; the UI does not convert a coefficient, interval, fairness metric, monitoring signal, or model-generated draft into selection or employment-decision authority.
- A chart never stands alone: the Figma `ValidationMetric` contract requires an exact-value table alongside every chart so the graphical encoding is not the sole carrier of magnitude or uncertainty evidence.
- The presentation explicitly avoids causal language. A descriptive or predictive association shown in a dashboard does not by itself establish a causal effect.
- Loading is exposed through `aria-busy` and disables duplicate requests while purpose-bound evidence is being resolved.
- Denied, stale, scope-blocked, and error states use alert semantics plus a concrete next action rather than widening scope, relying on cached evidence, or silently dropping unavailable evidence.
- Read-only state payloads are value-minimized: candidate/Person identifiers, raw scores, validity coefficients, p-values, intervals, fairness ratios, compensation, credentials, prompts, and model output are not embedded in generic interaction-state evidence.
- The interactive action retains a visible `:focus-visible` treatment and a 44-pixel minimum target height using existing Orgmetra design tokens.
- Fresh Figma `Orgmetra Baseline` Storybook Inventory node `1:64` was read on 2026-08-28. It lists `ValidationMetric` and continues to require default, hover, focus, disabled, loading, validation-error, read-only, and high-risk-confirmation states, with exact-value tables accompanying every chart.

The active PR must still be retargeted and revalidated after its parent integrates; focused child evidence is not shipped-product or statistical-validity evidence.
39 changes: 39 additions & 0 deletions docs/traceability/hr-workspace-validation-dashboard.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# HR Workspace Validation dashboard traceability

Status: **active PR evidence only**. This document does not change protected-main shipped truth or certify any selection procedure.

## Ownership boundary

- Parent PR #130 owns the shared HR Workspace protected-read interaction semantics and Figma/Storybook accessibility contract.
- This dependency-first child owns only the Validate / `ValidationMetric` presentation-state shell.
- Existing Orgmetra validity-study, criterion, Job-scope, selection-monitoring, and psychometric/statistical owners remain authoritative for their data and compute contracts. This UI does not duplicate a validity kernel, persistence boundary, monitoring service, candidate lifecycle, or employment-decision owner.
- Dedicated-writer CWL repositories remain read-only dependencies and are not mutated by this slice.

## Buyer-visible contract

The product baseline names a Validate surface and Figma `Orgmetra Baseline` Storybook Inventory node `1:64` lists `ValidationMetric`. This slice provides bounded executable states:

`idle / loading / ready / empty / denied / stale / scopeBlocked / error`.

`ready` is read-only governed evidence. It explicitly does not establish causality and does not rank, reject, advance, or authorize an employment decision. Every chart requires an exact-value table. `empty` means only that no governed validation evidence is visible inside the currently authorized scope. Denial, staleness, scope mismatch, and transport failure fail closed with a concrete next action.

The generic state payload intentionally contains no candidate/Person/Employment/Job/study identifiers, raw selection or assessment scores, validity coefficients, p-values, confidence intervals, adverse-impact ratios, ratings, compensation, credentials/tokens, prompts, or model output. Unsupported runtime values and prototype-inherited state names such as `constructor`, `toString`, and `__proto__` are rejected through exact primitive type plus own-key membership checks.

## Design and scientific evidence

Fresh Figma `Orgmetra Baseline` Storybook Inventory node `1:64` was read on 2026-08-28 and still requires default, hover, focus, disabled, loading, validation-error, read-only, and high-risk-confirmation behavior; it also states that exact-value tables accompany every chart. The implementation uses existing Orgmetra tokens and Storybook rather than parallel geometry.

WCAG 2.2, WAI-ARIA 1.2, the *Standards for Educational and Psychological Testing*, and SIOP's fifth-edition *Principles for the Validation and Use of Personnel Selection Procedures* are recorded under `docs/doctoring/hr-workspace-validation-dashboard-accessibility-references.md`. These references govern interpretation and interaction boundaries; they are not evidence that an individual study or procedure is valid.

## Verification contract

`.github/workflows/hr-workspace-validation-dashboard.yml` must:

1. check out and prove the exact candidate SHA;
2. use the reviewed Node 24 toolchain;
3. execute the focused interaction/privacy/fail-closed regression with exact 100% line, branch, and function coverage; and
4. finish with a clean checkout.

Contract-only head `2f6aec8940f557a3df93cb584648facfc068ab88` produced genuine hosted RED: run `33153931622`, job `98792136260` checked out and proved that exact SHA, set up Node 24.19.0, then failed at the focused contract with `ERR_MODULE_NOT_FOUND` because production `apps/hr-workspace/validation-dashboard-state.js` was intentionally absent.

Focused child GREEN is stack-local only. After #53 and #130 integrate, this child must be retargeted/reconciled against fresh `develop` and all applicable browser/accessibility/Foundation/Recovery/SAST/Security and central required workflows must execute again on one resulting exact head. Parent or predecessor checks/reviews never transfer.
Loading
Loading