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-work-capacity-review-state.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
name: HR Workspace Work Capacity Review State Quality

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

permissions:
contents: read

concurrency:
group: hr-workspace-work-capacity-review-state-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: true

jobs:
work-capacity-review-state:
name: Work-capacity review 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 work-capacity review 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-work-capacity-review-state.test.mjs
- name: Require clean checkout
run: |
git diff --exit-code
test -z "$(git status --porcelain)"
58 changes: 58 additions & 0 deletions apps/hr-workspace/work-capacity-review-state.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
.work-capacity-review-state {
display: grid;
gap: var(--orgmetra-space-md);
max-width: 40rem;
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);
}

.work-capacity-review-status,
.work-capacity-review-next-action {
display: grid;
gap: var(--orgmetra-space-xs);
margin: 0;
}

.work-capacity-review-status span,
.work-capacity-review-next-action span {
color: var(--orgmetra-text-muted);
}

.work-capacity-review-state[data-interaction-state="high-risk-confirmation"] {
border-color: var(--orgmetra-action-review);
}

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

.work-capacity-review-state[aria-busy="true"] {
cursor: progress;
}

.work-capacity-review-submit {
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;
}

.work-capacity-review-submit:disabled {
cursor: wait;
opacity: 0.62;
}

.work-capacity-review-submit:focus-visible {
outline: 3px solid var(--orgmetra-focus-ring);
outline-offset: 3px;
}
86 changes: 86 additions & 0 deletions apps/hr-workspace/work-capacity-review-state.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
const STATE_MODELS = Object.freeze({
idle: Object.freeze({
ariaBusy: 'false', ariaLive: 'polite', role: 'status', submitDisabled: false,
interactionState: 'default', actionLabel: 'Load work-capacity evidence',
label: 'Review work-capacity evidence',
message: 'Load fresh Employment, terms, and capacity-policy evidence before making a human review of a proposed contracted work-capacity change.',
nextAction: 'Load the current governed work-capacity evidence for the affected Employment.',
}),
loading: Object.freeze({
ariaBusy: 'true', ariaLive: 'polite', role: 'status', submitDisabled: true,
interactionState: 'loading', actionLabel: 'Loading work-capacity evidence',
label: 'Loading current work-capacity evidence',
message: 'Orgmetra is resolving fresh Employment, terms, and capacity-policy evidence. No cached capacity evidence is accepted.',
nextAction: 'Wait for the current work-capacity evidence load to finish.',
}),
review: Object.freeze({
ariaBusy: 'false', ariaLive: 'polite', role: 'status', submitDisabled: false,
interactionState: 'high-risk-confirmation', actionLabel: 'Confirm human review',
label: 'Work-capacity change requires human confirmation',
message: 'This is read-only evidence for human review. It does not change contracted work capacity and does not authorize compensation, scheduling, leave, or an employment decision.',
nextAction: 'Confirm the reviewed Employment, current capacity, proposed effective date, terms, and capacity-policy evidence before recording the human review.',
}),
recording: Object.freeze({
ariaBusy: 'true', ariaLive: 'polite', role: 'status', submitDisabled: true,
interactionState: 'loading', actionLabel: 'Recording human review',
label: 'Recording work-capacity review',
message: 'Orgmetra is recording human review evidence for the proposed contracted work-capacity change. Duplicate submission is disabled.',
nextAction: 'Wait for immutable work-capacity review evidence to be recorded.',
}),
recorded: Object.freeze({
ariaBusy: 'false', ariaLive: 'polite', role: 'status', submitDisabled: true,
interactionState: 'read-only', actionLabel: 'Review recorded',
label: 'Work-capacity review recorded',
message: 'The recorded review is evidence only and does not apply the work-capacity change or authorize compensation, scheduling, leave, or an employment decision.',
nextAction: 'Continue only through the separately authorized authoritative work-capacity boundary after fresh Employment and policy validation.',
}),
denied: Object.freeze({
ariaBusy: 'false', ariaLive: 'assertive', role: 'alert', submitDisabled: false,
interactionState: 'permission-denied', actionLabel: 'Review access',
label: 'Work-capacity review access denied',
message: 'The current purpose or reviewer authority does not permit this work-capacity review.',
nextAction: 'Check the access purpose and reviewer authority before trying again.',
}),
stale: Object.freeze({
ariaBusy: 'false', ariaLive: 'assertive', role: 'alert', submitDisabled: false,
interactionState: 'validation-error', actionLabel: 'Reload evidence',
label: 'Work-capacity evidence is stale',
message: 'The Employment, terms, capacity policy, or reviewed work-capacity evidence changed before review recording.',
nextAction: 'Reload authoritative Employment, terms, and capacity-policy evidence before reviewing again.',
}),
blocked: Object.freeze({
ariaBusy: 'false', ariaLive: 'assertive', role: 'alert', submitDisabled: false,
interactionState: 'validation-error', actionLabel: 'Review authoritative scope',
label: 'Work-capacity review is blocked by authoritative scope',
message: 'The proposal cannot proceed while current capacity, effective-date, Employment-status, or reviewed-policy evidence is inconsistent.',
nextAction: 'Resolve the current capacity, effective date, Employment status, and reviewed policy evidence before reviewing the change again.',
}),
error: Object.freeze({
ariaBusy: 'false', ariaLive: 'assertive', role: 'alert', submitDisabled: false,
interactionState: 'error', actionLabel: 'Retry governed load',
label: 'Work-capacity review unavailable',
message: 'The governed work-capacity evidence or immutable review service did not return a usable result. No cached work-capacity evidence is accepted.',
nextAction: 'Do not rely on cached work-capacity evidence; verify the service and authorization before retrying.',
}),
});

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

