# Knowledge Runtime State Bootstrap And Observability

> 覆盖 Blade 的进程启动目录状态、跨异步上下文 cwd 隔离、共享 Vanilla Store、日志与运行环境观测、运行时包信息、Session ID、原生目录选择和项目注册。 使用时机：调整 CLI 启动顺序、排查多 workspace 状态串扰、从 React 外访问 Store、增加运行态观测、修改版本识别、Session 标识或项目选择/绑定流程。 不包含：配置层级与持久化路由见 layered-configuration-and-runtime-settings，事件日志与持久投影见 durable-transcript-and-event-projection，容量和关闭编排见 capacity-lifecycle-and-egress。 关键词：bootstrap state, getCwd, runWithCwdOverride, vanillaStore, ensureStoreInitialized, Logger, streamDebug, getEnvironmentContext, getVersion, createSessionId, NativeDirectoryPicker, ProjectRegistry。

- Skill: `echovic/knowledge-runtime-state-bootstrap-and-observability` (Agent Skill)
- Install (CLI): `npx skillmds@latest add echovic/knowledge-runtime-state-bootstrap-and-observability`
- Raw SKILL.md: https://api.skillmd.com/api/skills/echovic/knowledge-runtime-state-bootstrap-and-observability/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Web & Frontend
- Author: echovic (https://skillmd.com/u/echovic)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/echovic/knowledge-runtime-state-bootstrap-and-observability

---


## Module Structure

该组件提供各运行表面共享的进程级基础状态与观测入口；它区分启动目录、当前工作区和稳定项目根，并把 UI 状态、日志、环境探测及项目发现收敛到可被 Agent、CLI、Server、工具和扩展复用的 API。

### Directory Layout
- `packages/cli/src/bootstrap/state.ts` — 惰性初始化的 cwd、originalCwd 与 projectRoot 三态
- `packages/cli/src/store/` — React 与非 React 共用的 Zustand Store、切片、类型和细粒度选择器
- `packages/cli/src/logging/` — 分类 JSONL 日志与独立流式调试日志
- `packages/cli/src/utils/cwd.ts` — 基于 AsyncLocalStorage 的并发 cwd 覆写
- `packages/cli/src/utils/environment.ts` — 项目根发现、环境提示和目录概览
- `packages/cli/src/utils/packageInfo.ts` — 源码与打包布局兼容的运行时包信息
- `packages/cli/src/utils/sessionId.ts` — 可安全用于存储路径的 Session ID
- `packages/cli/src/services/DirectoryPicker.ts` — macOS、Windows 与 Linux 原生目录选择
- `packages/cli/src/services/ProjectRegistry.ts` — 规范化项目路径的持久绑定注册表

### Key Entry Points
- `getCwd()` / `runWithCwdOverride()` — 获取当前执行上下文的 workspace，或为异步调用链临时覆写
- `vanillaStore` / `useBladeStore()` / `ensureStoreInitialized()` — 共享状态实例、React 订阅与幂等配置初始化
- `createLogger()` / `setLoggerSessionId()` / `streamDebug()` — 分类日志、会话日志路由与流式专项诊断
- `getEnvironmentContext()` / `getVersion()` / `createSessionId()` — 构造环境上下文、读取运行版本和生成存储安全标识
- `NativeDirectoryPicker.pick()` / `ProjectRegistry.list()` — 选择本机目录并读取规范化项目集合

## API Surface

### Workspace Location
- `getCwd()` — 优先返回当前 AsyncLocalStorage 覆写，否则返回进程级 cwd
- `runWithCwdOverride(cwd, fn)` — 在同步及后续异步调用链内隔离 workspace
- `getOriginalCwd()` — 返回 CLI 被调用时的原始目录，用于解析用户传入的相对路径
- `getProjectRoot()` — 返回启动后保持稳定的项目标识根
- `findProjectRoot(startDir)` / `setCwd(newPath, relativeTo?)` — 发现工作区根并更新规范化的全局 cwd

### Shared Store
- `useBladeStore(selector)` — React 对共享 Vanilla Store 的细粒度订阅入口
- `getState()` — Agent、服务与命令读取当前 Store 快照
- `sessionActions()` / `appActions()` / `configActions()` — 面向不同状态域的动作入口
- `ensureStoreInitialized()` — 以共享 Promise 合并并发配置初始化
- `useCurrentStreamingBuffer()` — 以浅比较订阅流式行缓冲、尾部、计数和版本

### Observability
- `createLogger(category, options?)` — 创建分类日志器，文件输出与终端调试过滤相互独立
- `Logger.setGlobalDebug(config)` — 动态设置全局终端日志开关或分类过滤
- `setLoggerSessionId(sessionId)` — 切换后续 JSONL 日志的文件名
- `streamDebug(source, message, data?)` — 向独立流式诊断文件追加记录且不向调用方抛错
- `getEnvironmentContext(options?)` — 组装系统、Git、脚本和目录信息供系统提示使用

### Runtime Identity And Project Discovery
- `getPackageInfo()` / `getVersion()` — 从运行时邻近 package.json 读取并缓存实际发布信息
- `createSessionId(prefix?, size?)` — 校验前缀后生成可用于文件名的 ID
- `NativeDirectoryPicker.pick()` — 跨平台打开至多一个并发原生目录选择器
- `ProjectRegistry.bind()` / `list()` / `unbind()` — 规范化、持久化并查询项目绑定

## Usage Examples

### CLI 启动时固定项目根
```typescript
const invocationCwd = process.cwd();
const detectedRoot = findProjectRoot(invocationCwd);
setCwd(detectedRoot);
setProjectRoot(getCwd());
```

### 启动 Server 前确保共享 Store 可用
```typescript
await ensureStoreInitialized();
const server = await BladeServer.listenAsync(opts);
```

### TUI 初始化完成后绑定会话日志
```typescript
const state = getState();
const sessionId = state.session.sessionId;
setLoggerSessionId(sessionId);
```

## Gotchas
- `originalCwd`、`cwd` 与 `projectRoot` 不能互换：CLI 相对参数以调用目录解析，运行工具使用可变 cwd，历史、Skill 和 Session 等项目身份使用启动后固定的 projectRoot (`packages/cli/src/bootstrap/state.ts`, `packages/cli/src/blade.tsx`, `packages/cli/src/cli/settings.ts`, `packages/cli/src/mcp/loadMcpConfig.ts`)
- CLI 必须先执行 `findProjectRoot()`、`setCwd()` 和 `setProjectRoot()` 再启动依赖 workspace 的流程，否则配置、信任与资源发现会以调用子目录为根 (`packages/cli/src/blade.tsx`, `packages/cli/src/utils/environment.ts`)
- 并发 Session 或子代理不能用全局 `setCwd()` 做临时切换；应使用 `runWithCwdOverride()`，其 AsyncLocalStorage 覆写会沿异步调用链传播且不污染其他执行上下文 (`packages/cli/src/utils/cwd.ts`, `git:4543542b`)
- `setCwd()` 解析相对路径时默认基于真实 `process.cwd()` 而不是当前 AsyncLocalStorage 覆写；在隔离上下文内传相对路径必须显式提供 `relativeTo` (`packages/cli/src/utils/environment.ts`, `packages/cli/src/utils/cwd.ts`)
- Store 是否初始化仅以 `config.config !== null` 判断；CLI、Server、ACP 和 Hook 等非 React 入口在读取配置前都必须等待 `ensureStoreInitialized()`，并发调用会共享同一个初始化 Promise (`packages/cli/src/store/vanilla.ts`, `packages/cli/src/commands/serve.ts`, `packages/cli/src/acp/BladeAgent.ts`)
- `resetSession()` 只重置 Zustand 内的 Session 字段并保留 sessionId，不会隐式清空模块级 `conversationProjection`；切换事件流时还要显式调用 `resetConversationProjection()` (`packages/cli/src/store/slices/sessionSlice.ts`)
- `Logger` 的 debug 开关只控制终端输出，所有级别仍同步追加到 JSONL；不要通过关闭 debug 假设磁盘日志已停用 (`packages/cli/src/logging/Logger.ts`)
- 日志文件路由使用进程级 `currentSessionId` 而非异步上下文；TUI 绑定 Session 前的记录进入 `blade-default.jsonl`，该机制也不能区分并发 Server Session (`packages/cli/src/logging/Logger.ts`, `packages/cli/src/ui/App.tsx`)
- 日志目录初始化只尝试一次；权限或创建失败后会缓存不可用状态，当前进程内即使外部修复目录也不会自动重试 (`packages/cli/src/logging/Logger.ts`)
- `streamDebug()` 不受 `Logger.setGlobalDebug()` 控制，并会在每个 storage root 的首次调用时重写 `stream-debug.log`；它是临时专项诊断通道，不是普通分类日志 (`packages/cli/src/logging/StreamDebugLogger.ts`, `packages/cli/src/ui/utils/loopEventHandler.ts`)
- `ProjectRegistry` 遇到缺失、损坏或版本不匹配的注册表会按空集合继续，并始终临时注入当前项目；因此列表只剩当前项目不代表绑定从未存在 (`packages/cli/src/services/ProjectRegistry.ts`, `packages/cli/tests/unit/services/project-registry.test.ts`)
- 原生目录选择服务本身不校验请求来源，只有 Server 路由拒绝非本机 Origin；新增调用表面时必须复用同等访问边界 (`packages/cli/src/services/DirectoryPicker.ts`, `packages/cli/src/server/routes/projects.ts`)
- Linux 目录选择仅在命令不存在时从 `zenity` 回退到 `kdialog`；已安装选择器返回真实错误时会立即失败，而空输出的退出码 1 被视为用户取消 (`packages/cli/src/services/DirectoryPicker.ts`, `packages/cli/tests/unit/platform/services/directory-picker.test.ts`)

## Architecture
- `bootstrap/state.ts` 保存进程级三态，`cwd.ts` 只在其上增加异步上下文覆盖；绝大多数业务代码应依赖 `getCwd()`，只有启动和测试夹具直接操作底层状态 (`packages/cli/src/bootstrap/state.ts`, `packages/cli/src/utils/cwd.ts`)
- 单一 `vanillaStore` 同时服务 Ink React 订阅和 Agent、服务、命令的命令式访问；Store 不使用 persist 中间件，配置和会话持久化分别交给 ConfigService 与 Context/JSONL (`packages/cli/src/store/index.ts`, `packages/cli/src/store/vanilla.ts`)
- Session Store 同时投影持久消息、临时流式缓冲和 Provider/停滞/重试等观测事件；命令结束或取消会集中清除易过期的运行态诊断，避免下一轮展示旧状态 (`packages/cli/src/store/slices/sessionSlice.ts`, `packages/cli/src/store/slices/commandSlice.ts`, `git:2aa2b22b`, `git:e6bd4e15`)
- `getEnvironmentContext()` 默认采集 Git 分支、工作树、最近提交、根目录清单和常用脚本；单项命令失败只省略对应片段，不会阻断提示构建 (`packages/cli/src/utils/environment.ts`, `packages/cli/tests/unit/platform/utils/environment.test.ts`, `git:7680e3eb`)
- 项目根发现会继续向上寻找 `.git`、`.blade` 或 `.claude`，仅在没有这些标记时回退到最近的 package.json，因此 monorepo 子包默认归属仓库根 (`packages/cli/src/utils/environment.ts`, `packages/cli/tests/unit/platform/utils/environment.test.ts`)
- 项目注册表以 realpath 作为去重身份，保留已失联项目供用户修复或解绑，并把当前项目固定排在名称排序之前 (`packages/cli/src/services/ProjectRegistry.ts`, `packages/cli/tests/unit/services/project-registry.test.ts`)

## Decisions
- cwd 隔离采用 AsyncLocalStorage 而不是 `process.chdir()`，使并行子代理、ACP 和真实 API 驱动可在同一进程内保持各自 workspace (`packages/cli/src/utils/cwd.ts`, `packages/cli/tests/support/browserToolAcpDriver.ts`)
- Zustand 只承担当前进程读模型，不承担磁盘权威状态；这一边界允许 Session 事件日志和配置服务独立处理恢复、原子写与版本迁移 (`packages/cli/src/store/vanilla.ts`, `packages/cli/src/store/types.ts`)
- 包版本改为从源码或 bundle 邻近目录向上搜索名称匹配的 package.json，避免发布包仍报告构建时导入的旧版本；找不到时才使用导入值或 `BLADE_VERSION` (`packages/cli/src/utils/packageInfo.ts`, `git:092d356b`)
- Session ID 统一由带受限前缀的 nanoid 生成，替代可能包含路径非法字符的临时标识，使 TUI、Web、ACP、任务和子代理共享存储安全格式 (`packages/cli/src/utils/sessionId.ts`, `packages/cli/src/store/slices/sessionSlice.ts`, `git:d148a8fa`)

## Patterns
- React 消费者通过专用 selector 订阅最小状态片段，对对象组合使用 `useShallow`，空模型列表复用常量引用；非 React 消费者通过 `getState()` 和动作访问器共享同一实例 (`packages/cli/src/store/index.ts`, `packages/cli/src/store/selectors/index.ts`, `packages/cli/src/store/vanilla.ts`)
- Store action 在并发清理时使用身份或请求 ID 防止迟到回调覆盖新状态，例如 `clearAbortController(expectedController)` 和旁路对话完成动作都先核对当前所有者 (`packages/cli/src/store/slices/commandSlice.ts`, `packages/cli/src/store/slices/appSlice.ts`)
- 项目绑定写入遵循 mutex 内 read-modify-write、原子替换和 `0600` 文件权限；目录本身同步收紧到 `0700` (`packages/cli/src/services/ProjectRegistry.ts`)
- `NativeDirectoryPicker.pick()` 缓存进行中的 Promise，并在 settle 后清空，多个并发 Web 请求只会打开一个系统对话框 (`packages/cli/src/services/DirectoryPicker.ts`, `packages/cli/tests/unit/platform/services/directory-picker.test.ts`)

## Dependencies
- 原生目录选择分别依赖 macOS `osascript`、Windows PowerShell Forms、Linux `zenity` 或 `kdialog`，统一受 15 分钟超时和 64 KiB 输出上限约束 (`packages/cli/src/services/DirectoryPicker.ts`)
- 日志和项目注册都从 Blade storage root 派生文件位置；前者容忍写入失败，后者使用 `write-file-atomic` 并向调用方传播非 ENOENT/语法错误 (`packages/cli/src/logging/Logger.ts`, `packages/cli/src/services/ProjectRegistry.ts`)

## Consumer Analysis
- TUI 是共享 Store 的最高频消费者，通过 selector 渲染消息、焦点和运行态，并通过动作协调初始化、模型、权限和日志 Session 绑定 (`packages/cli/src/ui/App.tsx`, `packages/cli/src/ui/hooks/useAgent.ts`, `packages/cli/src/store/selectors/index.ts`)
- Server 与 CLI 命令在无 React 环境调用 `ensureStoreInitialized()`、配置动作、环境版本和项目注册服务，启动顺序错误会直接影响 Web/Headless 可用性 (`packages/cli/src/commands/serve.ts`, `packages/cli/src/server/server.ts`, `packages/cli/src/server/routes/projects.ts`)
- Agent、SessionRuntime 与子代理使用共享 Store 投影运行状态，并以 `getCwd()` 或 AsyncLocalStorage 覆写确定每次执行的 workspace (`packages/cli/src/agent/Agent.ts`, `packages/cli/src/agent/runtime/SessionRuntime.ts`, `packages/cli/src/agent/subagents/BackgroundAgentManager.ts`)
- 工具、Hooks、MCP、Plugins 和 LSP 广泛依赖 `getCwd()` 与分类 Logger；这些扩展必须优先使用显式 `context.workspaceRoot`，缺失时才回退进程上下文 (`packages/cli/src/tools/execution/ToolExecutor.ts`, `packages/cli/src/hooks/HookManager.ts`, `packages/cli/src/plugins/PluginRegistry.ts`, `packages/cli/src/lsp/LspSessionManager.ts`)
- SessionService、PersistentStore 和协议适配层消费运行版本与存储安全 ID，把同一身份格式写入会话元数据、JSONL 路径、ACP 握手和 MCP 客户端信息 (`packages/cli/src/services/SessionService.ts`, `packages/cli/src/context/storage/PersistentStore.ts`, `packages/cli/src/acp/BladeAgent.ts`, `packages/cli/src/mcp/McpClient.ts`)

