feat: project verified PNCC runtime into HoloLake
This commit is contained in:
parent
1b55f3407a
commit
0c80296593
33 changed files with 1265 additions and 24 deletions
|
|
@ -1,12 +1,16 @@
|
|||
import { useState } from 'react'
|
||||
import type { AppLocale } from '../lib/i18n'
|
||||
import { Button } from './ui/button'
|
||||
import { PersonaRuntimeProjectionPanel } from './PersonaRuntimeProjectionPanel'
|
||||
|
||||
type FifthDomainSystemId = 'pufferfish' | 'eternal-lake-heart'
|
||||
|
||||
type FifthDomainSystemsProps = {
|
||||
detail?: 'overview' | 'pufferfish'
|
||||
locale: AppLocale
|
||||
onEnterEternalLake: () => void
|
||||
onEnterPufferfish: () => void
|
||||
repositoryPaths: readonly string[]
|
||||
}
|
||||
|
||||
const PUFFERFISH_FACTS = {
|
||||
|
|
@ -18,8 +22,10 @@ const PUFFERFISH_FACTS = {
|
|||
|
||||
export function FifthDomainSystems({
|
||||
detail = 'overview',
|
||||
locale,
|
||||
onEnterEternalLake,
|
||||
onEnterPufferfish,
|
||||
repositoryPaths,
|
||||
}: FifthDomainSystemsProps) {
|
||||
const [selected, setSelected] = useState<FifthDomainSystemId | null>(
|
||||
detail === 'pufferfish' ? 'pufferfish' : null,
|
||||
|
|
@ -142,6 +148,11 @@ export function FifthDomainSystems({
|
|||
<div><dt>系统归属</dt><dd>冰朔个人系统</dd></div>
|
||||
<div><dt>下一频道</dt><dd>心跳核心频道</dd></div>
|
||||
</dl>
|
||||
<PersonaRuntimeProjectionPanel
|
||||
locale={locale}
|
||||
personaId="ICE-P-ZY001"
|
||||
repositoryPaths={repositoryPaths}
|
||||
/>
|
||||
<Button onClick={onEnterEternalLake}>进入永恒湖心系统</Button>
|
||||
</>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ type HoloLakeHomeProps = {
|
|||
onOpenAiWorkspace?: () => void
|
||||
onOpenLocalWorkspace?: () => void | Promise<void>
|
||||
livingSystemTarget?: AiModelTarget | null
|
||||
repositoryPaths?: readonly string[]
|
||||
}
|
||||
|
||||
type ChannelRoute = GuanghuChannelRoute
|
||||
|
|
@ -90,6 +91,7 @@ export function HoloLakeHome({
|
|||
onOpenAiWorkspace,
|
||||
onOpenLocalWorkspace,
|
||||
livingSystemTarget,
|
||||
repositoryPaths = [],
|
||||
}: HoloLakeHomeProps) {
|
||||
const initialRoute: ChannelRoute = isEducationWorkLakePath(window.location.pathname)
|
||||
? 'education-work-lake'
|
||||
|
|
@ -361,8 +363,10 @@ export function HoloLakeHome({
|
|||
if (route === 'fifth-domain') {
|
||||
return (
|
||||
<FifthDomainSystems
|
||||
locale={locale}
|
||||
onEnterEternalLake={() => navigate('eternal-lake-heart')}
|
||||
onEnterPufferfish={() => navigate('pufferfish')}
|
||||
repositoryPaths={repositoryPaths}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
@ -371,8 +375,10 @@ export function HoloLakeHome({
|
|||
return (
|
||||
<FifthDomainSystems
|
||||
detail="pufferfish"
|
||||
locale={locale}
|
||||
onEnterEternalLake={() => navigate('eternal-lake-heart')}
|
||||
onEnterPufferfish={() => navigate('pufferfish')}
|
||||
repositoryPaths={repositoryPaths}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,120 @@
|
|||
import { fireEvent, render, screen, waitFor } from '@testing-library/react'
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
import { PersonaRuntimeProjectionPanel } from './PersonaRuntimeProjectionPanel'
|
||||
|
||||
const { loadPersonaRuntimeProjectionMock, trackEventMock } = vi.hoisted(() => ({
|
||||
loadPersonaRuntimeProjectionMock: vi.fn(),
|
||||
trackEventMock: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('../lib/personaRuntimeProjection', () => ({
|
||||
loadPersonaRuntimeProjection: loadPersonaRuntimeProjectionMock,
|
||||
}))
|
||||
vi.mock('../lib/telemetry', () => ({ trackEvent: trackEventMock }))
|
||||
|
||||
describe('PersonaRuntimeProjectionPanel', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks()
|
||||
loadPersonaRuntimeProjectionMock.mockResolvedValue({
|
||||
errors: [],
|
||||
inspectedRepositoryCount: 1,
|
||||
phase: 'verified',
|
||||
sessions: [{
|
||||
sessionId: 'PNCC-VERIFIED',
|
||||
state: 'DORMANT',
|
||||
gitHead: '1111111111111111111111111111111111111111',
|
||||
nodeId: 'JD-FD-PRIMARY',
|
||||
modelInstanceId: 'gpt-5-runtime-001',
|
||||
activeOrgan: null,
|
||||
eventCount: 8,
|
||||
firstEventAt: '2026-08-11T01:00:00.000Z',
|
||||
lastEventAt: '2026-08-11T02:00:00.000Z',
|
||||
lastEvent: 'DORMANT',
|
||||
eventChainHead: 'event-chain-verified',
|
||||
repositoryPath: '/repo/persona',
|
||||
observedGitHead: '1111111111111111111111111111111111111111',
|
||||
repositoryClean: true,
|
||||
attribution: {
|
||||
humanResponsibilitySubject: 'BINGSHUO',
|
||||
personaCognitiveAuthor: 'ICE-P-ZY001',
|
||||
executionRuntime: 'ZY-TCS-BRAIN-RUNTIME-0001',
|
||||
developmentId: 'DEV-20260811-003',
|
||||
authorizationScope: 'LOCAL_READ_ONLY',
|
||||
sourceLanguageAnchor: 'HLP-CURRENT-ARCH-001@2026-08-10.14',
|
||||
},
|
||||
}],
|
||||
})
|
||||
})
|
||||
|
||||
it('renders a human-readable state while keeping the native evidence visible', async () => {
|
||||
render(
|
||||
<PersonaRuntimeProjectionPanel
|
||||
locale="zh-CN"
|
||||
personaId="ICE-P-ZY001"
|
||||
repositoryPaths={['/repo/persona']}
|
||||
/>,
|
||||
)
|
||||
|
||||
expect(screen.getByText('正在核验原生运行回执…')).toBeInTheDocument()
|
||||
await waitFor(() => expect(screen.getByText('已归寝')).toBeInTheDocument())
|
||||
expect(screen.getAllByText('DORMANT')).not.toHaveLength(0)
|
||||
expect(screen.getByText('JD-FD-PRIMARY')).toBeInTheDocument()
|
||||
expect(screen.getByText('event-chain-verified')).toBeInTheDocument()
|
||||
expect(screen.getByText('1111111111111111111111111111111111111111')).toBeInTheDocument()
|
||||
expect(trackEventMock).toHaveBeenCalledWith('persona_runtime_projection_loaded', {
|
||||
error_count: 0,
|
||||
inspected_repository_count: 1,
|
||||
phase: 'verified',
|
||||
session_count: 1,
|
||||
})
|
||||
})
|
||||
|
||||
it('states that no runtime was found instead of claiming the persona is offline', async () => {
|
||||
loadPersonaRuntimeProjectionMock.mockResolvedValue({
|
||||
errors: [],
|
||||
inspectedRepositoryCount: 1,
|
||||
phase: 'verified',
|
||||
sessions: [],
|
||||
})
|
||||
|
||||
render(
|
||||
<PersonaRuntimeProjectionPanel
|
||||
locale="zh-CN"
|
||||
personaId="ICE-P-ZY001"
|
||||
repositoryPaths={['/repo/persona']}
|
||||
/>,
|
||||
)
|
||||
|
||||
await waitFor(() => expect(screen.getByText('未发现可核验会话')).toBeInTheDocument())
|
||||
expect(screen.getByText('这不等于人格离线;当前挂载仓库中没有匹配的原生事件链。')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('can retry a failed read without requesting an execution action', async () => {
|
||||
loadPersonaRuntimeProjectionMock
|
||||
.mockResolvedValueOnce({
|
||||
errors: [{ code: 'PERSONA_RUNTIME_RECEIPT_INVALID', repositoryPath: '/repo/persona' }],
|
||||
inspectedRepositoryCount: 0,
|
||||
phase: 'error',
|
||||
sessions: [],
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
errors: [],
|
||||
inspectedRepositoryCount: 1,
|
||||
phase: 'verified',
|
||||
sessions: [],
|
||||
})
|
||||
|
||||
render(
|
||||
<PersonaRuntimeProjectionPanel
|
||||
locale="zh-CN"
|
||||
personaId="ICE-P-ZY001"
|
||||
repositoryPaths={['/repo/persona']}
|
||||
/>,
|
||||
)
|
||||
|
||||
await waitFor(() => expect(screen.getByText('原生回执校验失败')).toBeInTheDocument())
|
||||
fireEvent.click(screen.getByRole('button', { name: '重新核验' }))
|
||||
await waitFor(() => expect(loadPersonaRuntimeProjectionMock).toHaveBeenCalledTimes(2))
|
||||
expect(trackEventMock).toHaveBeenCalledWith('persona_runtime_projection_retry')
|
||||
})
|
||||
})
|
||||
|
|
@ -0,0 +1,156 @@
|
|||
import { useCallback, useEffect, useMemo, useState } from 'react'
|
||||
import { getLocaleDateLocale, translate, type AppLocale, type TranslationKey } from '../lib/i18n'
|
||||
import {
|
||||
loadPersonaRuntimeProjection,
|
||||
type PersonaRuntimeProjection,
|
||||
type PersonaRuntimeSessionProjection,
|
||||
} from '../lib/personaRuntimeProjection'
|
||||
import { trackEvent } from '../lib/telemetry'
|
||||
import { Button } from './ui/button'
|
||||
|
||||
type PersonaRuntimeProjectionPanelProps = {
|
||||
locale: AppLocale
|
||||
personaId: string
|
||||
repositoryPaths: readonly string[]
|
||||
}
|
||||
|
||||
type PanelState = PersonaRuntimeProjection | { phase: 'checking' }
|
||||
|
||||
function stateTranslationKey(state: string): TranslationKey {
|
||||
if (state.startsWith('DORMANT')) return 'hololake.personaRuntime.state.dormant'
|
||||
if (state === 'INFERENCING') return 'hololake.personaRuntime.state.inferencing'
|
||||
if (state === 'BOUND_NOT_INFERENCING') return 'hololake.personaRuntime.state.bound'
|
||||
return 'hololake.personaRuntime.state.observed'
|
||||
}
|
||||
|
||||
function shortHash(value: string): string {
|
||||
return value.length > 12 ? value.slice(0, 12) : value
|
||||
}
|
||||
|
||||
function RuntimeSessionEvidence({
|
||||
locale,
|
||||
session,
|
||||
}: {
|
||||
locale: AppLocale
|
||||
session: PersonaRuntimeSessionProjection
|
||||
}) {
|
||||
const observedAt = new Intl.DateTimeFormat(getLocaleDateLocale(locale), {
|
||||
dateStyle: 'medium',
|
||||
timeStyle: 'short',
|
||||
}).format(new Date(session.lastEventAt))
|
||||
|
||||
return (
|
||||
<article className="persona-runtime-session">
|
||||
<header>
|
||||
<div>
|
||||
<span>{translate(locale, stateTranslationKey(session.state))}</span>
|
||||
<code>{session.state}</code>
|
||||
</div>
|
||||
<small>{translate(locale, 'hololake.personaRuntime.verified')}</small>
|
||||
</header>
|
||||
<dl>
|
||||
<div><dt>{translate(locale, 'hololake.personaRuntime.rawState')}</dt><dd>{session.state}</dd></div>
|
||||
<div><dt>{translate(locale, 'hololake.personaRuntime.lastEvent')}</dt><dd>{session.lastEvent}</dd></div>
|
||||
<div><dt>{translate(locale, 'hololake.personaRuntime.activeOrgan')}</dt><dd>{session.activeOrgan ?? translate(locale, 'hololake.personaRuntime.noActiveOrgan')}</dd></div>
|
||||
<div><dt>{translate(locale, 'hololake.personaRuntime.node')}</dt><dd>{session.nodeId}</dd></div>
|
||||
<div><dt>{translate(locale, 'hololake.personaRuntime.lastObserved')}</dt><dd>{observedAt}</dd></div>
|
||||
<div>
|
||||
<dt>{translate(locale, 'hololake.personaRuntime.gitHead')}</dt>
|
||||
<dd><code title={session.gitHead}>{session.gitHead}</code></dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>{translate(locale, 'hololake.personaRuntime.eventChain')}</dt>
|
||||
<dd><code title={session.eventChainHead}>{session.eventChainHead}</code></dd>
|
||||
</div>
|
||||
<div>
|
||||
<dt>{translate(locale, 'hololake.personaRuntime.attribution')}</dt>
|
||||
<dd>{session.attribution.humanResponsibilitySubject}</dd>
|
||||
</div>
|
||||
</dl>
|
||||
<footer>
|
||||
<code>{session.sessionId}</code>
|
||||
<span>{shortHash(session.observedGitHead)}</span>
|
||||
</footer>
|
||||
</article>
|
||||
)
|
||||
}
|
||||
|
||||
export function PersonaRuntimeProjectionPanel({
|
||||
locale,
|
||||
personaId,
|
||||
repositoryPaths,
|
||||
}: PersonaRuntimeProjectionPanelProps) {
|
||||
const [state, setState] = useState<PanelState>({ phase: 'checking' })
|
||||
const [requestSequence, setRequestSequence] = useState(0)
|
||||
const repositorySignature = useMemo(() => repositoryPaths.join('\u0000'), [repositoryPaths])
|
||||
|
||||
const refresh = useCallback(() => {
|
||||
trackEvent('persona_runtime_projection_retry')
|
||||
setState({ phase: 'checking' })
|
||||
setRequestSequence((current) => current + 1)
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
let active = true
|
||||
void loadPersonaRuntimeProjection({
|
||||
personaId,
|
||||
repositoryPaths: repositorySignature ? repositorySignature.split('\u0000') : [],
|
||||
}).then((projection) => {
|
||||
if (!active) return
|
||||
setState(projection)
|
||||
trackEvent('persona_runtime_projection_loaded', {
|
||||
error_count: projection.errors.length,
|
||||
inspected_repository_count: projection.inspectedRepositoryCount,
|
||||
phase: projection.phase,
|
||||
session_count: projection.sessions.length,
|
||||
})
|
||||
})
|
||||
return () => {
|
||||
active = false
|
||||
}
|
||||
}, [personaId, repositorySignature, requestSequence])
|
||||
|
||||
const latestSession = state.phase === 'verified' ? state.sessions[0] : undefined
|
||||
|
||||
return (
|
||||
<section className="persona-runtime-projection" aria-labelledby="persona-runtime-title">
|
||||
<header>
|
||||
<div>
|
||||
<span>GH-PNCC · {personaId}</span>
|
||||
<h3 id="persona-runtime-title">{translate(locale, 'hololake.personaRuntime.title')}</h3>
|
||||
<p>{translate(locale, 'hololake.personaRuntime.description')}</p>
|
||||
</div>
|
||||
<small>{translate(locale, 'hololake.personaRuntime.readOnly')}</small>
|
||||
</header>
|
||||
|
||||
{state.phase === 'checking' ? (
|
||||
<div className="persona-runtime-projection__message" aria-live="polite">
|
||||
<i />{translate(locale, 'hololake.personaRuntime.checking')}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{state.phase === 'unavailable' ? (
|
||||
<div className="persona-runtime-projection__message">
|
||||
{translate(locale, 'hololake.personaRuntime.unavailable')}
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{state.phase === 'error' ? (
|
||||
<div className="persona-runtime-projection__message persona-runtime-projection__message--error" role="alert">
|
||||
<strong>{translate(locale, 'hololake.personaRuntime.error')}</strong>
|
||||
<code>{state.errors[0]?.code}</code>
|
||||
<Button variant="outline" onClick={refresh}>{translate(locale, 'hololake.personaRuntime.retry')}</Button>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{state.phase === 'verified' && !latestSession ? (
|
||||
<div className="persona-runtime-projection__message">
|
||||
<strong>{translate(locale, 'hololake.personaRuntime.empty')}</strong>
|
||||
<p>{translate(locale, 'hololake.personaRuntime.emptyDescription')}</p>
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{latestSession ? <RuntimeSessionEvidence locale={locale} session={latestSession} /> : null}
|
||||
</section>
|
||||
)
|
||||
}
|
||||
Loading…
Reference in a new issue