/** Return immutable, value-minimized accessibility semantics for one Employment work-capacity review state. */
export function workCapacityReviewViewModel(state) {
return requireExactState(state);
}

/** Render one static Storybook proof without accepting caller-controlled worker, capacity, or HR values. */
export function workCapacityReviewStateMarkup(state) {
const model = requireExactState(state);
const disabled = model.submitDisabled ? ' disabled' : '';
return `<section class="work-capacity-review-state" data-figma-node-id="1:64" data-interaction-state="${model.interactionState}" aria-busy="${model.ariaBusy}">\n <p class="work-capacity-review-status" role="${model.role}" aria-live="${model.ariaLive}"><strong>${model.label}</strong><span>${model.message}</span></p>\n <p class="work-capacity-review-next-action"><strong>Next action</strong><span>${model.nextAction}</span></p>\n <button class="work-capacity-review-submit" type="button"${disabled}>${model.actionLabel}</button>\n</section>`;
}
26 changes: 26 additions & 0 deletions apps/hr-workspace/work-capacity-review-state.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { workCapacityReviewStateMarkup } from './work-capacity-review-state.js';
import './work-capacity-review-state.css';

export default {
title: 'HR Workspace/Employment Work Capacity Review States',
parameters: {
design: {
type: 'figma',
url: 'Orgmetra Baseline — Storybook Inventory node 1:64',
},
},
};

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

export const Idle = story('idle');
export const Loading = story('loading');
export const HighRiskHumanReview = story('review');
export const Recording = story('recording');
export const RecordedReadOnly = story('recorded');
export const PermissionDenied = story('denied');
export const StaleEvidence = story('stale');
export const AuthoritativeScopeBlocked = story('blocked');
export const Error = story('error');
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# HR Workspace Employment work-capacity review accessibility references

Status: **active PR research/doctoring**. These references support the interaction semantics; they are not a claim of product certification, protected-`develop` availability, or compliance attestation.

## Applied decisions

- Use native button semantics and expose `aria-busy`, status/live-region, and alert semantics without turning the review surface into a custom application widget.
- Keep a proposed contracted work-capacity change behind an explicit high-risk human-confirmation state. UI confirmation records review evidence only; it does not mutate Employment truth or authorize compensation, scheduling, leave, payroll, or an employment decision.
- Disable duplicate submission while protected evidence is loading or immutable review evidence is being recorded.
- Treat stale Employment/terms/capacity-policy evidence and authoritative-scope inconsistency as fail-closed validation states with explicit next actions.
- Preserve a visible keyboard focus indicator and a minimum 44 CSS-pixel action height in the workflow-specific proof.
- Keep Storybook proof data value-minimized: no worker identifiers/contact data, capacity ratios, compensation/payroll values, leave reasons, ratings/assessment values, credentials/tokens, prompts, or model output.

## Primary final standards

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

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

## Current-status note

WCAG 2.2 remains the completed W3C Recommendation used as this slice's normative accessibility baseline. W3C continued editorial-errata work for WCAG 2.2 in 2026 without republishing the Recommendation, so this branch does not treat draft or proposed corrections as a replacement final standard. WAI-ARIA 1.2 remains the completed Recommendation used here for roles, states, and properties. This slice claims neither WCAG/ARIA conformance certification nor broader product accessibility certification.
40 changes: 40 additions & 0 deletions docs/traceability/hr-workspace-work-capacity-review-state.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# HR Workspace Employment work-capacity review state traceability

