From 90486b897f39ffc6aa560087808651a12db9bd0f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 27 Aug 2026 05:05:44 -0700 Subject: [PATCH 01/10] test(ui): define one-time HR export delivery states --- ...r-workspace-export-delivery-state.test.mjs | 87 +++++++++++++++++++ 1 file changed, 87 insertions(+) create mode 100644 tests/hr-workspace-export-delivery-state.test.mjs diff --git a/tests/hr-workspace-export-delivery-state.test.mjs b/tests/hr-workspace-export-delivery-state.test.mjs new file mode 100644 index 000000000..54d3937c0 --- /dev/null +++ b/tests/hr-workspace-export-delivery-state.test.mjs @@ -0,0 +1,87 @@ +import assert from 'node:assert/strict'; +import { readFileSync } from 'node:fs'; +import test from 'node:test'; +import { + exportDeliveryStateMarkup, + exportDeliveryViewModel, +} from '../apps/hr-workspace/hr-export-delivery-state.js'; + +const story = readFileSync( + new URL('../apps/hr-workspace/hr-export-delivery-state.stories.js', import.meta.url), + 'utf8', +); +const css = readFileSync( + new URL('../apps/hr-workspace/hr-export-delivery-state.css', import.meta.url), + 'utf8', +); + +test('review state requires explicit high-risk confirmation before one-time delivery', () => { + const review = exportDeliveryViewModel('review'); + assert.equal(review.interactionState, 'high-risk-confirmation'); + assert.equal(review.ariaBusy, 'false'); + assert.equal(review.confirmDisabled, false); + assert.equal(review.publishDisabled, true); + assert.equal(review.role, 'status'); + assert.match(review.nextAction, /Confirm the reviewed scope/); + + const markup = exportDeliveryStateMarkup('review'); + assert.match(markup, /data-figma-node-id="1:64"/); + assert.match(markup, /data-interaction-state="high-risk-confirmation"/); + assert.match(markup, /class="hr-export-confirm"[^>]*>Confirm reviewed scope/); + assert.match(markup, /class="hr-export-publish"[^>]* disabled/); +}); + +test('publishing is perceivable and prevents duplicate confirmation or delivery', () => { + const publishing = exportDeliveryViewModel('publishing'); + assert.equal(publishing.ariaBusy, 'true'); + assert.equal(publishing.confirmDisabled, true); + assert.equal(publishing.publishDisabled, true); + assert.equal(publishing.role, 'status'); + assert.equal(publishing.nextAction, 'Wait for the current one-time delivery attempt to finish.'); + + const markup = exportDeliveryStateMarkup('publishing'); + assert.match(markup, /aria-busy="true"/); + assert.match(markup, /class="hr-export-confirm"[^>]* disabled/); + assert.match(markup, /class="hr-export-publish"[^>]* disabled/); +}); + +test('terminal delivery states never offer republish and always explain the next action', () => { + const delivered = exportDeliveryViewModel('delivered'); + assert.equal(delivered.interactionState, 'read-only'); + assert.equal(delivered.publishDisabled, true); + assert.equal(delivered.nextAction, 'Review the immutable delivery receipt. Do not send the export again.'); + + const indeterminate = exportDeliveryViewModel('indeterminate'); + assert.equal(indeterminate.role, 'alert'); + assert.equal(indeterminate.publishDisabled, true); + assert.equal(indeterminate.nextAction, 'Do not send again. Reconcile the existing delivery reference before any further action.'); + + const denied = exportDeliveryViewModel('denied'); + assert.equal(denied.role, 'alert'); + assert.equal(denied.publishDisabled, true); + assert.equal(denied.nextAction, 'Resolve purpose-bound authorization and start a new reviewed export only after approval.'); + + for (const state of ['delivered', 'indeterminate', 'denied']) { + const model = exportDeliveryViewModel(state); + const serialized = JSON.stringify(model); + assert.doesNotMatch(serialized, /employee|person|email|name|salary|compensation|token|credential|document_content/i); + const markup = exportDeliveryStateMarkup(state); + assert.match(markup, /Next action/); + assert.match(markup, /class="hr-export-publish"[^>]* disabled/); + } +}); + +test('unsupported or boxed state values fail closed before rendering', () => { + assert.throws(() => exportDeliveryViewModel('retrying'), /unsupported HR export delivery state/); + assert.throws(() => exportDeliveryViewModel(new String('review')), /exact built-in string/); +}); + +test('Storybook and styling cover the Figma-required high-risk and terminal states', () => { + for (const storyName of ['ReviewRequired', 'Publishing', 'DeliveredReadOnly', 'DeliveryIndeterminate', 'PermissionDenied']) { + assert.match(story, new RegExp(`export const ${storyName}`)); + } + assert.match(story, /exportDeliveryStateMarkup/); + assert.match(css, /data-interaction-state="high-risk-confirmation"/); + assert.match(css, /var\(--orgmetra-focus-ring\)/); + assert.match(css, /:focus-visible/); +}); From 30351aed0b59839255d21dc6cfdd50eb481cf429 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 27 Aug 2026 05:05:57 -0700 Subject: [PATCH 02/10] ci(ui): gate one-time HR export delivery states --- .../hr-workspace-export-delivery-state.yml | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 .github/workflows/hr-workspace-export-delivery-state.yml diff --git a/.github/workflows/hr-workspace-export-delivery-state.yml b/.github/workflows/hr-workspace-export-delivery-state.yml new file mode 100644 index 000000000..4562bd42c --- /dev/null +++ b/.github/workflows/hr-workspace-export-delivery-state.yml @@ -0,0 +1,55 @@ +name: HR Workspace Export Delivery State Quality + +on: + pull_request: + branches: + - develop + - feat/hr-workspace-protected-read-state + paths: + - "apps/hr-workspace/hr-export-delivery-state.js" + - "apps/hr-workspace/hr-export-delivery-state.css" + - "apps/hr-workspace/hr-export-delivery-state.stories.js" + - "tests/hr-workspace-export-delivery-state.test.mjs" + - "docs/traceability/hr-workspace-export-delivery-state.md" + - "docs/doctoring/hr-workspace-export-delivery-accessibility-references.md" + - ".github/workflows/hr-workspace-export-delivery-state.yml" + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: hr-workspace-export-delivery-state-${{ github.event.pull_request.number || github.ref }} + cancel-in-progress: true + +jobs: + export-delivery-state: + name: One-time export delivery interaction 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 one-time export interaction 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-export-delivery-state.test.mjs + - name: Require clean checkout + run: | + git diff --exit-code + test -z "$(git status --porcelain)" From 293c0d88b8768bb685e6d1fccd83d0bceeb1edd7 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 27 Aug 2026 05:07:27 -0700 Subject: [PATCH 03/10] test(ui): cover confirmed one-time export readiness --- .../hr-workspace-export-delivery-state.test.mjs | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/tests/hr-workspace-export-delivery-state.test.mjs b/tests/hr-workspace-export-delivery-state.test.mjs index 54d3937c0..6cf416c9a 100644 --- a/tests/hr-workspace-export-delivery-state.test.mjs +++ b/tests/hr-workspace-export-delivery-state.test.mjs @@ -31,6 +31,21 @@ test('review state requires explicit high-risk confirmation before one-time deli assert.match(markup, /class="hr-export-publish"[^>]* disabled/); }); +test('confirmed state enables exactly one backend handoff while keeping confirmation locked', () => { + const ready = exportDeliveryViewModel('ready'); + assert.equal(ready.interactionState, 'default'); + assert.equal(ready.ariaBusy, 'false'); + assert.equal(ready.confirmDisabled, true); + assert.equal(ready.publishDisabled, false); + assert.match(ready.message, /backend must still revalidate authorization/i); + assert.equal(ready.nextAction, 'Start one audited one-time delivery attempt.'); + + const markup = exportDeliveryStateMarkup('ready'); + assert.match(markup, /class="hr-export-confirm"[^>]* disabled/); + assert.match(markup, /class="hr-export-publish"[^>]*>Start one-time delivery/); + assert.doesNotMatch(markup, /class="hr-export-publish"[^>]* disabled/); +}); + test('publishing is perceivable and prevents duplicate confirmation or delivery', () => { const publishing = exportDeliveryViewModel('publishing'); assert.equal(publishing.ariaBusy, 'true'); @@ -77,7 +92,7 @@ test('unsupported or boxed state values fail closed before rendering', () => { }); test('Storybook and styling cover the Figma-required high-risk and terminal states', () => { - for (const storyName of ['ReviewRequired', 'Publishing', 'DeliveredReadOnly', 'DeliveryIndeterminate', 'PermissionDenied']) { + for (const storyName of ['ReviewRequired', 'ConfirmedReady', 'Publishing', 'DeliveredReadOnly', 'DeliveryIndeterminate', 'PermissionDenied']) { assert.match(story, new RegExp(`export const ${storyName}`)); } assert.match(story, /exportDeliveryStateMarkup/); From 9df1f8f6a9e86c065967cec0b1756d214dbdee1b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 27 Aug 2026 05:07:52 -0700 Subject: [PATCH 04/10] feat(ui): implement governed one-time HR export delivery states --- apps/hr-workspace/hr-export-delivery-state.js | 102 ++++++++++++++++++ 1 file changed, 102 insertions(+) create mode 100644 apps/hr-workspace/hr-export-delivery-state.js diff --git a/apps/hr-workspace/hr-export-delivery-state.js b/apps/hr-workspace/hr-export-delivery-state.js new file mode 100644 index 000000000..0019cf061 --- /dev/null +++ b/apps/hr-workspace/hr-export-delivery-state.js @@ -0,0 +1,102 @@ +const EXPORT_DELIVERY_STATES = Object.freeze({ + review: Object.freeze({ + ariaBusy: 'false', + ariaLive: 'polite', + role: 'status', + confirmDisabled: false, + publishDisabled: true, + interactionState: 'high-risk-confirmation', + label: 'Review one-time HR export', + message: 'Confirm the approved purpose, field scope, destination class, and reviewed evidence before any delivery attempt.', + nextAction: 'Confirm the reviewed scope to prepare one audited one-time delivery attempt.', + }), + ready: Object.freeze({ + ariaBusy: 'false', + ariaLive: 'polite', + role: 'status', + confirmDisabled: true, + publishDisabled: false, + interactionState: 'default', + label: 'Reviewed export scope confirmed', + message: 'UI confirmation is complete. The backend must still revalidate authorization and reviewed export evidence before delivery.', + nextAction: 'Start one audited one-time delivery attempt.', + }), + publishing: Object.freeze({ + ariaBusy: 'true', + ariaLive: 'polite', + role: 'status', + confirmDisabled: true, + publishDisabled: true, + interactionState: 'loading', + label: 'One-time HR export delivery in progress', + message: 'Orgmetra is waiting for the authoritative delivery result. A second delivery attempt is disabled.', + nextAction: 'Wait for the current one-time delivery attempt to finish.', + }), + delivered: Object.freeze({ + ariaBusy: 'false', + ariaLive: 'polite', + role: 'status', + confirmDisabled: true, + publishDisabled: true, + interactionState: 'read-only', + label: 'One-time HR export delivered', + message: 'Delivery evidence is read-only. The completed export must not be published again.', + nextAction: 'Review the immutable delivery receipt. Do not send the export again.', + }), + indeterminate: Object.freeze({ + ariaBusy: 'false', + ariaLive: 'assertive', + role: 'alert', + confirmDisabled: true, + publishDisabled: true, + interactionState: 'error', + label: 'Delivery outcome needs reconciliation', + message: 'The delivery may already have completed. Automatic republication is disabled while the existing delivery reference is reconciled.', + nextAction: 'Do not send again. Reconcile the existing delivery reference before any further action.', + }), + denied: Object.freeze({ + ariaBusy: 'false', + ariaLive: 'assertive', + role: 'alert', + confirmDisabled: true, + publishDisabled: true, + interactionState: 'permission-denied', + label: 'HR export delivery not authorized', + message: 'The reviewed purpose or authorization is not sufficient for this one-time export delivery.', + nextAction: 'Resolve purpose-bound authorization and start a new reviewed export only after approval.', + }), +}); + +function requireExactExportDeliveryState(state) { + if (typeof state !== 'string') { + throw new TypeError('HR export delivery state must be an exact built-in string'); + } + const model = EXPORT_DELIVERY_STATES[state]; + if (!model) { + throw new TypeError(`unsupported HR export delivery state: ${state}`); + } + return model; +} + +/** + * Return value-minimized interaction semantics for one governed HR export delivery state. + * The model contains no HR payload values, credentials, identifiers, or delivery secrets. + * @param {string} state Governed export delivery state. + * @returns {Readonly} Immutable accessibility and next-action semantics. + */ +export function exportDeliveryViewModel(state) { + return requireExactExportDeliveryState(state); +} + +/** + * Render one Storybook proof for the one-time HR export delivery interaction. + * Only constant governed copy is emitted; caller-controlled HR values are not accepted. + * @param {string} state Governed export delivery state. + * @returns {string} Static HTML for the existing HR Workspace Storybook fixture. + */ +export function exportDeliveryStateMarkup(state) { + const model = requireExactExportDeliveryState(state); + const confirmDisabled = model.confirmDisabled ? ' disabled' : ''; + const publishDisabled = model.publishDisabled ? ' disabled' : ''; + return `
\n

${model.label}${model.message}

\n

Next action${model.nextAction}

\n
\n \n \n
\n
`; +} From 605dc18c9b2b415f578b58803d3d6ff32e5c026f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 27 Aug 2026 05:08:01 -0700 Subject: [PATCH 05/10] feat(ui): add HR export delivery Storybook states --- .../hr-workspace/hr-export-delivery-state.stories.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 apps/hr-workspace/hr-export-delivery-state.stories.js diff --git a/apps/hr-workspace/hr-export-delivery-state.stories.js b/apps/hr-workspace/hr-export-delivery-state.stories.js new file mode 100644 index 000000000..3de4a3c75 --- /dev/null +++ b/apps/hr-workspace/hr-export-delivery-state.stories.js @@ -0,0 +1,12 @@ +import { exportDeliveryStateMarkup } from './hr-export-delivery-state.js'; + +export default { + title: 'HR Workspace/One-time HR export delivery', +}; + +export const ReviewRequired = () => exportDeliveryStateMarkup('review'); +export const ConfirmedReady = () => exportDeliveryStateMarkup('ready'); +export const Publishing = () => exportDeliveryStateMarkup('publishing'); +export const DeliveredReadOnly = () => exportDeliveryStateMarkup('delivered'); +export const DeliveryIndeterminate = () => exportDeliveryStateMarkup('indeterminate'); +export const PermissionDenied = () => exportDeliveryStateMarkup('denied'); From 9c6b619b4fb537ee57bc7c8ec0ceb16af001ad5c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 27 Aug 2026 05:08:16 -0700 Subject: [PATCH 06/10] feat(ui): style high-risk HR export delivery states --- .../hr-workspace/hr-export-delivery-state.css | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 apps/hr-workspace/hr-export-delivery-state.css diff --git a/apps/hr-workspace/hr-export-delivery-state.css b/apps/hr-workspace/hr-export-delivery-state.css new file mode 100644 index 000000000..0d92bbde8 --- /dev/null +++ b/apps/hr-workspace/hr-export-delivery-state.css @@ -0,0 +1,69 @@ +.hr-export-delivery-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); +} + +.hr-export-status, +.hr-export-next-action { + display: grid; + gap: var(--orgmetra-space-xs); + margin: 0; +} + +.hr-export-status span, +.hr-export-next-action span { + color: var(--orgmetra-text-muted); +} + +.hr-export-delivery-state[data-interaction-state="high-risk-confirmation"] { + border-color: var(--orgmetra-action-review); +} + +.hr-export-delivery-state[data-interaction-state="permission-denied"], +.hr-export-delivery-state[data-interaction-state="error"] { + border-color: var(--orgmetra-danger); +} + +.hr-export-actions { + display: flex; + flex-wrap: wrap; + gap: var(--orgmetra-space-sm); +} + +.hr-export-confirm, +.hr-export-publish { + min-height: 44px; + padding: var(--orgmetra-space-sm) var(--orgmetra-space-md); + border: 0; + border-radius: var(--orgmetra-radius-sm); + font: inherit; + cursor: pointer; +} + +.hr-export-confirm { + background: var(--orgmetra-surface-muted); + color: var(--orgmetra-text-primary); +} + +.hr-export-publish { + background: var(--orgmetra-action-review); + color: #fff; +} + +.hr-export-confirm:disabled, +.hr-export-publish:disabled { + cursor: not-allowed; + opacity: 0.62; +} + +.hr-export-confirm:focus-visible, +.hr-export-publish:focus-visible { + outline: 3px solid var(--orgmetra-focus-ring); + outline-offset: 3px; +} From 332c24ce05eca2d5b5efd95ad2e3741036e0e229 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 27 Aug 2026 05:08:52 -0700 Subject: [PATCH 07/10] docs(ui): record export delivery accessibility references --- ...xport-delivery-accessibility-references.md | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 docs/doctoring/hr-workspace-export-delivery-accessibility-references.md diff --git a/docs/doctoring/hr-workspace-export-delivery-accessibility-references.md b/docs/doctoring/hr-workspace-export-delivery-accessibility-references.md new file mode 100644 index 000000000..244b26e9f --- /dev/null +++ b/docs/doctoring/hr-workspace-export-delivery-accessibility-references.md @@ -0,0 +1,20 @@ +# HR Workspace one-time export delivery accessibility references + +Status: active-PR research evidence for the dependency-first HR Workspace export-delivery interaction slice. This note does not claim WCAG conformance, legal compliance, production deployment, or backend authorization. + +## Design evidence reviewed + +- Existing Figma `Orgmetra Baseline` file `xu1ZK1zmtFcDep95R8oE9O`, Storybook Inventory node `1:64`, reviewed read-only on 2026-08-27. The published inventory explicitly requires `default / hover / focus / disabled / loading / validation-error / read-only / high-risk-confirmation` states. This slice reuses that requirement and does not create new Figma geometry. +- Parent PR #130 owns shared protected-read loading/disabled/error/read-only/focus semantics and Orgmetra design-token usage. This child adds only one-time HR export-specific high-risk confirmation, delivered-receipt, and do-not-republish ambiguity behavior. + +## Standards implications + +WCAG 2.2 is a W3C Recommendation. The interaction proof is designed around keyboard-operable controls with visible `:focus-visible` treatment, clear labels/instructions, explicit error/denial next actions, and programmatically exposed status changes. The high-risk export flow deliberately keeps the delivery action disabled until a distinct confirmation state, and terminal delivery/indeterminate states keep republish disabled. + +WAI-ARIA 1.2 is a W3C Recommendation used here only for interaction semantics. Non-urgent progress/read-only changes use `role=status` with polite live announcements; authorization denial and ambiguous delivery outcomes use `role=alert` with assertive announcements. `aria-busy=true` is reserved for the in-progress delivery state. These semantics do not substitute for backend authorization or immutable audit evidence. + +## APA 7 references + +World Wide Web Consortium. (2024, December 12). *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/ From 8cfeb21f2bad73a9c7a4a60b1b7597e4779f429e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 27 Aug 2026 05:09:17 -0700 Subject: [PATCH 08/10] docs(ui): trace one-time export delivery interaction safety --- .../hr-workspace-export-delivery-state.md | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 docs/traceability/hr-workspace-export-delivery-state.md diff --git a/docs/traceability/hr-workspace-export-delivery-state.md b/docs/traceability/hr-workspace-export-delivery-state.md new file mode 100644 index 000000000..1ef1f2ff1 --- /dev/null +++ b/docs/traceability/hr-workspace-export-delivery-state.md @@ -0,0 +1,47 @@ +# HR Workspace one-time export delivery interaction traceability + +## Status + +Active PR only. This interaction evidence is not shipped default-branch product truth and is not an authorization or release artifact. + +Technical dependency chain: #53 → #130 → this child. Semantic export-owner dependencies remain #75 (governed export review) and #120 (audited one-time delivery). Their checks, reviews, backend authority, and persistence evidence do not transfer into this UI branch. + +## Buyer risk closed + +A one-time HR export is a sensitive, consequential data-egress operation. The UI must not make review, delivery, retry, or reconciliation look interchangeable. This slice makes the next safe action explicit without embedding protected HR values or credentials in component-state evidence. + +| State | Interaction evidence | Safe next action | +|---|---|---| +| `review` | Figma-required high-risk confirmation; delivery disabled | Confirm the reviewed purpose/scope/destination evidence | +| `ready` | UI confirmation complete; confirmation locked; delivery handoff enabled | Start one audited one-time delivery attempt; backend still revalidates authority | +| `publishing` | `aria-busy=true`; confirmation and delivery disabled | Wait; do not duplicate the send | +| `delivered` | Read-only terminal receipt state; delivery disabled | Review immutable receipt; do not republish | +| `indeterminate` | Assertive error state; delivery disabled | Do not send again; reconcile the existing delivery reference | +| `denied` | Assertive authorization-denied state; delivery disabled | Resolve purpose-bound authorization and begin a newly reviewed export only after approval | + +## Privacy and authority boundary + +`hr-export-delivery-state.js` accepts only the governed state name and emits constant copy. It does not accept HR values, Person/Employment identifiers, document content, destination addresses, credentials, tokens, compensation, ratings, or model output. Storybook evidence therefore cannot itself exfiltrate protected HR payloads. + +The `ready` UI state is not delivery authorization. The authoritative export service must still re-resolve the exact reviewed export packet, tenant/purpose/field scope, authorization freshness, destination class, immutable audit/outbox correlation, and one-time publication semantics defined by the export owner boundaries. An ambiguous external outcome is reconciliation-only; the UI deliberately has no republish action in `indeterminate`. + +## Accessibility / Product Design mapping + +Figma `Orgmetra Baseline` node `1:64` requires high-risk confirmation, loading, disabled, read-only, error, and focus states. Parent #130 owns the shared pattern. This child reuses existing Orgmetra spacing/surface/action/focus tokens and adds only workflow-specific semantics. Focusable controls use the existing `--orgmetra-focus-ring`; busy/error status is conveyed programmatically rather than by color alone. + +The dedicated Storybook states are `ReviewRequired`, `ConfirmedReady`, `Publishing`, `DeliveredReadOnly`, `DeliveryIndeterminate`, and `PermissionDenied`. + +## Verification + +`tests/hr-workspace-export-delivery-state.test.mjs` requires: + +- high-risk confirmation before delivery; +- exactly one enabled delivery handoff state; +- duplicate confirmation/delivery prevention during publishing; +- no republish action after delivered or indeterminate outcomes; +- explicit customer next actions for denial and ambiguity; +- no protected-value vocabulary in state evidence; +- exact built-in state input semantics; and +- Storybook/Figma correlation, tokenized focus treatment, and exact 100% line/branch/function coverage in the dedicated workflow. + +After the technical parent integrates, this child must be retargeted to fresh `develop`. Before representing the interaction as commercial product truth, also refetch the current #75/#120 export contracts and rerun every applicable browser/accessibility/Foundation/SAST/Security/Recovery/central workflow on the resulting exact head. Predecessor or parent evidence never transfers. From afa976e20949d6724f95d383336296457d70f5b3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 29 Aug 2026 02:10:21 +0900 Subject: [PATCH 09/10] fix: harden export delivery state boundary --- apps/hr-workspace/hr-export-delivery-state.css | 2 +- apps/hr-workspace/hr-export-delivery-state.js | 5 ++--- .../hr-workspace-export-delivery-state.test.mjs | 17 +++++++++++++++++ 3 files changed, 20 insertions(+), 4 deletions(-) diff --git a/apps/hr-workspace/hr-export-delivery-state.css b/apps/hr-workspace/hr-export-delivery-state.css index 0d92bbde8..d9961b8c3 100644 --- a/apps/hr-workspace/hr-export-delivery-state.css +++ b/apps/hr-workspace/hr-export-delivery-state.css @@ -47,7 +47,7 @@ } .hr-export-confirm { - background: var(--orgmetra-surface-muted); + background: var(--orgmetra-surface-page); color: var(--orgmetra-text-primary); } diff --git a/apps/hr-workspace/hr-export-delivery-state.js b/apps/hr-workspace/hr-export-delivery-state.js index 0019cf061..e6ae5c0d7 100644 --- a/apps/hr-workspace/hr-export-delivery-state.js +++ b/apps/hr-workspace/hr-export-delivery-state.js @@ -71,11 +71,10 @@ function requireExactExportDeliveryState(state) { if (typeof state !== 'string') { throw new TypeError('HR export delivery state must be an exact built-in string'); } - const model = EXPORT_DELIVERY_STATES[state]; - if (!model) { + if (!Object.hasOwn(EXPORT_DELIVERY_STATES, state)) { throw new TypeError(`unsupported HR export delivery state: ${state}`); } - return model; + return EXPORT_DELIVERY_STATES[state]; } /** diff --git a/tests/hr-workspace-export-delivery-state.test.mjs b/tests/hr-workspace-export-delivery-state.test.mjs index 6cf416c9a..66ee9a0bd 100644 --- a/tests/hr-workspace-export-delivery-state.test.mjs +++ b/tests/hr-workspace-export-delivery-state.test.mjs @@ -91,12 +91,29 @@ test('unsupported or boxed state values fail closed before rendering', () => { assert.throws(() => exportDeliveryViewModel(new String('review')), /exact built-in string/); }); +test('prototype-inherited names cannot masquerade as governed export states', () => { + for (const inheritedName of ['constructor', 'toString', '__proto__']) { + assert.throws( + () => exportDeliveryViewModel(inheritedName), + /unsupported HR export delivery state/, + `${inheritedName} must fail closed at the view-model boundary`, + ); + assert.throws( + () => exportDeliveryStateMarkup(inheritedName), + /unsupported HR export delivery state/, + `${inheritedName} must fail closed before markup is emitted`, + ); + } +}); + test('Storybook and styling cover the Figma-required high-risk and terminal states', () => { for (const storyName of ['ReviewRequired', 'ConfirmedReady', 'Publishing', 'DeliveredReadOnly', 'DeliveryIndeterminate', 'PermissionDenied']) { assert.match(story, new RegExp(`export const ${storyName}`)); } assert.match(story, /exportDeliveryStateMarkup/); assert.match(css, /data-interaction-state="high-risk-confirmation"/); + assert.match(css, /var\(--orgmetra-surface-page\)/); + assert.doesNotMatch(css, /var\(--orgmetra-surface-muted\)/); assert.match(css, /var\(--orgmetra-focus-ring\)/); assert.match(css, /:focus-visible/); }); From 9f2c36dec6de64f6d7de98fb653c4e7336eed0d2 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 30 Aug 2026 09:25:26 +0900 Subject: [PATCH 10/10] fix(ui): load export delivery story styles --- apps/hr-workspace/hr-export-delivery-state.stories.js | 1 + tests/hr-workspace-export-delivery-state.test.mjs | 1 + 2 files changed, 2 insertions(+) diff --git a/apps/hr-workspace/hr-export-delivery-state.stories.js b/apps/hr-workspace/hr-export-delivery-state.stories.js index 3de4a3c75..20fa3f76a 100644 --- a/apps/hr-workspace/hr-export-delivery-state.stories.js +++ b/apps/hr-workspace/hr-export-delivery-state.stories.js @@ -1,3 +1,4 @@ +import './hr-export-delivery-state.css'; import { exportDeliveryStateMarkup } from './hr-export-delivery-state.js'; export default { diff --git a/tests/hr-workspace-export-delivery-state.test.mjs b/tests/hr-workspace-export-delivery-state.test.mjs index 66ee9a0bd..cfd7abe44 100644 --- a/tests/hr-workspace-export-delivery-state.test.mjs +++ b/tests/hr-workspace-export-delivery-state.test.mjs @@ -110,6 +110,7 @@ test('Storybook and styling cover the Figma-required high-risk and terminal stat for (const storyName of ['ReviewRequired', 'ConfirmedReady', 'Publishing', 'DeliveredReadOnly', 'DeliveryIndeterminate', 'PermissionDenied']) { assert.match(story, new RegExp(`export const ${storyName}`)); } + assert.match(story, /import ['"]\.\/hr-export-delivery-state\.css['"]/); assert.match(story, /exportDeliveryStateMarkup/); assert.match(css, /data-interaction-state="high-risk-confirmation"/); assert.match(css, /var\(--orgmetra-surface-page\)/);