LifeOS PROJECTS.md 项目注册表实战指南:路由别名、部署门禁与记忆系统集成
2026/9/14 22:10:12 网站建设 项目流程

LifeOS PROJECTS.md 项目注册表实战指南:路由别名、部署门禁与记忆系统集成

【免费下载链接】LifeOS⛰️ The Life Operating System — an intent engineering platform that moves you from your current state to your ideal state, in life and work.项目地址: https://gitcode.com/GitHub_Trending/pe/LifeOS

PROJECTS.md 是 LifeOS 身份层(USER/)中的项目注册表:它以一张紧凑的表格登记你正在维护的全部项目(本地路径、线上 URL、部署命令、技术栈),并配套一组"路由别名",让 DA 在每次会话启动时就能把 "my blog""the workspace" 这类自然语言指代准确路由到对应代码库。读完本文,你将掌握 PROJECTS.md 的完整字段语义、初始化与维护流程,并理解 Atlas、Pulse、ProposalScope、DeployRegistrationGate 等子系统是如何以这张表格为唯一事实源进行解析、路由与强制校验的。

PROJECTS.md 在 LifeOS 中的定位

在 USER/README.md 描述的目录布局中,LIFEOS/USER/是 LifeOS 的"身份层",其中明确标注了:

LIFEOS/USER/ ├── PROJECTS/PROJECTS.md # Project registry + routing aliases (loaded at startup)

PROJECTS.md 是五个"启动即加载"的身份文件之一。在 CLAUDE.template.md 中,它与其他四个身份文件(PRINCIPAL_TELOS、PRINCIPAL_IDENTITY、DA_IDENTITY、OPERATIONAL_RULES)一起被声明为顶层@-import

# @LIFEOS/USER/TELOS/PRINCIPAL_TELOS.md # @LIFEOS/USER/PRINCIPAL/PRINCIPAL_IDENTITY.md # @LIFEOS/USER/DIGITAL_ASSISTANT/DA_IDENTITY.md # @LIFEOS/USER/PROJECTS.md # @LIFEOS/USER/CONFIG/OPERATIONAL_RULES.md

从源码结构看,这些 import 默认处于注释状态,由 agentic 的/LifeOS setup流程(通过 ActivateImports.ts)在 USER 脚手架填充完成后统一激活。由于 Claude Code 不会跟随被导入文件内部的传递式@-import,所以每个身份文件都必须在该路由表顶层单独列出。这意味着:一旦激活,每次会话启动时 PROJECTS.md 的全文都会被加载进 DA 的上下文——这也正是 ConfigSystem.md 把它归类为"USER 项目注册表(@-imported)"、lifeos-context.ts 将其纳入每次会话 CONTEXT 注入块的原因。

表格结构与字段语义

原文档给出的模板表格如下:

ProjectPathURLDeployStack
(interview — first project)~/code/exampleexample.combun run deployTS, React

五个字段的语义如下:

  • Project(项目名):DA 用于指代该项目的规范名称。建议用加粗**Name**包裹(ProposalScope 与 Pulse 都会优先解析加粗内容),并可附加状态标记,如🎯(system-of-record,系统事实源)、🚨(sensitive,敏感)、🚧(建设中)、🚀等,或(in design)(decommissioned)(concept)这类斜体状态。
  • Path(本地路径):代码库在本机的绝对路径,如`~/code/example`。反引号会被解析器剥除。
  • URL(线上地址):该项目的公网域名或完整 URL,可以是裸域名、带端口的 host 或完整https://链接;-表示无线上地址,~~删除线~~表示已下线的基础设施(退役行不会被渲染为链接)。
  • Deploy(部署命令):可复制的部署命令,如bun run deploy。命令中合法的管道符需要用\|转义(如grep … \| xargs),解析器会把转义还原为字面|
  • Stack(技术栈):项目技术栈或一句话描述,如TS, React。这一列历史上经历过重命名(Stack → ISA / detail),因此解析器不锁定末列列名。

解析器的真实读取规则

Pulse 的 projects.ts 是理解这张表格格式约束的最佳教材(它是只读表面,"零数据持有":每次请求实时解析 markdown)。其parseProjects()的规则包括:

  • 表头必须同时包含ProjectDeploy关键词(/\bProject\b/i/\bDeploy\b/i),解析器以此为定位锚点;
  • 行必须至少有 5 个单元格,否则跳过;
  • 项目名提取优先取**加粗**片段,随后剥离状态 emoji、(in design|decommissioned|concept)等描述性标记;
  • URL 列会尝试还原为真实https://链接(localhost则补http://),删除线开头的链接返回null
  • 另支持可选的## Open Sessions to Resume小节:会话标签以项目名为前缀时,该项目被标记为openSession

Atlas 的项目收集器 Projects.ts 则遵循略有不同的解析约定:它只解析## Routing Aliases之前的"主表格",跳过表头与分隔线,并从单元格中提取两类观测:

  • SERVES 边:从 URL 列提取域名(跳过localhost.langithub.com),建立"项目 → 域名"的服务关系;
  • DEPLOYED_FROM 边:行内任意位置出现github.com/owner/repo时,建立"项目 → GitHub 仓库"的部署来源关系。

