feat: drive public domain vestibules from validated manifests

This commit is contained in:
冰朔 2026-08-10 05:28:30 +08:00
commit 2c50b28557
7 changed files with 297 additions and 40 deletions

View file

@ -0,0 +1,45 @@
{
"schema": "gh-aios.hololake-local-stage-receipt/v1",
"receipt_id": "GH-HOLOLAKE-PUBLIC-VESTIBULE-DIRECTORY-20260810-001",
"result": "PASS_100_LOCAL_PUBLIC_VESTIBULE_DIRECTORY",
"development_id": "DEV-20260809-007",
"observed_at": "2026-08-10T05:27:00+08:00",
"source": {
"repository_id": "REPO-014",
"base_commit": "7a90fad360085ae4779179df194a7296301c5bf5",
"branch": "main"
},
"public_directory": {
"domain_count": 5,
"official_order": ["光湖主域", "光湖分域", "光湖零域", "光湖零感域", "第五域 · 光湖本源域"],
"enterprise_theme_state": "DOMAIN_TEAM_OWNED_UNPUBLISHED",
"fifth_domain_theme_state": "LAKE_REFLECTS_STARS_SELECTED_REFERENCE",
"private_resources_exposed": false,
"supported_node_types": ["local-terminal", "cloud-resident"]
},
"acceptance": {
"single_validated_public_directory": true,
"official_zero_domain_name_fixed": true,
"first_four_theme_ownership_preserved": true,
"fifth_domain_selected_reference_preserved": true,
"public_lighthouse_kept_visually_distinct": true,
"runtime_entry_gates_unchanged": true
},
"verification": {
"desktop_test_count": 55,
"desktop_test_failures": 0,
"production_build": "PASS",
"packaged_arm64_app": "PASS",
"codesign_deep_strict": "PASS",
"installed_binary_sha256": "f1e5474262bf0c64ceec3f912d8b4e9995dfa73612ca3193da93c1508d1709f8",
"installed_app_asar_sha256": "fd35a09ac2aff572c83f698a09be9c00d7a53096d7732f202f961cc937b6ceeb",
"current_ui_screenshot_sha256": "375a06d805d6ee31a7499bfef0c922229dc1ec6cfa0169ef61251d7189d29f18"
},
"boundaries": [
"These are built-in public vestibule manifests, not signed runtime domain manifests.",
"The first four domain themes remain unpublished until their responsible teams provide them.",
"The selected Fifth Domain reference is not yet proof of an independently loaded domain runtime theme.",
"No private domain resource was loaded before login.",
"This receipt does not claim server deployment, runtime health, persona birth or notarization."
]
}

View file

@ -13,9 +13,10 @@ import { ModuleLibrarySheet } from './components/ModuleLibrarySheet';
import { cleanDisplayText } from './presentation';
import { WorldEntry } from './components/WorldEntry';
import { DomainConnectionSheet } from './components/DomainConnectionSheet';
import type { DomainRouteId } from './public-domain-directory';
type View = 'editor' | 'history';
type RouteId = 'fifth' | 'main' | 'sub' | 'zero' | 'zero-sense';
type RouteId = DomainRouteId;
type ModuleId = 'knowledge' | 'education';
interface RepositoryStatus {
@ -65,14 +66,6 @@ function findFirstDocument(nodes: DocTreeNode[]): string | null {
return null;
}
const routeCopy: Record<RouteId, { title: string; body: string }> = {
fifth: { title: '我的第五域', body: '当前账号的个人第五域频道。模块在这里按需挂载、收起和组合。' },
main: { title: '光湖主域', body: '公共产品事实、发布与公告入口。当前客户端尚未取得该域的操作权限。' },
sub: { title: '光湖分域', body: '行业入口与初始化频道目录。教育行业与网文行业从这里进入。' },
zero: { title: '光湖零域', body: '实验、模块试装、对比与质量验证入口。' },
'zero-sense': { title: '光湖零感域', body: '治理、部署审批、事故、回滚与审计入口。' },
};
export default function App() {
const agentApiBase = window.location.protocol === 'file:' ? 'http://127.0.0.1:3890' : '';
const [tree, setTree] = useState<DocTreeNode[]>([]);
@ -339,7 +332,6 @@ export default function App() {
}
}, [openDoc, refreshTree]);
const activeRouteCopy = routeCopy[activeRoute];
const currentTitle = cleanDisplayText(currentDoc?.meta.title || '知识库');
const knowledgeState = channelState?.modules.find(module => module.id === 'HL-MOD-KNOWLEDGE-001');
const knowledgeInstalled = knowledgeState?.installed !== false;