Status: **active stacked PR**. This document records the presentation/interaction contract owned by this branch. It does not claim protected-`develop` availability, accessibility certification, or authority to mutate Employment truth.

## Ownership boundary

- Parent presentation contract: #130 `feat/hr-workspace-protected-read-state@b3b30058a79174000919d566fbbb1fdad80c62bf`.
- HR Workspace product parent: #53.
- Governed Employment work-capacity review evidence: #103. This branch does not import or duplicate that unmerged backend implementation.
- Durable Employment work-capacity persistence: #128. This branch does not write its tables or bypass its future authoritative mutation boundary.
- Employment leave/separation workflows remain separate governed concepts; this UI must not infer leave, scheduling, payroll, compensation, or employment-decision authority from a work-capacity review.
- Figma correlation: `Orgmetra Baseline`, Storybook Inventory node `1:64`, freshly read on 2026-08-28. The node requires `default / hover / focus / disabled / loading / validation-error / read-only / high-risk-confirmation` states and an exact-value table alongside chart evidence where charts exist. This slice uses only the interaction-state inventory; it does not invent a parallel design system.

## State-to-governance mapping

| UI state | Interaction proof | Governance meaning | Required next action |
| --- | --- | --- | --- |
| `idle` | default | No governed evidence has been loaded. | Load fresh Employment, terms, and capacity-policy evidence. |
| `loading` | loading + disabled | Protected evidence resolution is in progress. Duplicate action is blocked. | Wait for the governed load to complete. |
| `review` | high-risk confirmation | Human review only. No Employment, compensation, scheduling, leave, or employment-decision mutation authority. | Confirm the reviewed scope and evidence before recording the review. |
| `recording` | loading + disabled | Immutable review-evidence recording is in progress; duplicate submission is blocked. | Wait for recording to complete. |
| `recorded` | read-only | Review evidence exists; no work-capacity change has been applied. | Continue only through a separately authorized authoritative work-capacity boundary after fresh validation. |
| `denied` | permission denied | Purpose or reviewer authority is insufficient. | Correct the purpose/authority before retrying. |
| `stale` | validation error | Employment, terms, capacity-policy, or reviewed evidence changed. | Reload authoritative evidence before reviewing again. |
| `blocked` | validation error | Current capacity, effective date, Employment status, or reviewed policy evidence is inconsistent. | Resolve authoritative scope conflicts before retrying. |
| `error` | error | Governed evidence/review service did not return a usable result; cached evidence is not accepted. | Verify service and authorization, then retry the governed load. |

## Privacy and integrity contract

The Storybook proof is deliberately value-minimized. Its immutable view-model vocabulary contains only presentation semantics (`ariaBusy`, `ariaLive`, `role`, `submitDisabled`, `interactionState`, `actionLabel`, `label`, `message`, `nextAction`). It must not carry Person/Employment/Assignment identifiers, worker names/contact data, current or proposed capacity ratios, compensation/payroll values, leave reasons, ratings/assessment scores, credentials/tokens, prompts, or model output.

The state input is an exact built-in string drawn from a finite vocabulary. Caller-defined boxed/string-like runtime objects fail closed before rendering. Static proof markup is correlated to Figma node `1:64`, preserves visible focus semantics, exposes busy/live-region state, uses native button semantics, and enforces a minimum 44 CSS-pixel action height.

## Verification

The dedicated `HR Workspace Work Capacity Review State Quality` workflow is intentionally scoped to this presentation slice and its documentation. It checks out the exact PR head, runs Node.js 24 tests with exact 100% line/branch/function coverage, and requires a clean checkout. This focused evidence is **stack-local** only. It does not inherit #53/#130 or #103/#128 reviews/checks and does not substitute for browser/accessibility/Foundation/Recovery/SAST/Security/central required workflows after integration.

## Integration order

Process #53 -> #130 first. After #130 integrates, retarget this child to fresh `develop`, reconcile any parent changes without transferring predecessor evidence, and rerun every applicable exact-head browser/accessibility/Foundation/Recovery/SAST/Security/central gate. High-impact UI confirmation remains review evidence only until an authoritative backend independently revalidates tenant/scope, actor separation, effective time, policy/evidence freshness, and immutable audit/outbox requirements.
Loading
Loading