feat: require complete data context header evidence (AEG-V16-018)

Make projection and watermark visible requirements, expose stale state accessibly, and add component regression coverage. Status remains IN_PROGRESS pending predecessor and UX evidence.
This commit is contained in:
2026-08-08 13:02:22 +09:00
parent 52bdf926ad
commit f2b7b40668
6 changed files with 143 additions and 24 deletions
@@ -4,7 +4,7 @@ import type { VersionSet } from '../../contracts/versionSet'
import EvidenceVersionSet from '../EvidenceVersionSet.vue'
const props = defineProps<{
title: string; asOf: string; datasetId: string; dataHash: string; modelVersion: string; configVersion: string;
codeSha: string; contractVersion: string; projectionVersion?: string; watermark?: string;
codeSha: string; contractVersion: string; projectionVersion: string; watermark: string;
stale?: boolean; correlationId?: string
}>()
const versionSet = computed<VersionSet>(() => ({
@@ -18,11 +18,11 @@ const versionSet = computed<VersionSet>(() => ({
</script>
<template>
<header class="ks-data-context" :data-stale="stale || undefined">
<div><h1>{{ title }}</h1><p>As-of {{ asOf }} <strong v-if="stale">STALE</strong></p></div>
<div><h1>{{ title }}</h1><p>As-of {{ asOf }} <strong v-if="stale" role="status" aria-label="Data is stale">STALE</strong></p></div>
<EvidenceVersionSet :value="versionSet" compact />
<dl v-if="projectionVersion || watermark || correlationId">
<template v-if="projectionVersion"><dt>Projection</dt><dd>{{ projectionVersion }}</dd></template>
<template v-if="watermark"><dt>Watermark</dt><dd>{{ watermark }}</dd></template>
<dl>
<dt>Projection</dt><dd>{{ projectionVersion }}</dd>
<dt>Watermark</dt><dd>{{ watermark }}</dd>
<template v-if="correlationId"><dt>Correlation</dt><dd>{{ correlationId }}</dd></template>
</dl>
</header>
@@ -30,7 +30,10 @@ __VLS_asFunctionalElement1(__VLS_intrinsics.h1, __VLS_intrinsics.h1)({});
__VLS_asFunctionalElement1(__VLS_intrinsics.p, __VLS_intrinsics.p)({});
(__VLS_ctx.asOf);
if (__VLS_ctx.stale) {
__VLS_asFunctionalElement1(__VLS_intrinsics.strong, __VLS_intrinsics.strong)({});
__VLS_asFunctionalElement1(__VLS_intrinsics.strong, __VLS_intrinsics.strong)({
role: "status",
'aria-label': "Data is stale",
});
}
const __VLS_0 = EvidenceVersionSet;
// @ts-ignore
@@ -42,26 +45,20 @@ const __VLS_2 = __VLS_1({
value: (__VLS_ctx.versionSet),
compact: true,
}, ...__VLS_functionalComponentArgsRest(__VLS_1));
if (__VLS_ctx.projectionVersion || __VLS_ctx.watermark || __VLS_ctx.correlationId) {
__VLS_asFunctionalElement1(__VLS_intrinsics.dl, __VLS_intrinsics.dl)({});
if (__VLS_ctx.projectionVersion) {
__VLS_asFunctionalElement1(__VLS_intrinsics.dt, __VLS_intrinsics.dt)({});
__VLS_asFunctionalElement1(__VLS_intrinsics.dd, __VLS_intrinsics.dd)({});
(__VLS_ctx.projectionVersion);
}
if (__VLS_ctx.watermark) {
__VLS_asFunctionalElement1(__VLS_intrinsics.dt, __VLS_intrinsics.dt)({});
__VLS_asFunctionalElement1(__VLS_intrinsics.dd, __VLS_intrinsics.dd)({});
(__VLS_ctx.watermark);
}
if (__VLS_ctx.correlationId) {
__VLS_asFunctionalElement1(__VLS_intrinsics.dt, __VLS_intrinsics.dt)({});
__VLS_asFunctionalElement1(__VLS_intrinsics.dd, __VLS_intrinsics.dd)({});
(__VLS_ctx.correlationId);
}
__VLS_asFunctionalElement1(__VLS_intrinsics.dl, __VLS_intrinsics.dl)({});
__VLS_asFunctionalElement1(__VLS_intrinsics.dt, __VLS_intrinsics.dt)({});
__VLS_asFunctionalElement1(__VLS_intrinsics.dd, __VLS_intrinsics.dd)({});
(__VLS_ctx.projectionVersion);
__VLS_asFunctionalElement1(__VLS_intrinsics.dt, __VLS_intrinsics.dt)({});
__VLS_asFunctionalElement1(__VLS_intrinsics.dd, __VLS_intrinsics.dd)({});
(__VLS_ctx.watermark);
if (__VLS_ctx.correlationId) {
__VLS_asFunctionalElement1(__VLS_intrinsics.dt, __VLS_intrinsics.dt)({});
__VLS_asFunctionalElement1(__VLS_intrinsics.dd, __VLS_intrinsics.dd)({});
(__VLS_ctx.correlationId);
}
// @ts-ignore
[stale, stale, title, asOf, versionSet, projectionVersion, projectionVersion, projectionVersion, watermark, watermark, watermark, correlationId, correlationId, correlationId,];
[stale, stale, title, asOf, versionSet, projectionVersion, watermark, correlationId, correlationId,];
const __VLS_export = (await import('vue')).defineComponent({
__typeProps: {},
});
@@ -0,0 +1,43 @@
import { mount } from '@vue/test-utils';
import { describe, expect, it } from 'vitest';
import KsDataContextHeader from '../KsDataContextHeader.vue';
const props = {
title: 'Decision context',
asOf: '2026-08-08T03:00:00Z',
datasetId: 'dataset-20260808',
dataHash: 'data-sha',
modelVersion: 'model-v4',
configVersion: 'config-v2',
codeSha: 'code-sha',
contractVersion: '4.0',
projectionVersion: 'projection-v7',
watermark: 'watermark-42',
correlationId: 'corr-123'
};
function mountHeader(overrides = {}) {
return mount(KsDataContextHeader, {
props: { ...props, ...overrides },
global: {
stubs: {
EvidenceVersionSet: { props: ['value', 'compact'], template: '<output :data-value="JSON.stringify(value)" />' }
}
}
});
}
describe('KsDataContextHeader', () => {
it('renders the required point-in-time and projection context', () => {
const wrapper = mountHeader();
expect(wrapper.text()).toContain('As-of 2026-08-08T03:00:00Z');
expect(wrapper.text()).toContain('Projectionprojection-v7');
expect(wrapper.text()).toContain('Watermarkwatermark-42');
expect(wrapper.text()).toContain('Correlationcorr-123');
expect(wrapper.get('output').attributes('data-value')).toContain('dataset-20260808');
expect(wrapper.get('output').attributes('data-value')).toContain('code-sha');
});
it('exposes stale data both visually and to assistive technology', () => {
const wrapper = mountHeader({ stale: true });
expect(wrapper.get('header').attributes('data-stale')).toBe('true');
expect(wrapper.get('[role="status"]').attributes('aria-label')).toBe('Data is stale');
expect(wrapper.get('[role="status"]').text()).toBe('STALE');
});
});
@@ -0,0 +1,51 @@
import { mount } from '@vue/test-utils'
import { describe, expect, it } from 'vitest'
import KsDataContextHeader from '../KsDataContextHeader.vue'
const props = {
title: 'Decision context',
asOf: '2026-08-08T03:00:00Z',
datasetId: 'dataset-20260808',
dataHash: 'data-sha',
modelVersion: 'model-v4',
configVersion: 'config-v2',
codeSha: 'code-sha',
contractVersion: '4.0',
projectionVersion: 'projection-v7',
watermark: 'watermark-42',
correlationId: 'corr-123'
}
type HeaderProps = typeof props & { stale?: boolean }
function mountHeader(overrides: Partial<HeaderProps> = {}) {
return mount(KsDataContextHeader, {
props: { ...props, ...overrides },
global: {
stubs: {
EvidenceVersionSet: { props: ['value', 'compact'], template: '<output :data-value="JSON.stringify(value)" />' }
}
}
})
}
describe('KsDataContextHeader', () => {
it('renders the required point-in-time and projection context', () => {
const wrapper = mountHeader()
expect(wrapper.text()).toContain('As-of 2026-08-08T03:00:00Z')
expect(wrapper.text()).toContain('Projectionprojection-v7')
expect(wrapper.text()).toContain('Watermarkwatermark-42')
expect(wrapper.text()).toContain('Correlationcorr-123')
expect(wrapper.get('output').attributes('data-value')).toContain('dataset-20260808')
expect(wrapper.get('output').attributes('data-value')).toContain('code-sha')
})
it('exposes stale data both visually and to assistive technology', () => {
const wrapper = mountHeader({ stale: true })
expect(wrapper.get('header').attributes('data-stale')).toBe('true')
expect(wrapper.get('[role="status"]').attributes('aria-label')).toBe('Data is stale')
expect(wrapper.get('[role="status"]').text()).toBe('STALE')
})
})