两个解析器都强调故障降级而非抛错:源文件缺失时返回空结果,绝不中断流水线(对应 Atlas 的 ratchet gate 3);但文件存在而格式漂移(有内容却解析不出任何行)会被标记为 degraded 状态并告警——projects.ts 的健康检查里记录了一次真实的列名重命名事故,曾让页面静默置零一周。这提醒你:改表头列名或破坏表格结构会影响下游解析

Routing Aliases:自然语言路由的核心机制

原文档强调:DA 在每次会话启动时读取该表,用于把别名路由到具体仓库。这就是 Routing Aliases 小节的作用:

When you say...The DA routes to...
"my site", "the blog"(interview — primary site)
"the workspace", "that project"(interview — main active project)

它的价值在于让 DA 从"上下文推断"升级为"查表路由":当你说 "check the blog deploy" 时,DA 直接命中别名行,得到对应的本地路径、URL 与部署命令,无需在庞杂的会话历史中猜测。这种"别名 → 规范条目"的映射是身份层文件常见的模式(CONTACTS.md 的人名路由同理),也是 PROJECTS.md 保持轻量、可增量维护的原因——原文档的结语很直白:"保持它是最新的——增量维护很便宜,事后重建很昂贵。"

初始化:从样板行到真实身份

原文档开头有两条醒目提示:

Bootstrap default — functional before interview. Run/interview(projects phase) to personalize. ⚠ INTERVIEW REQUIRED — run/interviewto populate this file with your real identity content. The DA loads it at every session start; without your content, the model operates on placeholders.

也就是说,新安装的 LifeOS 携带的是样例行(interview — first project)),此时 DA 加载到的只是占位符。个性化流程有两条路径:

  1. /interview的 projects 阶段:面试会逐个询问你的活跃项目,并把结果以行追加到表中,同时生成对应的路由别名;
  2. /LifeOS setup的 Phase 0:在 Phase0Setup.md 中,Step 0.5 明确要求"至少一个项目,让 PROJECTS 路由可用",验收标准是"PROJECTS.md 拥有 ≥1 行非样例行"。

Phase 0 的扫描器是 InterviewScan.ts,它把USER/PROJECTS.md列为 phase 0 / setup 类目标(leverage 权重 8),可用以下命令查看完成度:

bun ~/.claude/LIFEOS/TOOLS/InterviewScan.ts --json | jq '.targets[] | select(.phase == 0)'

若所有 phase 0 目标 ≥80% 完成,工作流自动跳过并转向 TelosCheckin。完成 Phase 0 后,流程会重新生成PRINCIPAL_TELOS.md并发送 Pulse/reload,让运行中的守护进程加载新身份。日常维护则交给 ContextCheckin.md 的例行检查——它会主动询问:"PROJECTS 距今 N 天。有没有新项目要加,或者已完成的项目要退役?路由别名还符合你的叫法吗?"并将 PROJECTS.md 与新鲜度预算(如 45 行预算)对比,超预算时提示裁剪或拆分。

谁在读这个文件:六个消费方

PROJECTS.md 不是一份给人看的档案,而是一个被多个子系统读取的结构化事实源

消费方文件读取方式
会话上下文CLAUDE.template.md、lifeos-context.ts@-import全文加载 / CONTEXT 注入块
Atlas 观测collectors/Projects.ts解析主表格,生成 SERVES / DEPLOYED_FROM 边
Pulse 仪表盘modules/projects.tsGET /api/projects实时解析(Live Apps / TELOS / Retired 三组)
Proposal 作用域ProposalScope.ts读取加粗项目名,判定记忆提案是全局还是项目级
部署门禁DeployRegistrationGate.hook.ts检查自定义域名是否已登记
记忆写入边界MutationTier.ts判定 PROJECTS.md 属于 Tier B

下面展开几个最值得关注的机制。

部署门禁:新上线站点必须"先登记、后部署"

DeployRegistrationGate.hook.ts 是 PROJECTS.md 最硬核的消费方。它的契约(OPERATIONAL_RULES § Bunker registration)规定:本次会话中任何带 CUSTOM DOMAIN 的wrangler deploy,必须在会话结束时同时登记到 PROJECTS.md 表格行和 ARBOL 的 infra-inventory.ts,否则 Stop 时该 hook 会返回decision: "block"阻止会话结束。

实现上有几个细节值得注意:

  • 域名识别用正则CUSTOM_DOMAIN_RE从 transcript 中提取 wrangler 的触发行(<domain> (custom domain)),并以行边界锚定避免误匹配正文中的提法;
  • 校验是字面子串检查:if (!projects.includes(d)) missing.push("PROJECTS.md row (+ routing alias)")——所以登记的域名必须与部署的域名逐字符一致(会去掉www.前缀);
  • 每个域名每会话只拦截一次(状态文件去重),任何异常都"fail open",绝不因为本 gate 让 Stop 中断;
  • ARBOL 定制不存在时自动跳过第二项检查。

