Contract · protocol-v1 · AIOS 协议契约 v1
🔒 本文档已 FROZEN(contract-v1.0)
- Owner:ClaudeB(独占写权限)
- Status:
frozen· §1-§9 全部完成 · tagcontract-v1.0已打 · 任何修改必须走 ADR-AIOS-NN- Frozen at:2026-05-27(HARD-DEADLINE B4 · ClaudeB)
- Freeze 后规则:全员只读 · 修改须 ADR 决议 → fork P_contracts.K2-protocol-v2
其他智能体:本文档已 frozen,可直接引用,无需暂行替代文档。
0. 文档约定
- 命名空间格式:
<scope>.<entity>.<action>(如shell.slot.set) - payload 格式:JSON · 必须含
version/timestamp/payload - 错误码格式:4 位数字(如
1001)· 区间分配见 §3 - 章节锁:每章节由 ClaudeB 独立 commit · 顺序 §1 → §9
1. Store Action 命名空间总则
状态:✅ B1 草稿完成(2026-05-27)
1.1 三段式命名规范
所有 Pinia store action 的逻辑标识符(用于文档、协议追踪、错误上报)遵循三段式:
| 段位 | 说明 | 枚举值示例 |
|---|---|---|
scope |
归属层 | shell · engine · workspace · dialog · toast · preset · history |
entity |
操作对象(UI 区域或数据对象) | topbar · dock · inspector · bottom · statusbar · stage · module |
verb |
动词(祈使式) | set · clear · toggle · open · close · show · hide · add · remove |
重要:三段式是协议标识符,不是 JavaScript 变量名。Pinia 实现使用标准驼峰命名,见 §1.3。
1.2 消息信封(通用 payload 格式)
所有跨层消息(stage → shell / shell → stage)使用统一信封:
// TypeScript 信封接口
export interface ActionEnvelope<T = unknown> {
version: string // 协议版本,当前 "1"
timestamp: number // Unix ms
action: StoreActionKey // 见 §1.5 全表
payload: T
}
规则:
- version 在 §1-§9 全部 freeze 前始终为 "1"
- timestamp 由发送方在发出时填写(单调递增,不做持久化)
- payload schema 由各 action 条目单独定义(见 §1.5)
- WS / Pinia / EventBus 传递时,信封可以被框架代理,不要求在运行时显式序列化
1.3 Pinia 实现映射规则
协议标识符 <scope>.<entity>.<verb> |
Pinia Store | Action 方法签名 |
|---|---|---|
shell.topbar.set-toolbar |
useTopbarStore |
setToolbar(buttons: ToolbarButton[]) |
shell.topbar.set-modes |
useTopbarStore |
setModes(chips: ModeChip[], activeKey?: string) |
shell.topbar.set-doc-tabs |
useTopbarStore |
setDocTabs(tabs: DocTab[], activeKey?: string) |
shell.dock.set-left |
useDockStore |
setLeft(panes: DockPane[]) |
shell.dock.set-right |
useDockStore |
setRight(panes: DockPane[]) |
shell.inspector.set-html |
useInspectorStore |
setHtml(html: string, module: string) |
shell.bottom.set-module-html |
useBottomStore |
setModuleHtml(html: string, module: string) |
shell.bottom.set-hint |
useBottomStore |
setHint(html: string) |
shell.statusbar.set-stage |
useStatusBarStore |
setStageHtml(html: string) |
engine.state.set |
useEngineStore |
setState(info: EngineStateInfo) |
dialog.module.open |
useDialogStore |
open(payload: DialogOpenPayload) |
toast.message.show |
useToastStore |
show(message: string, type?: ToastType) |
history.state.set |
useShellSlotsStore |
setHistory(state: HistoryState) |
workspace.stage.set-ready |
useStageStore |
setReady(stage: StageKey) |
preset.module.activate |
usePresetStore |
activate(id: string) |
1.4 命名禁忌与保留字
禁止:
- ❌ 单段命名:set · click · init
- ❌ 大写字母(用 kebab-case 连字符代替 camelCase):setToolbar ❌ set-toolbar ✅
- ❌ 含 agent 代号:claudeA.xxx · ClaudeB.xxx
- ❌ 含实现细节:pinia.xxx · mitt.xxx · vuex.xxx
保留 scope(禁止 stage 私用):
- internal · system · aios · debug
缩写规范(唯一允许表):
| 缩写 | 全称 |
|---|---|
| ws | workspace |
| doc | document |
| btn | ❌ 禁止,写 button |
| msg | ❌ 禁止,写 message |
1.5 12 类 postMessage → Store Action 完整映射表
| v4.3 postMessage type | 方向 | 协议标识符 / Store Action | EventBus signal | 优先级 |
|---|---|---|---|---|
toolbar-inject |
↓ shell→stage | shell.topbar.set-toolbar |
— | P1 |
toolbar-click |
↑ stage→shell | — | toolbar:click |
P1 |
mode-switcher-inject |
↓ | shell.topbar.set-modes |
— | P1 |
mode-switch-click |
↑ | — | mode-switch:click |
P1 |
engine-control |
↑ | engine.state.toggle |
engine:control |
P1 |
engine-state-changed |
→ shell内 | engine.state.set |
— | P1 |
dock-inject |
↓ | shell.dock.set-left / shell.dock.set-right |
— | P2 |
inspector-inject |
↓ | shell.inspector.set-html |
— | P2 |
module-properties-inject |
↓ | shell.bottom.set-module-html |
— | P2 |
select-node |
↑ | — | node:selected |
P2 |
tuning-dialog |
↑ | dialog.module.open |
dialog:open |
P2 |
show-toast |
↑ | toast.message.show |
toast:show |
P1 |
stage-status |
↓ | shell.statusbar.set-stage |
— | P2 |
stage-hint |
↓ | shell.bottom.set-hint |
— | P2 |
doc-tabs-inject |
↓ | shell.topbar.set-doc-tabs |
— | P3 |
doc-tab-click/close/new |
↑ | — | doc-tab:{click\|close\|new} |
P3 |
stage-ready |
↑ | workspace.stage.set-ready |
— | P1 |
stage-hint |
↓ | shell.bottom.set-hint |
— | P2 |
符号:↑ = stage→shell · ↓ = shell→stage · → = shell 内部广播
2. EventBus 信号集
状态:✅ B1 草稿完成(2026-05-27)
EventBus 基于 mitt 实现(src/utils/eventBus.ts),所有 stage ↔ shell 的事件驱动通信通过此信道传递。
2.1 信号命名规范
| 段位 | 说明 | 规则 |
|---|---|---|
entity |
操作对象(名词) | 全小写 kebab-case |
verb |
动作(过去时或动词原形) | 全小写,意义明确 |
示例:toolbar:click · mode-switch:click · node:selected · toast:show · history:undo
2.2 TypeScript 信号全表
权威来源:
frontend_vue3/src/types/protocol.ts
// ─────────────────────────────────────────
// §2 AppEvents — EventBus 信号完整类型映射
// ─────────────────────────────────────────
export type AppEvents = {
// ── Topbar ──
'toolbar:click': ToolbarClickPayload // stage toolbar 按钮点击
'mode-switch:click': ModeSwitchClickPayload // 模式切换 chip 点击
'doc-tab:click': DocTabPayload // 文档 tab 切换
'doc-tab:close': DocTabPayload // 文档 tab 关闭
'doc-tab:new': DocTabPayload // 新建文档 tab
// ── Stage 内容事件 ──
'node:selected': NodeSelectedPayload // 节点选中(xilink → inspector)
'dialog:open': DialogOpenPayload // 调音对话框打开(stage → DialogManager)
// ── 引擎 ──
'engine:control': EngineControlPayload // stage 发出引擎控制指令
// ── 通知 ──
'toast:show': ToastPayload // 显示 Toast 通知
'toast:dismiss': { id: string } // 手动关闭 Toast
// ── 历史(撤销/重做)──
'history:undo': { stage: StageKey } // Shell TopbarToolbar → 当前 stage
'history:redo': { stage: StageKey }
// ── 项目操作(MenuBar → 所有 stage)──
'project:save': void
'project:new': void
'project:open': void
'project:propagate': void // workspace 文件已变更,stage 同步
}
// ── 信号 payload 接口(同 §1.3 ActionEnvelope 中的 payload 字段)──
export interface ToolbarClickPayload {
stage: StageKey
id: string // ToolbarButton.id
}
export interface ModeSwitchClickPayload {
stage: StageKey
key: string // ModeChip.key
}
export interface DocTabPayload {
stage: StageKey
key?: string // click/close 时必填;new 时可选
}
export interface NodeSelectedPayload {
stage: StageKey
node: string // instanceId
label: string // 显示名(用于 Inspector 标题)
}
export interface DialogOpenPayload {
stage: StageKey
module: string // instanceId
label: string
preset?: string // 关联的 preset id(可选)
}
export interface EngineControlPayload {
stage: StageKey
action: 'toggle' | 'start' | 'stop' | 'restart'
}
export interface ToastPayload {
id: string // nanoid · 由发送方生成
type: ToastType // 'info' | 'success' | 'warning' | 'error'
message: string
duration?: number // ms · 默认 3000 · 0 = 不自动关闭
}
2.3 触发方 / 接收方矩阵
| 信号 | 触发方 | 接收方 | 生命周期 |
|---|---|---|---|
toolbar:click |
TopbarToolbar.vue |
当前激活 stage | fire-once |
mode-switch:click |
TopbarModeSwitcher.vue |
当前激活 stage | fire-once |
doc-tab:click |
TopbarDocTabs.vue |
当前激活 stage | fire-once |
doc-tab:close |
TopbarDocTabs.vue |
当前激活 stage | fire-once |
doc-tab:new |
TopbarDocTabs.vue |
当前激活 stage | fire-once |
node:selected |
当前 stage(xilink) | InspectorPanel.vue |
fire-once |
dialog:open |
当前 stage | DialogManager.vue |
fire-once |
engine:control |
当前 stage toolbar | EngineStatusBar.vue |
fire-once |
toast:show |
任意 stage / shell 组件 | ToastContainer.vue |
fire-once |
toast:dismiss |
ToastContainer.vue / 定时器 |
ToastContainer.vue |
fire-once |
history:undo |
TopbarToolbar.vue |
当前激活 stage | fire-once |
history:redo |
TopbarToolbar.vue |
当前激活 stage | fire-once |
project:save |
MenuBar.vue |
所有 stage | fire-once |
project:new |
MenuBar.vue |
所有 stage | fire-once |
project:open |
MenuBar.vue |
所有 stage | fire-once |
project:propagate |
任意 stage | 所有 stage | fire-once |
2.4 信号生命周期说明
| 生命周期类型 | 说明 | 本协议使用 |
|---|---|---|
fire-once |
发出后即消费,不持久化 | ✅ 全部信号 |
persistent |
新监听者也能收到最近值 | ❌ 不使用(用 Pinia reactive state 替代) |
debounced |
防抖聚合 | ❌ 由消费方自行处理 |
规则:EventBus 不持久化任何信号。需要"最新状态"的场景一律用 Pinia store reactive state。
3. 错误码体系
状态:✅ B1 草稿完成(2026-05-27)
3.1 区间分配总表
| 区间 | 归属 | 用途 |
|---|---|---|
| 1000-1999 | shell | Shell 容器层错误(slot / stage 注册 / 顶栏) |
| 2000-2999 | stage 通用 | Stage 生命周期 / 状态机通用错误 |
| 3000-3999 | xilink | XiLink stage 错误(模块 / 链路 / 工作区) |
| 4000-4999 | xitune | XiTune stage 错误(参数 / preset / profile) |
| 5000-5999 | xiforge | XiForge stage 错误(widget / module-uid / codegen) |
| 6000-6999 | xitest | XiTest stage 错误(测试用例 / runner / 断言) |
| 7000-7999 | engine / mode-switcher | 音频引擎 + 模式切换错误 |
| 8000-8999 | preset / profile | Preset / Profile CRUD 错误 |
| 9000-9999 | event-bus / 通用 | EventBus 协议层 + 兜底通用错误 |
3.2 TypeScript ErrorCode 枚举
// frontend_vue3/src/types/protocol.ts — ErrorCode
// 同步到 §3(权威源在此文件)
export enum ErrorCode {
// ──────────────── 1000-1999 · Shell ────────────────
/** Shell slot 未注册或不存在 */
SHELL_SLOT_NOT_FOUND = 1001,
/** Stage 未在 Shell 注册 */
SHELL_STAGE_NOT_REGISTERED = 1002,
/** Toolbar 按钮 id 不存在 */
SHELL_TOOLBAR_BTN_NOT_FOUND = 1003,
/** ModeChip key 不存在 */
SHELL_MODE_NOT_FOUND = 1004,
/** DocTab key 不存在 */
SHELL_DOC_TAB_NOT_FOUND = 1005,
/** Dock pane key 重复 */
SHELL_DOCK_PANE_DUPLICATE = 1006,
// ──────────────── 2000-2999 · Stage 通用 ────────────────
/** Stage 尚未初始化完成(setReady 未触发) */
STAGE_NOT_READY = 2001,
/** 目标 stage key 不存在 */
STAGE_NOT_FOUND = 2002,
/** Stage 初始化失败 */
STAGE_INIT_FAILED = 2003,
/** Stage 已处于目标状态(无需切换) */
STAGE_ALREADY_ACTIVE = 2004,
/** Stage 切换被当前操作阻塞 */
STAGE_TRANSITION_BLOCKED = 2005,
// ──────────────── 3000-3999 · XiLink ────────────────
/** 模块 instanceId 不存在 */
XILINK_MODULE_NOT_FOUND = 3001,
/** 尝试添加已存在的节点 */
XILINK_NODE_DUPLICATE = 3002,
/** Edge 端口类型不匹配 */
XILINK_EDGE_INVALID = 3003,
/** 链路包含环路 */
XILINK_CHAIN_CIRCULAR = 3004,
/** Workspace 文件加载失败 */
XILINK_WORKSPACE_LOAD_FAILED = 3005,
/** 模块定义 JSON 不合法 */
XILINK_MODULE_DEF_INVALID = 3006,
// ──────────────── 4000-4999 · XiTune ────────────────
/** 参数值超出范围 */
XITUNE_PARAM_OUT_OF_RANGE = 4001,
/** Preset id 不存在 */
XITUNE_PRESET_NOT_FOUND = 4002,
/** 目标模块 DSP 未 ready */
XITUNE_MODULE_NOT_READY = 4003,
/** Profile 文件加载失败 */
XITUNE_PROFILE_LOAD_FAILED = 4004,
/** 参数 id 不存在于当前模块 */
XITUNE_PARAM_NOT_FOUND = 4005,
// ──────────────── 5000-5999 · XiForge ────────────────
/** Widget 类型未在注册表中 */
XIFORGE_WIDGET_NOT_REGISTERED = 5001,
/** ModuleDef 结构不合法 */
XIFORGE_MODULE_DEF_INVALID = 5002,
/** codegen 失败(推迟到下季度,预留码) */
XIFORGE_CODEGEN_FAILED = 5003,
/** Module UID 与已有模块冲突 */
XIFORGE_UID_CONFLICT = 5004,
/** Module UID 超出 xistudio 段位范围 */
XIFORGE_UID_OUT_OF_RANGE = 5005,
// ──────────────── 6000-6999 · XiTest ────────────────
/** 测试用例 id 不存在 */
XITEST_CASE_NOT_FOUND = 6001,
/** 测试 runner 正忙,无法并行 */
XITEST_RUNNER_BUSY = 6002,
/** 断言失败 */
XITEST_ASSERTION_FAILED = 6003,
/** 测试超时 */
XITEST_TIMEOUT = 6004,
// ──────────────── 7000-7999 · Engine / Mode-Switcher ────────────────
/** 音频引擎启动失败 */
ENGINE_START_FAILED = 7001,
/** 音频引擎停止失败 */
ENGINE_STOP_FAILED = 7002,
/** 操作要求引擎处于 ready 状态 */
ENGINE_NOT_READY = 7003,
/** 模式切换失败(当前模式不允许切换) */
MODE_TRANSITION_FAILED = 7004,
/** 目标模式未授权(license) */
MODE_NOT_ALLOWED = 7005,
/** 引擎重启超时 */
ENGINE_RESTART_TIMEOUT = 7006,
// ──────────────── 8000-8999 · Preset / Profile ────────────────
/** Preset id 不存在 */
PRESET_NOT_FOUND = 8001,
/** Preset 写入失败 */
PRESET_SAVE_FAILED = 8002,
/** Preset 读取失败 */
PRESET_LOAD_FAILED = 8003,
/** Preset schema 版本不匹配 */
PRESET_SCHEMA_MISMATCH = 8004,
/** Preset 名称重复 */
PRESET_NAME_DUPLICATE = 8005,
// ──────────────── 9000-9999 · EventBus / 通用 ────────────────
/** EventBus 收到未知 signal 类型 */
BUS_EVENT_UNKNOWN = 9001,
/** EventBus handler 未注册 */
BUS_HANDLER_MISSING = 9002,
/** payload 类型不符合协议契约 */
BUS_PAYLOAD_INVALID = 9003,
/** 兜底:未分类错误 */
UNKNOWN_ERROR = 9999,
}
// 错误码对应的 message 快查表
export const ErrorMessage: Record<ErrorCode, string> = {
[ErrorCode.SHELL_SLOT_NOT_FOUND]: 'Shell slot not found',
[ErrorCode.SHELL_STAGE_NOT_REGISTERED]: 'Stage not registered in shell',
[ErrorCode.SHELL_TOOLBAR_BTN_NOT_FOUND]: 'Toolbar button id not found',
[ErrorCode.SHELL_MODE_NOT_FOUND]: 'Mode chip key not found',
[ErrorCode.SHELL_DOC_TAB_NOT_FOUND]: 'Doc tab key not found',
[ErrorCode.SHELL_DOCK_PANE_DUPLICATE]: 'Dock pane key already exists',
[ErrorCode.STAGE_NOT_READY]: 'Stage is not ready yet',
[ErrorCode.STAGE_NOT_FOUND]: 'Stage key does not exist',
[ErrorCode.STAGE_INIT_FAILED]: 'Stage initialization failed',
[ErrorCode.STAGE_ALREADY_ACTIVE]: 'Stage is already in the target state',
[ErrorCode.STAGE_TRANSITION_BLOCKED]: 'Stage transition is blocked',
[ErrorCode.XILINK_MODULE_NOT_FOUND]: 'Module instanceId not found',
[ErrorCode.XILINK_NODE_DUPLICATE]: 'Node already exists',
[ErrorCode.XILINK_EDGE_INVALID]: 'Edge port type mismatch',
[ErrorCode.XILINK_CHAIN_CIRCULAR]: 'Chain contains a cycle',
[ErrorCode.XILINK_WORKSPACE_LOAD_FAILED]: 'Workspace file load failed',
[ErrorCode.XILINK_MODULE_DEF_INVALID]: 'Module definition JSON is invalid',
[ErrorCode.XITUNE_PARAM_OUT_OF_RANGE]: 'Parameter value out of range',
[ErrorCode.XITUNE_PRESET_NOT_FOUND]: 'Preset not found',
[ErrorCode.XITUNE_MODULE_NOT_READY]: 'Module DSP not ready',
[ErrorCode.XITUNE_PROFILE_LOAD_FAILED]: 'Profile load failed',
[ErrorCode.XITUNE_PARAM_NOT_FOUND]: 'Parameter id not found',
[ErrorCode.XIFORGE_WIDGET_NOT_REGISTERED]: 'Widget type not registered',
[ErrorCode.XIFORGE_MODULE_DEF_INVALID]: 'ModuleDef structure invalid',
[ErrorCode.XIFORGE_CODEGEN_FAILED]: 'Code generation failed',
[ErrorCode.XIFORGE_UID_CONFLICT]: 'Module UID conflicts with existing module',
[ErrorCode.XIFORGE_UID_OUT_OF_RANGE]: 'Module UID out of xistudio range',
[ErrorCode.XITEST_CASE_NOT_FOUND]: 'Test case not found',
[ErrorCode.XITEST_RUNNER_BUSY]: 'Test runner is busy',
[ErrorCode.XITEST_ASSERTION_FAILED]: 'Test assertion failed',
[ErrorCode.XITEST_TIMEOUT]: 'Test timed out',
[ErrorCode.ENGINE_START_FAILED]: 'Engine start failed',
[ErrorCode.ENGINE_STOP_FAILED]: 'Engine stop failed',
[ErrorCode.ENGINE_NOT_READY]: 'Engine is not ready',
[ErrorCode.MODE_TRANSITION_FAILED]: 'Mode transition failed',
[ErrorCode.MODE_NOT_ALLOWED]: 'Mode not allowed (check license)',
[ErrorCode.ENGINE_RESTART_TIMEOUT]: 'Engine restart timed out',
[ErrorCode.PRESET_NOT_FOUND]: 'Preset not found',
[ErrorCode.PRESET_SAVE_FAILED]: 'Preset save failed',
[ErrorCode.PRESET_LOAD_FAILED]: 'Preset load failed',
[ErrorCode.PRESET_SCHEMA_MISMATCH]: 'Preset schema version mismatch',
[ErrorCode.PRESET_NAME_DUPLICATE]: 'Preset name already exists',
[ErrorCode.BUS_EVENT_UNKNOWN]: 'Unknown EventBus signal',
[ErrorCode.BUS_HANDLER_MISSING]: 'EventBus handler not registered',
[ErrorCode.BUS_PAYLOAD_INVALID]: 'EventBus payload does not match contract',
[ErrorCode.UNKNOWN_ERROR]: 'Unknown error',
}
// 辅助:创建协议错误对象
export function createProtocolError(code: ErrorCode, detail?: string): Error {
const msg = ErrorMessage[code] ?? 'Unknown error'
return new Error(`[${code}] ${msg}${detail ? ': ' + detail : ''}`)
}
3.3 错误上报约定
- 上报字段:
{ code: ErrorCode, message: string, detail?: string, stage?: StageKey } - 上报通道:调用
toast.message.show信号(type: 'error')向用户展示 - 日志:
console.error('[AIOS]', code, detail)— 格式固定,便于 AIOS 日志过滤 - 不抛出:协议错误不使用
throw,使用createProtocolError()创建 Error 对象后上报
4. sourceList 音源管理协议
状态:✅ B2 草稿完成(2026-05-27)
sourceList 协议管理 XiLink 链路中的音频源模块(moduleId 前缀为 source_*_v1)。所有操作均为 WS 消息 + Pinia store action 双格式。
4.1 音源信号类型(10 种)
来源:CLAUDE.md Phase 8 + ADR-AIOS-05
| moduleId | 类型 | TypeNumId | 参数 |
|---|---|---|---|
source_sine_v1 |
tone | 0x10080010 |
frequencyHz / amplitudeDb / phaseDeg |
source_sweep_v1 |
tone | 0x10080011 |
startFrequencyHz / endFrequencyHz / durationSec |
source_triangle_v1 |
tone | 0x10080012 |
symmetry |
source_square_v1 |
tone | 0x10080013 |
dutyCycle |
source_saw_v1 |
tone | 0x10080014 |
direction |
source_noise_white_v1 |
noise | 0x10080015 |
— |
source_noise_pink_v1 |
noise | 0x10080016 |
— |
source_noise_brown_v1 |
noise | 0x10080017 |
— |
source_wav_v1 |
file | 0x10080018 |
filePath(structural 参数) |
source_device_v1 |
device | 0x10080019 |
deviceId(structural 参数) |
Structural 参数(
filePath/deviceId)变更触发SetLink;其他参数变更触发SetParam。
4.2 WS 消息格式
4.2.1 set_source_signal(切换信号类型)
Request:
{
"type": "set_source_signal",
"instanceId": "source_sine#0",
"signalType": "source_sine_v1",
"params": {
"frequencyHz": 1000,
"amplitudeDb": -6.0,
"phaseDeg": 0
}
}
Response(set_source_signal_ack):
{
"type": "set_source_signal_ack",
"success": true,
"signalType": "source_sine_v1",
"restarted": false
}
restarted=true表示信号类型切换触发了引擎重启(structural 参数变更)。
4.2.2 source_file_play / source_file_stop / source_file_seek
source_file_play:
source_file_stop:
source_file_seek:
所有 source_file_* 操作无 ack 响应(fire-and-forget)。
4.3 Store Action 命名空间
| 协议标识符 | Pinia Store | Action 签名 |
|---|---|---|
source.signal.set |
useSourceStore |
setSignal(instanceId: string, signalType: SourceSignalType, params?: Record<string, unknown>) |
source.file.play |
useSourceStore |
filePlay(instanceId: string) |
source.file.stop |
useSourceStore |
fileStop(instanceId: string) |
source.file.seek |
useSourceStore |
fileSeek(instanceId: string, positionMs: number) |
source.list.get |
useLinkStore |
getSourceModules(): SourceModule[] |
4.4 TypeScript 接口
// ─── §4 sourceList 类型 ───────────────────────────────────────────────────
/** 10 种音源 moduleId(Phase 8 · schemaVersion: "8.0") */
export type SourceSignalType =
| 'source_sine_v1'
| 'source_sweep_v1'
| 'source_triangle_v1'
| 'source_square_v1'
| 'source_saw_v1'
| 'source_noise_white_v1'
| 'source_noise_pink_v1'
| 'source_noise_brown_v1'
| 'source_wav_v1'
| 'source_device_v1'
/** 音源类型分类 */
export type SourceCategory = 'tone' | 'noise' | 'file' | 'device'
/** set_source_signal WS 请求 */
export interface SetSourceSignalRequest {
type: 'set_source_signal'
instanceId: string // 目标模块 instanceId
signalType: SourceSignalType // 目标信号类型
params?: Record<string, unknown> // 信号参数(见 §4.1)
}
/** set_source_signal_ack WS 响应 */
export interface SetSourceSignalAck {
type: 'set_source_signal_ack'
success: boolean
signalType?: SourceSignalType // 实际生效的 signalType(success=true 时)
restarted?: boolean // true = structural 变更触发引擎重启
error?: string // success=false 时的错误说明
}
/** source_file_* 控制请求(play/stop/seek 共用字段结构) */
export interface SourceFileControlRequest {
type: 'source_file_play' | 'source_file_stop' | 'source_file_seek'
instanceId: string
positionMs?: number // seek 时必填
}
/** 链路中一个音源模块的摘要信息 */
export interface SourceModule {
instanceId: string
moduleId: SourceSignalType
category: SourceCategory
displayName: string
params: Record<string, unknown>
}
5. refresh-link 双语义协议
状态:✅ B2 草稿完成(2026-05-27)
refresh_link 是 ADR-AIOS-05 §2.1.5 决议的协议,有两种语义(由 mode 字段区分):
load(加载反读):从后端/目标读取当前部署的链路结构 → 填充linkStore.deployedverify(校验对账):与 host 当前编辑态链路比较 → 返回 diff 结果
5.1 请求格式
{
"type": "refresh_link",
"mode": "verify",
"source": "pc_backend",
"hostLink": { "id": "...", "modules": [...], "connections": [...] }
}
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
type |
'refresh_link' |
✅ | 固定值 |
mode |
'load' \| 'verify' |
✅ | 操作语义 |
source |
RefreshLinkSourceWire |
否 | 反读源(默认 'pc_backend') |
hostLink |
DSPLink |
verify 必填 | host 编辑态链路,供后端对账 |
📌 v1.0.1 命名统一补丁(2026-05-27 用户拍板): - 正式名:
'pc_backend'(前端语义层与 WS 协议层统一) - 兼容期:后端旧实现接受'host',视同'pc_backend',1 个版本后移除 -RefreshLinkSourceWire新值:'pc_backend' | 'target_dsp' | 'target_pc'-'host'标@deprecated,仅在兼容期(当前版本)接受
5.2 响应格式(load 和 verify 共用同一 ack type)
load 成功:
{
"type": "refresh_link_ack",
"mode": "load",
"success": true,
"data": {
"id": "link-uuid",
"modules": [
{ "instanceId": "gain#0", "moduleId": "channel_gain_v1", "paramValues": { "gainDb#0": "6.0" } }
],
"connections": [
{ "id": "conn-1", "fromModule": "source_sine#0", "fromPort": "out0", "toModule": "gain#0", "toPort": "in0" }
]
}
}
verify 成功:
{
"type": "refresh_link_ack",
"mode": "verify",
"success": true,
"diff": {
"missingModules": ["limiter#0"],
"extraModules": [],
"paramDrift": [
{ "instanceId": "gain#0", "paramId": "gainDb#0", "host": "6.0", "target": "3.0" }
],
"connectionMismatch": []
}
}
失败:
{
"type": "refresh_link_ack",
"mode": "load",
"success": false,
"code": "LINK_NOT_FOUND",
"message": "无法从 source=host 读取链路"
}
5.3 错误码
| code | 触发场景 |
|---|---|
LINK_NOT_FOUND |
load · source=host 但 current_link.json 不存在 |
HOST_LINK_EMPTY |
verify · host 无链路数据(hostLink 字段为空) |
TARGET_LINK_NOT_FOUND |
load/verify · target 不可达(target 传输层未实现) |
UNKNOWN_MODE |
mode 字段不为 load/verify |
5.4 TuningMode 访问限制
| TuningMode | load 支持 | verify 支持 | 默认 source |
|---|---|---|---|
pc_simulation |
✅(校验 PC backend) | ✅ | 'pc_backend' |
target_live |
✅(启动时自动) | ✅ | 'target' |
legacy_compat |
❌(链路装饰性) | ❌ | — |
auto_inverse |
❌(数据流反向) | ❌ | — |
5.5 TypeScript 接口
注意:完整实现见
frontend_vue3/src/api/refreshLink.ts(已实现)。以下为协议契约快照。
// ─── §5 refresh-link 类型 ────────────────────────────────────────────────
/**
* WS 协议层的 source 字段值
* v1.0.1 起 'pc_backend' 为正式名,'host' 为 @deprecated 兼容别名
*/
export type RefreshLinkSourceWire = 'pc_backend' | 'target_dsp' | 'target_pc'
/** @deprecated 兼容期别名,下一版协议升级时移除,使用 'pc_backend' 替代 */
export type RefreshLinkSourceLegacy = 'host'
/** refresh_link WS 请求(与 refreshLink.ts 中 RefreshLinkRequest 对齐) */
export interface RefreshLinkRequestPayload {
type: 'refresh_link'
mode: 'load' | 'verify'
source?: RefreshLinkSourceWire // 默认 'host'
hostLink?: unknown // DSPLink · verify 模式必填
}
/** verify 模式的单条参数漂移 */
export interface ParamDriftEntry {
instanceId: string
paramId: string
host: string
target: string
}
/** verify 模式的单条连接不一致 */
export interface ConnectionMismatchEntry {
connectionId: string
reason: string
}
/** refresh_link diff 结果(verify 模式) */
export interface LinkDiff {
missingModules: string[]
extraModules: string[]
paramDrift: ParamDriftEntry[]
connectionMismatch: ConnectionMismatchEntry[]
}
/** refresh_link_ack WS 响应(load + verify 共用) */
export interface RefreshLinkAckPayload {
type: 'refresh_link_ack'
mode: 'load' | 'verify'
success: boolean
data?: unknown // DSPLink · load 成功时
diff?: LinkDiff // verify 成功时
code?: RefreshLinkErrorWire // 失败时
message?: string
}
/** refresh_link 后端错误码 */
export type RefreshLinkErrorWire =
| 'LINK_NOT_FOUND'
| 'HOST_LINK_EMPTY'
| 'TARGET_LINK_NOT_FOUND'
| 'UNKNOWN_MODE'
6. dsp-interop · P5↔P6 后端 IPC
状态:✅ B2 草稿完成(2026-05-27)
dsp-interop 定义 C# 后端(P5)与 DSP 算法层(P6,DLL 或嵌入式目标板)之间的二进制帧通信协议。前端不直接参与此层,但需要了解 DSP 连接状态和传输参数。
6.1 二进制帧格式(TLV 风格)
| 段位 | 字节数 | 说明 |
|---|---|---|
| HEADER | 2 | 魔数:0x55 0xAA(帧起始) |
| CMD | 1 | 命令类型(见 §6.2) |
| SEQ | 2 | 序列号(Little-Endian · 用于丢帧检测) |
| LEN | 2 | DATA 字节数(LE) |
| DATA | LEN | 命令负载(格式见 §6.3) |
| CRC8 | 1 | CRC-8 校验(覆盖 CMD+SEQ+LEN+DATA) |
6.2 CMD 命令表
| CMD | 值 | 用途 |
|---|---|---|
CMD_SET_PARAM |
0x01 |
设置单个模块参数 |
CMD_SET_LINK |
0x02 |
发送完整链路结构(v3/v4 格式) |
CMD_PING |
0x03 |
心跳探测(DATA 为空) |
6.3 DATA 格式
CMD_SET_PARAM (0x01)
| valueType | 含义 | valueBytes |
|---|---|---|
0x01 |
float32 | 4 bytes LE |
0x02 |
int32 | 4 bytes LE |
0x03 |
bool | 1 byte (0/1) |
0x04 |
string | UTF-8 raw(LEN - 前缀字节) |
CMD_SET_LINK (0x02) — v4 格式
[schemaVer 1B][numChannels 2B LE][sampleRate 2B LE][blockSize 2B LE]
[M 2B LE] × M 个 ModuleEntry
[C 2B LE] × C 个 ConnEntry
ModuleEntry(固定 40 字节):
ConnEntry(8 字节):
CMD_PING (0x03)
DATA 为空(LEN=0)。后端收到 PONG(0x83)视为连通。
6.4 物理传输层(3 种)
| 传输类型 | 连接参数 | 用途 |
|---|---|---|
| TCP | host:port(默认 127.0.0.1:9000) |
PC 后端 ↔ PC DLL / 仿真器 |
| Serial | comPort@baudRate(如 COM3@115200) |
嵌入式目标板(RS-232/UART) |
| ADB FIFO | Android 设备 adb forward | Android DSP 调试通道 |
DspConnection.IsConnected可查询当前连接状态。ConnectionType返回"tcp"/"serial"/"adb"/"none"。
6.5 TX 队列背压策略
- TX Channel 有界 512 帧(
BoundedChannelFullMode.DropOldest) - 满时丢旧帧(允许丢参数帧;链路帧因体积大更容易触发丢帧,需在上层重试)
- 调用方非阻塞:
DeliverAsync(frame)立即返回,不等 ACK
6.6 DLL Mock 模式
当 DynChainInterop.IsDllAvailable = false 时:
- DeliverAsync 直接丢弃帧(无实际 DSP 通信)
- HandleSetParam 仍更新 paramStore(保证前端状态一致性)
- 返回 { success: true } 避免前端报错
6.7 WS 前端通知(DSP 连接状态变更)
后端在 DSP 连接状态变化时,通过广播推送到所有前端客户端:
TypeScript 接口:
// ─── §6 dsp-interop 类型 ─────────────────────────────────────────────────
/** DSP 物理传输类型 */
export type DspTransportType = 'tcp' | 'serial' | 'adb' | 'none'
/** 后端推送的 DSP 连接状态通知 */
export interface DspStatusPayload {
type: 'dsp_status'
connected: boolean
protocol: DspTransportType
target: string // "host:port" / "comPort@baud" / "adb" / ""
}
/** P5↔P6 二进制帧命令类型 */
export const enum DspFrameCmd {
SET_PARAM = 0x01,
SET_LINK = 0x02,
PING = 0x03,
}
/** DSP 连接配置(前端 store 保存) */
export interface DspConnectionConfig {
transport: DspTransportType
host?: string // tcp
port?: number // tcp
comPort?: string // serial
baudRate?: number // serial
adbSerial?: string // adb · 设备序列号
}
7. source-sink-api · P5 音源/录制/设备 API
状态:✅ B3 草稿完成(2026-05-27)
source-sink-api 定义 P5 C# 后端的音频设备、引擎、录制控制接口。与 §4 sourceList 的关系:§4 是链路层(信号类型/参数),§7 是引擎层(设备选择/录制控制)。
7.1 RefreshLinkSource 正式命名
📌 用户拍板(2026-05-27 14:29):
'pc_backend'为正式名,后端旧'host'标@deprecated,兼容期 1 个版本。
| 语义 | 正式值 | deprecated 别名 |
|---|---|---|
| PC 后端仿真模式 | 'pc_backend' |
'host'(兼容期接受) |
| DSP 目标板 | 'target_dsp' |
— |
| 目标 PC | 'target_pc' |
— |
7.2 音频设备管理 WS 消息
7.2.1 list_audio_devices / get_audio_devices
请求:
响应(audio_devices_list):
{
"type": "audio_devices_list",
"outputs": [
{ "id": "wasapi:{guid}", "name": "扬声器 (Realtek Audio)", "type": "wasapi" }
],
"inputs": [
{ "id": "wasapi:{guid}", "name": "麦克风 (Realtek Audio)", "type": "wasapi" }
]
}
7.2.2 set_audio_device
响应:{ "type": "set_audio_device_ack", "success": true }
7.2.3 start_audio_engine / stop_audio_engine
响应:{ "type": "audio_engine_status", "running": true/false, "error": null|string }
7.3 Sink 录制控制 WS 消息
7.3.1 sink_record_start
{
"type": "sink_record_start",
"instanceId": "sink_v1#0",
"outDir": "./output",
"fileName": "record_01.wav",
"channels": [0, 1]
}
7.3.2 sink_record_stop
响应(sink_record_ack):
{
"type": "sink_record_ack",
"instanceId": "sink_v1#0",
"wavPath": "./output/record_01.wav",
"wavUrl": "/output/record_01.wav",
"sizeBytes": 192044
}
7.4 REST 端点
| 方法 | 路径 | 说明 |
|---|---|---|
GET |
/api/status |
后端状态(引擎 running / DSP 连接 / 在线客户端数) |
GET |
/api/link |
当前链路 JSON 快照 |
GET |
/api/devices |
TODO:设备列表 REST stub(当前由 WS 提供) |
7.5 TypeScript 接口
// ─── §7 source-sink-api 类型 ─────────────────────────────────────────────
/** 音频设备描述 */
export interface AudioDeviceEntry {
id: string // "wasapi:{guid}" / "asio:{name}"
name: string
type: 'wasapi' | 'asio' | 'soft'
}
/** list_audio_devices 响应 */
export interface AudioDevicesListPayload {
type: 'audio_devices_list'
outputs: AudioDeviceEntry[]
inputs: AudioDeviceEntry[]
}
/** start_audio_engine 请求 */
export interface StartAudioEngineRequest {
type: 'start_audio_engine'
sampleRate?: number // 默认 48000
channels?: number // 默认 2
blockSize?: number // 默认 64
deviceId?: string // 可选,覆盖当前设备
}
/** 后端推送引擎状态变化 */
export interface AudioEngineStatusPayload {
type: 'audio_engine_status'
running: boolean
error?: string
}
/** sink_record_start 请求 */
export interface SinkRecordStartRequest {
type: 'sink_record_start'
instanceId: string
outDir?: string // 默认 './output'
fileName?: string // 默认自动生成时间戳
channels?: number[] // 默认全通道
}
/** sink_record_stop 请求 */
export interface SinkRecordStopRequest {
type: 'sink_record_stop'
instanceId: string
}
/** sink_record_ack 响应 */
export interface SinkRecordAckPayload {
type: 'sink_record_ack'
instanceId: string
wavPath: string
wavUrl: string // 静态文件路径(/output/{file})
sizeBytes: number
}
8. test-runner-api · P4-xitest 后端测试协议
状态:✅ B3 草稿完成(2026-05-27)
test-runner-api 定义 P4-xitest 前端与 P5 后端之间的集成测试/E2E 测试调用协议,基于 REST(不走 WS)。
8.1 测试模式覆盖范围
| TestMode | 触发方式 | 后端端点 |
|---|---|---|
unit |
前端本地 /dev-api/test/vitest/run(Vite 插件) |
不涉及后端 |
integration |
REST → 后端 | POST /api/test/run-case |
e2e |
REST → 后端 | POST /api/test/run-project |
perf |
stub(TBD) | — |
visual |
stub(TBD) | — |
8.2 REST 端点详细规范
8.2.1 GET /api/test/list-tests
列出所有可运行的集成测试项目及场景。
响应:
{
"success": true,
"projects": [
{
"folder": "project-A",
"name": "project-A",
"scenarios": [
{
"file": "default",
"configFilePath": ".../integrationtest/default.json",
"config": { "durationSec": 3.0, "sinkCriteria": {} }
}
]
}
]
}
8.2.2 POST /api/test/run-case
运行单个测试用例(integration mode)。
请求:
响应:
{
"success": true,
"summary": "PASS",
"caseId": "case_001",
"projectFolder": "project-A",
"durationSec": 3.0,
"sinks": [
{
"sinkId": "sink_v1#0",
"sourceId": "source_sine#0",
"pass": true,
"wavPath": "./output/run_case_001_sink_v1_0_1234567890.wav",
"wavUrl": "/output/run_case_001_sink_v1_0_1234567890.wav",
"wavSizeBytes": 576044,
"measuredRmsDb": -6.02,
"measuredPeakFreqHz": 440.1,
"criteriaDetails": [
{ "check": "rms_range", "pass": true, "expected": "[-10, -3] dBFS", "measured": "-6.02" },
{ "check": "peak_freq", "pass": true, "expected": "440 ± 5 Hz", "measured": "440.1" }
]
}
],
"timestamp": "2026-05-27T14:00:00Z"
}
8.2.3 POST /api/test/run-project
运行整个项目的集成测试场景(e2e mode)。
请求:
响应格式与 run-case 相同(内部转发至 run-case)。
8.2.4 POST /api/test/report
生成完整测试报告(带 WAV + pysidecar 频域分析)。
请求:
响应(含 RMS + 频响数据):
{
"success": true,
"summary": "PASS",
"wavPath": "./output/report_1234567890.wav",
"wavUrl": "/output/report_1234567890.wav",
"wavSizeBytes": 384044,
"rms": [
{ "channel": 0, "rms_db": -6.02, "peak_db": -3.01, "pass": true },
{ "channel": 1, "rms_db": -6.05, "peak_db": -3.02, "pass": true }
],
"freqResponse": { "freq_hz": [20, 50, ...], "magnitude_db": [-3.0, -3.1, ...] },
"timestamp": "2026-05-27T14:00:00Z"
}
8.3 TypeScript 接口
// ─── §8 test-runner-api 类型 ─────────────────────────────────────────────
/** GET /api/test/list-tests 响应 */
export interface TestListResponse {
success: boolean
projects: TestProject[]
}
export interface TestProject {
folder: string
name: string
scenarios: TestScenario[]
}
export interface TestScenario {
file: string
configFilePath: string
config: Record<string, unknown>
}
/** POST /api/test/run-case 请求 */
export interface RunCaseRequest {
caseId: string
durationSec?: number
}
/** POST /api/test/run-project 请求 */
export interface RunProjectRequest {
projectFolder: string
scenario?: string // 默认 'default'
durationSec?: number
}
/** 单个 sink 的测试结果 */
export interface SinkTestResult {
sinkId: string
sourceId?: string
pass: boolean
wavPath: string
wavUrl: string
wavSizeBytes: number
measuredRmsDb?: number
measuredPeakFreqHz?: number
measuredDurationSec?: number
criteriaDetails: CriteriaCheckEntry[]
}
export interface CriteriaCheckEntry {
check: string
pass: boolean
expected?: string
measured?: string
reason?: string
}
/** POST /api/test/run-case 响应 */
export interface RunCaseResponse {
success: boolean
summary: 'PASS' | 'FAIL'
caseId: string
projectFolder?: string
durationSec: number
sinks: SinkTestResult[]
timestamp: string
log?: string
dspLog?: string[]
}
/** POST /api/test/report 请求 */
export interface RunReportRequest {
durationSec?: number
projectFolder?: string
}
9. py-subprocess · P7-pysidecar 子进程协议
状态:✅ B3 草稿完成(2026-05-27)
py-subprocess 定义 P5 C# 后端与 P7 Python pysidecar 之间的进程管理 + HTTP 通信协议。pysidecar 以 FastAPI 进程形式运行,监听 http://localhost:8001,P5 通过 HTTP 调用其分析接口。
9.1 进程生命周期
| 阶段 | 机制 | 说明 |
|---|---|---|
| spawn | P5 启动时自动 Process.Start |
uvicorn pysidecar.main:app --port 8001 |
| heartbeat | P5 每 30s GET /health |
返回 {"status":"ok"} 视为存活 |
| restart | heartbeat 失败 3 次 → 重启 | 超时 10s 视为无响应 |
| kill | P5 关闭时 Process.Kill |
确保端口释放 |
当前实现:P5 在
Program.cs启动时 spawn pysidecar;heartbeat 为被动(仅在有分析请求时探活)。kill 已通过进程树管理。
9.2 HTTP 接口规范
基础 URL:http://localhost:8001(可通过 PYSIDECAR_PORT 环境变量覆盖)
9.2.1 GET /health
9.2.2 POST /analyze/rms
请求:
响应:
9.2.3 POST /analyze/freq_response
请求:
{
"output_wav_b64": "<base64 WAV>",
"input_wav_b64": null,
"channel": 0,
"config": {
"point_freqs": [440.0, 1000.0, 4000.0]
}
}
响应:
{
"freq_hz": [20.0, 50.0, 100.0, "..."],
"magnitude_db": [-3.0, -3.1, -2.9, "..."],
"peak_freq_hz": 440.1,
"point_magnitudes": {
"440.0": -6.02,
"1000.0": -6.15,
"4000.0": -5.98
}
}
9.2.4 其他分析接口(摘要)
| 端点 | 用途 |
|---|---|
POST /analyze/thd |
谐波失真分析(THD+N) |
POST /analyze/octave_band |
倍频程频带 SPL |
POST /analyze/loudness |
LUFS / A-加权 SPL |
POST /analyze/phase |
相位分析 + 相位延迟 |
POST /analyze/tdoa |
到达时间差(GCC-PHAT) |
POST /analyze/room_acoustics |
RT60 / C50 / D50 室内声学 |
POST /analyze/coherence |
输入/输出信号相干性 |
POST /analyze/waterfall |
瀑布图(时频域) |
POST /analyze/weighted_spl |
加权 SPL(A/C/Z) |
POST /analyze/signal_gen |
测试信号生成(正弦/扫频/脉冲/白噪声) |
9.3 P5→P7 调用约定
- 传输格式:WAV 文件以 Base64 编码传送(不用文件路径,避免跨进程文件锁)
- 超时:每次请求 30s(分析大文件时可能接近上限)
- 并发:P5 串行调用,不并发(pysidecar 无全局锁,可并发,但 P5 端顺序发送)
- 降级:pysidecar 不可达时,后端返回
{ success: true, rms: [], freqResponse: null }— 测试仍执行,仅缺少频域指标
9.4 TypeScript 接口
// ─── §9 py-subprocess 类型 ───────────────────────────────────────────────
/** GET /health 响应 */
export interface PySidecarHealthPayload {
status: 'ok' | 'error'
version: string
}
/** POST /analyze/rms 请求 */
export interface RmsAnalysisRequest {
wav_b64: string // Base64 WAV
channel: number
}
/** POST /analyze/rms 响应 */
export interface RmsAnalysisResult {
rms_db: number
peak_db: number
duration_sec: number
pass_: boolean
}
/** POST /analyze/freq_response 请求 */
export interface FreqResponseRequest {
output_wav_b64: string
input_wav_b64?: string | null
channel: number
config?: {
point_freqs?: number[] // 需要精确测量的频率点
}
}
/** POST /analyze/freq_response 响应 */
export interface FreqResponseResult {
freq_hz: number[]
magnitude_db: number[]
peak_freq_hz: number
point_magnitudes: Record<string, number> // "440.0" → -6.02 dB
}
/** 后端 WS 推送的 pysidecar 状态通知 */
export interface PySidecarStatusPayload {
type: 'pysidecar_status'
running: boolean
port: number
error?: string
}
10. 变更管理
10.1 freeze 后的变更流程
- 任一 agent 提出变更 → 在 KANBAN 标
CONTRACT-CHANGE-REQ: §X - AIOS 评估影响 → 升级人类
- 人类拍板 → 写 ADR-AIOS-NN
- ClaudeB 改本文档 + bump version 到 v1.1 / v2.0
- Continue 打新 tag
contract-v1.1.0
10.2 版本号语义
- patch(v1.0.x):错别字 / examples 补充 · 不影响实施
- minor(v1.x.0):新增协议 · 向后兼容
- major(vX.0.0):破坏性变更 · 触发回滚或迁移计划
11. 关联文档
../index.md· AIOS 总览../agents/Repository-Partition.md· ClaudeB 独占写权限说明../prompts/templates/claude-b-backend.md· 填充指南../../30-frontend-vue3/00-baseline-alignment.md· 18 项协议改造对照表(freeze 前的临时依据)../../30-frontend-vue3/00-master-plan.md§7 · 7 ADR-D4-01~07
12. 演进历史
| 版本 | 日期 | 状态 | 责任人 | 变化 |
|---|---|---|---|---|
| v0.1-skeleton | 2026-05-19 | pending-claude-a | AIOS(占位) | 12 章节骨架建立 |
| v0.6-§1-§6-draft | 2026-05-27 | draft-b2 | ClaudeB | §1-§6 草稿填充(B1+B2) |
| v0.9-§1-§9-draft | 2026-05-27 | draft-b3 | ClaudeB | §7-§9 草稿填充 + §5 RefreshLinkSource 命名统一(B3) |
| v1.0 | 2026-05-27 | frozen | ClaudeB | 全文首版 · freeze · tag contract-v1.0 · HARD-DEADLINE B4 |
📌 再次提醒:本文件 freeze 前任何具体内容均为占位。智能体在 Day 1-4 实施任务时,不要引用本文件,而是引用
baseline-alignment.md。