View file

@ -1,6 +1,7 @@
import { useEffect, useState } from 'react';
import { getPublicDomainVestibule, type DomainRouteId } from '../public-domain-directory';
type DomainId = 'main' | 'sub' | 'zero' | 'zero-sense';
type DomainId = Exclude<DomainRouteId, 'fifth'>;
interface DomainEntry {
id: DomainId;
@ -92,15 +93,15 @@ export function DomainSurface({ activeDomain, educationSelected = false, onOpenS
const entry = registry?.domains.find(domain => domain.id === activeDomain);
const liveReadOnly = entry?.live?.access_state === 'ONLINE_READ_ONLY';
const fallbackName = activeDomain === 'main' ? '光湖主域' : activeDomain === 'sub' ? '光湖分域' : activeDomain === 'zero' ? '光湖零域' : '光湖零感域';
const publicVestibule = getPublicDomainVestibule(activeDomain);
return (
<main className="domain-surface">
<header className="domain-surface-header">
<div>
<small>{entry?.number || '企业四域公共入口'}</small>
<h1>{educationSelected ? '教育行业' : entry?.name || fallbackName}</h1>
<p>{educationSelected ? '光湖分域中的首个行业原型。模块进入个人频道后仍由个人服务器承载数据。' : entry?.responsibility || '读取企业灯塔后显示当前域职责与入口状态。'}</p>
<small>{entry?.number || publicVestibule.hldpNumber}</small>
<h1>{educationSelected ? '教育行业' : entry?.name || publicVestibule.displayName}</h1>
<p>{educationSelected ? '光湖分域中的首个行业原型。模块进入个人频道后仍由个人服务器承载数据。' : entry?.responsibility || publicVestibule.responsibility}</p>
</div>
<div className={`domain-connection ${registry?.verified ? 'online' : ''}`}>
<span aria-hidden="true" />

View file

@ -1,7 +1,6 @@
import { useState } from 'react';
import { Crosshair, DoorOpen, Landmark, Link2, LogIn, ShieldCheck } from 'lucide-react';
type DomainId = 'main' | 'sub' | 'zero' | 'zero-sense' | 'fifth';
import { getPublicDomainVestibule, publicDomainDirectory, type DomainRouteId } from '../public-domain-directory';
interface Props {
access: { blockers: string[]; runtimeReady: boolean; stage: 'checking' | 'login-required' | 'identity-verified' | 'runtime-ready' };
@ -10,19 +9,12 @@ interface Props {
onOpenConnection: () => void;
}
const domains: Array<{ id: DomainId; number: string; name: string; summary: string }> = [
{ id: 'main', number: '01', name: '光湖主域', summary: '公共产品事实、发布与公告入口' },
{ id: 'sub', number: '02', name: '光湖分域', summary: '行业入口与初始化频道目录' },
{ id: 'zero', number: '03', name: '光湖零域', summary: '实验、模块试装与质量验证入口' },
{ id: 'zero-sense', number: '04', name: '光湖零感域', summary: '治理、审批、回滚与审计入口' },
{ id: 'fifth', number: '05', name: '第五域 · 光湖本源域', summary: '零点原核的工程本体 · 语言架构层' },
];
export function WorldEntry({ access, onEnterFifthRuntime, onEnterLocalWorkspace, onOpenConnection }: Props) {
const [stage, setStage] = useState<'lighthouse' | 'directory' | 'fifth'>('lighthouse');
const [selectedDomain, setSelectedDomain] = useState<DomainId | null>(null);
const [selectedDomain, setSelectedDomain] = useState<DomainRouteId | null>(null);
const selectedVestibule = selectedDomain ? getPublicDomainVestibule(selectedDomain) : null;
const selectDomain = (id: DomainId) => {
const selectDomain = (id: DomainRouteId) => {
setSelectedDomain(id);
if (id === 'fifth') setStage('fifth');
};
@ -40,15 +32,15 @@ export function WorldEntry({ access, onEnterFifthRuntime, onEnterLocalWorkspace,
</header>
<nav className="fifth-domain-rail" aria-label="五域公开导航">
{domains.map(domain => (
{publicDomainDirectory.map(domain => (
<button
key={domain.id}
key={domain.routeId}
type="button"
className={domain.id === 'fifth' ? 'selected' : ''}
onClick={() => domain.id === 'fifth' ? undefined : (setStage('directory'), setSelectedDomain(domain.id))}
className={domain.routeId === 'fifth' ? 'selected' : ''}
onClick={() => domain.routeId === 'fifth' ? undefined : (setStage('directory'), setSelectedDomain(domain.routeId))}
>
<span>{domain.number}</span>
<strong>{domain.name}</strong>
<span>{domain.directoryNumber}</span>
<strong>{domain.displayName}</strong>
</button>
))}
</nav>
@ -95,20 +87,28 @@ export function WorldEntry({ access, onEnterFifthRuntime, onEnterLocalWorkspace,
</section>
<nav className="public-domain-strip" aria-label="五域公开门厅">
{domains.map(domain => (
{publicDomainDirectory.map(domain => (
<button
key={domain.id}
key={domain.routeId}
type="button"
className={selectedDomain === domain.id ? 'selected' : ''}
onClick={() => selectDomain(domain.id)}
className={selectedDomain === domain.routeId ? 'selected' : ''}
onClick={() => selectDomain(domain.routeId)}
>
<span>{domain.number}</span>
<strong>{domain.name}</strong>
{stage === 'directory' && <small>{domain.summary}</small>}
<span>{domain.directoryNumber}</span>
<strong>{domain.displayName}</strong>
{stage === 'directory' && <small>{domain.publicSummary}</small>}
</button>
))}
</nav>
{stage === 'directory' && selectedVestibule && (
<aside className="public-vestibule-fact" aria-live="polite">
<strong>{selectedVestibule.stableDomainId}</strong>
<span>{selectedVestibule.themeLabel}</span>
<small> · </small>
</aside>
)}
<footer className="public-entry-footer"></footer>
</main>
);

View file

@ -0,0 +1,39 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import { assertPublicDomainDirectory, getPublicDomainVestibule, publicDomainDirectory, type PublicDomainVestibule } from './public-domain-directory.js';
test('the public vestibule directory fixes the five official names and order', () => {
assert.deepEqual(publicDomainDirectory.map(item => [item.directoryNumber, item.displayName]), [
['01', '光湖主域'],
['02', '光湖分域'],
['03', '光湖零域'],
['04', '光湖零感域'],
['05', '第五域 · 光湖本源域'],
]);
});
test('the first four domain themes remain owned and unpublished by their own teams', () => {
for (const item of publicDomainDirectory.slice(0, 4)) {
assert.equal(item.themeOwner, 'domain-team');
assert.equal(item.themeStatus, 'UNPUBLISHED');
assert.match(item.themeLabel, /对应|光湖/);
}
const fifth = getPublicDomainVestibule('fifth');
assert.equal(fifth.themeOwner, 'fifth-domain');
assert.equal(fifth.themeStatus, 'SELECTED_REFERENCE');
assert.match(fifth.themeLabel, /湖面映星/);
});
test('no public vestibule exposes private domain resources', () => {
assert.ok(publicDomainDirectory.every(item => item.privateResourcesExposed === false));
});
test('validation rejects a misspelled zero domain and shared enterprise theme override', () => {
const clone = publicDomainDirectory.map(item => ({ ...item })) as PublicDomainVestibule[];
clone[2] = { ...clone[2], displayName: '光湖灵域' };
assert.throws(() => assertPublicDomainDirectory(clone), /PUBLIC_DOMAIN_ZERO_NAME_INVALID/);
const themed = publicDomainDirectory.map(item => ({ ...item })) as PublicDomainVestibule[];
themed[0] = { ...themed[0], themeOwner: 'fifth-domain', themeStatus: 'SELECTED_REFERENCE' };
assert.throws(() => assertPublicDomainDirectory(themed), /ENTERPRISE_DOMAIN_THEME_OWNERSHIP_INVALID/);
});

View file

@ -0,0 +1,133 @@
export type DomainRouteId = 'main' | 'sub' | 'zero' | 'zero-sense' | 'fifth';
export type PublicDomainVestibule = Readonly<{
routeId: DomainRouteId;
directoryNumber: '01' | '02' | '03' | '04' | '05';
stableDomainId: string;
hldpNumber: string;
displayName: string;
publicSummary: string;
responsibility: string;
repositoryId: string;
themeOwner: 'domain-team' | 'fifth-domain';
themeStatus: 'UNPUBLISHED' | 'SELECTED_REFERENCE';
themeLabel: string;
supportedNodeTypes: readonly ('local-terminal' | 'cloud-resident')[];
privateResourcesExposed: false;
}>;
const rawDirectory: PublicDomainVestibule[] = [
{
routeId: 'main',
directoryNumber: '01',
stableDomainId: 'DOMAIN-MAIN',
hldpNumber: 'HLDP-DOMAIN-MAIN-001',
displayName: '光湖主域',
publicSummary: '公共产品事实、发布与公告入口',
responsibility: '公共产品事实、正式发布、公告与公共回执。',
repositoryId: 'domain-main',
themeOwner: 'domain-team',
themeStatus: 'UNPUBLISHED',
themeLabel: '主题由光湖主域主控团队发布 · 当前未装载',
supportedNodeTypes: ['local-terminal', 'cloud-resident'],
privateResourcesExposed: false,
},
{
routeId: 'sub',
directoryNumber: '02',
stableDomainId: 'DOMAIN-SUB',
hldpNumber: 'HLDP-DOMAIN-SUB-001',
displayName: '光湖分域',
publicSummary: '行业入口与初始化频道目录',
responsibility: '行业入口、初始化频道与对应行业模块目录。',
repositoryId: 'domain-sub',
themeOwner: 'domain-team',
themeStatus: 'UNPUBLISHED',
themeLabel: '主题由光湖分域主控团队发布 · 当前未装载',
supportedNodeTypes: ['local-terminal', 'cloud-resident'],
privateResourcesExposed: false,
},
{
routeId: 'zero',
directoryNumber: '03',
stableDomainId: 'DOMAIN-ZERO',
hldpNumber: 'HLDP-DOMAIN-ZERO-001',
displayName: '光湖零域',
publicSummary: '实验、模块试装与质量验证入口',
responsibility: '实验、模块试装、真实预览与质量验证。',
repositoryId: 'domain-zero',
themeOwner: 'domain-team',
themeStatus: 'UNPUBLISHED',
themeLabel: '主题由光湖零域主控团队发布 · 当前未装载',
supportedNodeTypes: ['local-terminal', 'cloud-resident'],
privateResourcesExposed: false,
},
{
routeId: 'zero-sense',
directoryNumber: '04',
stableDomainId: 'DOMAIN-ZS',
hldpNumber: 'HLDP-DOMAIN-ZEROSENSE-001',
displayName: '光湖零感域',
publicSummary: '治理、审批、回滚与审计入口',
responsibility: '治理、部署审批、事故处置、回滚与审计。',
repositoryId: 'domain-zero-sense',
themeOwner: 'domain-team',
themeStatus: 'UNPUBLISHED',
themeLabel: '主题由光湖零感域主控团队发布 · 当前未装载',
supportedNodeTypes: ['local-terminal', 'cloud-resident'],
privateResourcesExposed: false,
},
{
routeId: 'fifth',
directoryNumber: '05',
stableDomainId: 'DOM-FIFTH-0001',
hldpNumber: 'DOM-FIFTH-0001',
displayName: '第五域 · 光湖本源域',
publicSummary: '零点原核的工程本体 · 语言架构层',
responsibility: '光湖语言世界的语言架构层与语言协议更新源点。',
repositoryId: 'REPO-012',
themeOwner: 'fifth-domain',
themeStatus: 'SELECTED_REFERENCE',
themeLabel: '湖面映星 · 已选定参考 · 进入后由独立运行体装载',
supportedNodeTypes: ['local-terminal', 'cloud-resident'],
privateResourcesExposed: false,
},
];
export function assertPublicDomainDirectory(directory: readonly PublicDomainVestibule[]): void {
if (directory.length !== 5) throw new Error('PUBLIC_DOMAIN_DIRECTORY_MUST_CONTAIN_FIVE_DOMAINS');
const unique = (values: string[]) => new Set(values).size === values.length;
if (!unique(directory.map(item => item.routeId))) throw new Error('PUBLIC_DOMAIN_ROUTE_ID_DUPLICATED');
if (!unique(directory.map(item => item.directoryNumber))) throw new Error('PUBLIC_DOMAIN_NUMBER_DUPLICATED');
if (!unique(directory.map(item => item.stableDomainId))) throw new Error('PUBLIC_DOMAIN_STABLE_ID_DUPLICATED');
if (directory.map(item => item.directoryNumber).join(',') !== '01,02,03,04,05') {
throw new Error('PUBLIC_DOMAIN_DIRECTORY_ORDER_INVALID');
}
const zero = directory.find(item => item.routeId === 'zero');
if (zero?.displayName !== '光湖零域') throw new Error('PUBLIC_DOMAIN_ZERO_NAME_INVALID');
const fifth = directory.find(item => item.routeId === 'fifth');
if (fifth?.themeOwner !== 'fifth-domain' || fifth.themeStatus !== 'SELECTED_REFERENCE') {
throw new Error('FIFTH_DOMAIN_THEME_REFERENCE_INVALID');
}
if (directory.some(item => item.routeId !== 'fifth' && (item.themeOwner !== 'domain-team' || item.themeStatus !== 'UNPUBLISHED'))) {
throw new Error('ENTERPRISE_DOMAIN_THEME_OWNERSHIP_INVALID');
}
if (directory.some(item => item.privateResourcesExposed !== false)) {
throw new Error('PUBLIC_VESTIBULE_MUST_NOT_EXPOSE_PRIVATE_RESOURCES');
}
if (directory.some(item => item.supportedNodeTypes.join(',') !== 'local-terminal,cloud-resident')) {
throw new Error('PUBLIC_VESTIBULE_NODE_TYPES_INVALID');
}
}
assertPublicDomainDirectory(rawDirectory);
export const publicDomainDirectory: readonly PublicDomainVestibule[] = Object.freeze(
rawDirectory.map(item => Object.freeze({ ...item, supportedNodeTypes: Object.freeze([...item.supportedNodeTypes]) }))
);
export function getPublicDomainVestibule(routeId: DomainRouteId): PublicDomainVestibule {
const entry = publicDomainDirectory.find(item => item.routeId === routeId);
if (!entry) throw new Error(`PUBLIC_DOMAIN_NOT_FOUND: ${routeId}`);
return entry;
}

View file

@ -233,6 +233,53 @@
min-height: 118px;
}
.public-lighthouse.is-directory .public-entry-footer {
display: none;
}
.public-vestibule-fact {
position: absolute;
left: 50%;
bottom: 54px;
z-index: 2;
width: min(760px, calc(100vw - 40px));
transform: translateX(-50%);
display: grid;
grid-template-columns: auto 1fr auto;
align-items: center;
gap: 16px;
padding: 10px 16px;
border-top: 1px solid rgba(191, 216, 255, 0.28);
color: rgba(232, 241, 255, 0.9);
text-align: left;
letter-spacing: 0.04em;
}
.public-vestibule-fact strong,
.public-vestibule-fact span,
.public-vestibule-fact small {
min-width: 0;
}
.public-vestibule-fact strong {
color: rgba(198, 220, 255, 0.78);
font-size: 12px;
font-weight: 500;
}
.public-vestibule-fact span {
overflow: hidden;
font-size: 13px;
font-weight: 500;
text-overflow: ellipsis;
white-space: nowrap;
}
.public-vestibule-fact small {
color: rgba(198, 215, 241, 0.62);
font-size: 11px;
}
.fifth-brand {
position: absolute;
z-index: 1;