这解释了原文档"保持它是最新的"为什么是一条被强制执行的纪律:在 LifeOS 的运营规则里,项目登记不是事后整理,而是部署动作的一部分

记忆系统:Tier B 追加 + 项目级作用域

在 MemorySystem.md 的记忆分类体系中,PROJECTS.md 被明确列为projects类型的提案目标:

target_kindTarget file触发条件
projectsUSER/PROJECTS.md主理人提到一个应进入项目路由表的新项目

写入边界由 MutationTier.ts 的四层分类器决定:LIFEOS/USER/PROJECTS.md属于Tier B(logged-append with audit)——记忆评审器可以追加,但每次写入都要在tier-b-writes.jsonl记录审计行;它不像 Tier C 的身份文件那样"只提案不可直改",也不像 Tier D 那样完全不可触碰。这对应了原文档"interview 一次一个项目、逐行追加到表格"的增量维护设计:追加是系统允许的默认形态。

值得注意的边界设计:在 MemoryTypes.ts 中,projects类型被故意排除在作用域门控之外——因为"PROJECTS.md 一行的全部职责就是命名一个项目",对它做作用域限制会把本应落在注册表的提案挡在门外。反观 ProposalScope.ts,它正是从"路由表格的加粗第一列"(/^\|\s*\*\*([^*|]{2,40})\*\*/)读取项目名,用来判定某条记忆提案是全局教义还是归属于某个具体项目("Vector's portal invites are never sent" → 归入 Vector 项目)。由于它是实时读文件而不是硬编码,新项目只要加入表格就立刻具备可作用域性——表格即注册表,无需第二处维护点。

Pulse 仪表盘与 Freshness 检查

Pulse 的 projects.ts 模块提供GET /api/projects,把项目按三个来源分组展示:

  • Live Apps(来自USER/PROJECTS.md主表格);
  • TELOS(来自USER/TELOS/TELOS.md## Projects散文小节,每行一个项目,无 path/URL/deploy 信息);
  • Retired(来自USER/PROJECTS_RETIRED.md)。

此外,FreshnessSystem.md 把LIFEOS/USER/PROJECTS.md列为受新鲜度约定(pai-freshness-v1)约束的"项目注册表 + 路由别名"文件,tab-freshness.ts 会持续跟踪它的最后修改时间,配合 ContextCheckin.md 的例行检查驱动周期性的"增删项目 / 校准别名"维护。工作系统 WorkSystem.md 也会读取它:登记在 PROJECTS.md 中、但 14 天无提交且无未关闭 issue 的项目,会被标记为[Project-Check]待办。

维护最佳实践

综合原文档与各消费方的解析约定,维护 PROJECTS.md 时有几条实操准则:

  1. 保持至少一行真实项目:Phase 0 的验收标准是"≥1 行非样例行",样例行会让 DA 基于占位符工作。
  2. 项目名用加粗包裹,状态标记用 emoji 或斜体后缀**Blog** 🎯同时服务 ProposalScope(加粗列)与 Pulse 徽章(🎯=system-of-record、🚨=sensitive、decommissioned/concept 等),并注意解析器会剥离这些标记,显示名是干净的。
  3. URL 列只放可路由的域名localhost.lan后缀、github.com会被 Atlas 的 SERVES 边过滤;需要关联 GitHub 仓库时直接在行内写github.com/owner/repo(触发 DEPLOYED_FROM 边)。
  4. 新项目先加行再加表项,退役项目移入PROJECTS_RETIRED.md(Pulse 有独立的 Retired 分组,删除线 URL 不会被渲染为链接)。
  5. 改列名要谨慎:Pulse 解析器只锁定"含 Project 与 Deploy 关键词"的表头,但列数少于 5 或破坏表格行格式会导致解析漂移并触发 degraded 告警。
  6. 别名与表格同步更新:路由别名是自然语言路由的入口,新增/重命名项目时务必同步,否则"check the blog deploy"这类指令会命中过期映射。
  7. 不要手动绕过门禁:上线自定义域名时,先按 DeployRegistrationGate.hook.ts 的要求完成 PROJECTS.md 行 + ARBOL infra-inventory 登记,再执行部署,否则 Stop 会被拦截并提示补齐注册。

小结

PROJECTS.md 以一张五行表格 + 一组别名,承担了 LifeOS 中"项目身份"的单一事实源职责:它被@-import进每次会话上下文支撑自然语言路由,被 Atlas 解析为领域与仓库的关联图,被 Pulse 渲染为 Live Apps 仪表盘,被 ProposalScope 用作项目级记忆提案的作用域字典,并被 DeployRegistrationGate 用作公网部署的强制注册清单。维护好它,本质上是维护 DA 对"你在做什么"的结构化认知——这正是原文档那句"增量维护便宜、事后重建昂贵"的底层原因。

【免费下载链接】LifeOS⛰️ The Life Operating System — an intent engineering platform that moves you from your current state to your ideal state, in life and work.项目地址: https://gitcode.com/GitHub_Trending/pe/LifeOS

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询