diff --git a/.claude-plugin/marketplace.json b/.claude-plugin/marketplace.json index 5bf760b..96bc708 100644 --- a/.claude-plugin/marketplace.json +++ b/.claude-plugin/marketplace.json @@ -9,7 +9,7 @@ { "name": "llmdoc", "source": "./", - "description": "llmdoc workflow for Claude Code: init, commit-watermark update detection, update modes, temporary investigation cache, and use" + "description": "llmdoc workflow for Claude Code: bounded cold-start context, routed docs, init, commit-watermark updates, and use" } ] } diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json index d121e2d..d32e877 100644 --- a/.claude-plugin/plugin.json +++ b/.claude-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "llmdoc", - "description": "llmdoc Claude Code plugin with init, commit-watermark update detection, update modes, temporary investigation cache, and use", - "version": "2.2.0", + "description": "llmdoc plugin with bounded cold-start context, routed docs, init, commit-watermark updates, and use", + "version": "2.3.0", "author": { "name": "DJJ & Danniel" } diff --git a/.codex-plugin/plugin.json b/.codex-plugin/plugin.json index 9a31ec6..4a6d5ab 100644 --- a/.codex-plugin/plugin.json +++ b/.codex-plugin/plugin.json @@ -1,7 +1,7 @@ { "name": "llmdoc", - "version": "2.2.0", - "description": "Doc-driven workflow with startup docs, commit-watermark update detection, update modes, temporary investigation cache, and reusable skills for Codex.", + "version": "2.3.0", + "description": "Doc-driven workflow with bounded cold-start context, compact re-entry, commit-watermark updates, and reusable skills for Codex.", "author": { "name": "DJJ & Danniel", "email": "shuaiqijianaho@qq.com", @@ -13,10 +13,15 @@ "interface": { "displayName": "llmdoc", "shortDescription": "Doc-driven coding workflow", - "longDescription": "Use startup docs, guides, commit-watermark update detection, update modes, temporary investigation cache, and focused reflections to improve coding quality and reduce repeated work.", + "longDescription": "Load a bounded startup package once, resume compacted tasks without replaying llmdoc, and use routed docs, commit-watermark updates, temporary investigation cache, and focused reflections.", "developerName": "TokenRoll", "category": "Productivity", "capabilities": ["Read", "Write"], - "websiteURL": "https://github.com/TokenRollAI/llmdoc" + "websiteURL": "https://github.com/TokenRollAI/llmdoc", + "defaultPrompt": [ + "Load llmdoc and help me implement this task.", + "Initialize llmdoc for this repository.", + "Update llmdoc after my recent changes." + ] } } diff --git a/.codex/agents/llmdoc-recorder.toml b/.codex/agents/llmdoc-recorder.toml index 9f6012a..499e6e0 100644 --- a/.codex/agents/llmdoc-recorder.toml +++ b/.codex/agents/llmdoc-recorder.toml @@ -11,6 +11,9 @@ Load the llmdoc skill first. Your responsibilities: - Maintain stable llmdoc documents. - Keep llmdoc/index.md and llmdoc/startup.md distinct. +- Keep index.md + startup.md + must/ under 24 KiB by default. +- In monoliths, keep the root index as an L0 router and route leaf docs through subsystem indexes. +- Treat the startup pack as cold-start-only; context compaction resumes from LLMDOC_STATE. - Keep temporary investigation artifacts out of stable docs. - Read relevant guides and reflections before updating stable docs. - Split documents aggressively rather than growing one large file. diff --git a/AGENTS.example.md b/AGENTS.example.md index a159706..576b6c6 100644 --- a/AGENTS.example.md +++ b/AGENTS.example.md @@ -2,6 +2,8 @@ Before broad source-code exploration, planning, or documentation work, load the `llmdoc` skill. +Load it once on cold start. After context compaction, continue from `LLMDOC_STATE` and do not replay the skill or startup pack unless the state is stale or insufficient. + The main assistant should align with the user before non-trivial plans or edits. Use available `llmdoc` subagents when they fit the task. Prefer `investigator` for context exploration, current-state research, unfamiliar subsystems, and reusable scratch reports; use `recorder` for stable doc updates, `worker` for scoped implementation, and `reflector` for process lessons. diff --git a/CLAUDE.example.md b/CLAUDE.example.md index 2b3c489..54a1e92 100644 --- a/CLAUDE.example.md +++ b/CLAUDE.example.md @@ -3,6 +3,8 @@ Always answer in 简体中文 Load the `llmdoc` skill before broad code exploration, planning, document updates, or non-trivial code edits. +Load it once on cold start. After context compaction, continue from `LLMDOC_STATE` and do not replay the skill or startup pack unless the state is stale or insufficient. + The main assistant should align with the user before non-trivial plans or edits. Use available `llmdoc` subagents when they fit the task. Prefer `investigator` for context exploration, current-state research, unfamiliar subsystems, and reusable scratch reports; use `recorder` for stable doc updates, `worker` for scoped implementation, and `reflector` for process lessons. diff --git a/README.md b/README.md index e797fde..f65150c 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,8 @@ The default setup is simple: - `CLAUDE.md` and `AGENTS.md` only need one short rule: step one is loading the `llmdoc` skill - the core skill entry is short, while detailed rationale, protocols, and templates are split under `skills/llmdoc/references/` - the core skill defines proactive guide/reflection reading and proactive user discussion before non-trivial edits +- the startup package is loaded once on cold start; Codex compaction resumes through a compact `LLMDOC_STATE` instead of replaying llmdoc +- monoliths keep a bounded root router and use subsystem indexes instead of loading a repository-wide leaf catalog - the workflow restores the good pattern of proactively asking whether to run `/llmdoc:update` at the end of non-trivial tasks - `/llmdoc:update` supports lightweight and heavier modes, so immediate post-task doc updates do not always require a full multi-agent pipeline - helper Codex skills provide command-like entrypoints without pretending Codex has custom slash commands for this plugin @@ -36,7 +38,7 @@ This refactor keeps the public interface small and moves the rest into one reusa - Claude Code plugin support: `.claude-plugin/` - Codex CLI plugin support: `.codex-plugin/plugin.json` and `.agents/plugins/marketplace.json` - Codex CLI subagents: `.codex/agents/*.toml` -- Codex CLI hooks: `SessionStart`, `Stop` templates included +- Codex CLI hooks: lifecycle-aware `SessionStart` bundled; `Stop` and compact-prompt templates included ## Workflow @@ -92,13 +94,25 @@ The command: In normal use, the main assistant should proactively ask whether to run `/llmdoc:update` when the task produced durable knowledge or a useful reflection. +## Context Lifecycle + +`llmdoc` distinguishes a cold start from compact re-entry: + +- `startup` and `clear` load the core skill, root index, startup list, and MUST pack once +- `resume` reuses a valid `LLMDOC_STATE`, otherwise it performs one cold start +- `compact` continues the same task without reloading the skill, startup pack, lessons, or already-loaded task docs + +The bundled Codex `SessionStart` hook fingerprints the startup pack and reports its byte size. Re-entry refreshes only the smallest relevant document set when the fingerprint changed, a relevant document changed, the task entered a new subsystem, or the compact state is insufficient. + +The default startup budget is 24 KiB for `index.md` + `startup.md` + `must/`. This is a deterministic proxy, not an exact model-token count. In a monolith, keep `llmdoc/index.md` as an L0 router, add subsystem indexes beside their docs, and load only the active subsystem route. + ## llmdoc Layout ```text llmdoc/ ├── index.md ├── startup.md -├── must/ # Small startup context package +├── must/ # Small cold-start context package ├── overview/ # Project and feature identity ├── architecture/ # Retrieval maps, invariants, ownership ├── guides/ # One workflow per document @@ -112,8 +126,8 @@ llmdoc/ └── investigations/ # Temporary scratch investigation reports ``` -`llmdoc/index.md` is the global doc map. -`llmdoc/startup.md` is only the startup reading order. +`llmdoc/index.md` is the bounded global router. +`llmdoc/startup.md` is only the cold-start reading order. They should link to each other, but they should not repeat the same content. `.llmdoc-tmp/` is a local temporary context cache. Investigator reports can persist across nearby sessions and help avoid repeated research, but they are ignored by git, not indexed, and not a source of truth. Promote only durable conclusions into tracked `llmdoc/` docs. @@ -178,11 +192,13 @@ This repository contains two separate Codex integration surfaces: - [`skills/llmdoc/`](skills/llmdoc/) - [`skills/llmdoc-init/`](skills/llmdoc-init/) - [`skills/llmdoc-update/`](skills/llmdoc-update/) + - [`hooks/hooks.json`](hooks/hooks.json) for the bundled lifecycle-aware `SessionStart` hook - [`.agents/plugins/marketplace.json`](.agents/plugins/marketplace.json) as a repo-scoped local marketplace example - Repo-local Codex workflow files for this repository: - [`.codex/config.toml`](.codex/config.toml) - [`.codex/agents/`](.codex/agents) - [`skills/llmdoc/templates/codex-hooks.json`](skills/llmdoc/templates/codex-hooks.json) + - [`skills/llmdoc/templates/compact-prompt.md`](skills/llmdoc/templates/compact-prompt.md) #### Option 1: Install from GitHub (recommended) @@ -198,7 +214,8 @@ Then: 2. Run `/plugins` in Codex. 3. Find `llmdoc` in the plugin list, select it to open the detail page. 4. Install the plugin. -5. Start a new thread in any repository and either: +5. Review and trust the bundled lifecycle hook in `/hooks`. +6. Start a new thread in any repository and either: - ask Codex to load the `llmdoc` skill first for normal work - choose `llmdoc-init` when you want the `/llmdoc:init` workflow - choose `llmdoc-update` when you want the `/llmdoc:update` workflow @@ -219,7 +236,9 @@ Use this when you are working inside this repository — contributing to `llmdoc - choose `llmdoc-init` when you want the `/llmdoc:init` workflow - choose `llmdoc-update` when you want the `/llmdoc:update` workflow - or type `@` and choose the plugin or one of its bundled skills explicitly -8. If you want hooks, copy [`skills/llmdoc/templates/codex-hooks.json`](skills/llmdoc/templates/codex-hooks.json) to `.codex/hooks.json` and adjust the script paths for your machine. +8. Review and trust the bundled hook in `/hooks`. If you prefer repo-local hooks instead of the installed plugin hook, copy [`skills/llmdoc/templates/codex-hooks.json`](skills/llmdoc/templates/codex-hooks.json) to `.codex/hooks.json` and adjust the script paths for your machine; do not enable both copies. + +The compact-prompt template is optional because configuring `compact_prompt` or `experimental_compact_prompt_file` overrides Codex's built-in compaction prompt. Use it only when you want a stronger `LLMDOC_STATE` shape and are prepared to review it as Codex evolves. When you open this repository itself, Codex can also use the project-scoped agents under [`.codex/agents/`](.codex/agents) and the agent limits from [`.codex/config.toml`](.codex/config.toml). @@ -229,6 +248,7 @@ The reusable skill lives at [`skills/llmdoc/SKILL.md`](skills/llmdoc/SKILL.md). The Codex helper entry skills live at [`skills/llmdoc-init/SKILL.md`](skills/llmdoc-init/SKILL.md) and [`skills/llmdoc-update/SKILL.md`](skills/llmdoc-update/SKILL.md). Detailed references live under [`skills/llmdoc/references/`](skills/llmdoc/references/). Codex hook templates live under [`skills/llmdoc/templates/`](skills/llmdoc/templates/). +The plugin-bundled Codex hook lives at [`hooks/hooks.json`](hooks/hooks.json). ## Codex Subagents diff --git a/README.zh-CN.md b/README.zh-CN.md index 142b3f5..03c7044 100644 --- a/README.zh-CN.md +++ b/README.zh-CN.md @@ -13,6 +13,8 @@ - `CLAUDE.md` 和 `AGENTS.md` 里只保留一条短规则:step one 是加载 `llmdoc` skill - core skill 入口保持简短,详细的方法论、协议和模板拆到 `skills/llmdoc/references/` - core skill 还定义了主动阅读 guides/reflection,以及在非简单改动前主动和用户沟通 +- startup package 只在冷启动读取一次;Codex compact 后通过精简的 `LLMDOC_STATE` 恢复,而不是重放 llmdoc +- 巨石仓库使用有固定预算的根路由和子系统索引,不加载覆盖全仓库的叶子文档清单 - 整套工作流还恢复了一个好模式:在非简单任务结束时,主动询问是否运行 `/llmdoc:update` - `/llmdoc:update` 支持轻量和重型模式,所以刚完成实现后的文档更新不必每次都跑完整多 agent 流水线 - Codex helper skills 提供了接近 command 的入口,但不会误导用户以为 Codex 已经支持这个插件的自定义 slash command @@ -36,7 +38,7 @@ - Claude Code plugin 支持:`.claude-plugin/` - Codex CLI plugin 支持:已提供 `.codex-plugin/plugin.json` 和 `.agents/plugins/marketplace.json` - Codex CLI subagents 支持:已提供 `.codex/agents/*.toml` -- Codex CLI hooks:已提供 `SessionStart`、`Stop` 模板 +- Codex CLI hooks:插件内置生命周期感知的 `SessionStart`;另提供 `Stop` 与 compact-prompt 模板 ## 工作流 @@ -92,13 +94,25 @@ 在日常使用里,如果任务产生了值得长期保留的知识或反思,主 assistant 应该主动询问是否现在运行 `/llmdoc:update`。 +## Context 生命周期 + +`llmdoc` 区分冷启动和 compact 热恢复: + +- `startup`、`clear`:只加载一次 core skill、根 index、startup 列表和 MUST pack +- `resume`:优先复用有效的 `LLMDOC_STATE`,没有有效状态时才执行一次冷启动 +- `compact`:继续同一任务,不重新加载 skill、startup pack、lessons 或已经加载的任务文档 + +插件内置的 Codex `SessionStart` hook 会计算 startup pack 指纹和字节数。只有指纹变化、相关文档被修改、任务进入新子系统或 compact 状态不足时,才读取最小的相关文档集合。 + +`index.md` + `startup.md` + `must/` 的默认预算是 24 KiB;这是确定性的近似约束,不是精确 token 数。巨石仓库应让 `llmdoc/index.md` 只做 L0 路由,在相关文档旁建立子系统索引,并且只加载当前子系统的路由。 + ## llmdoc 结构 ```text llmdoc/ ├── index.md ├── startup.md -├── must/ # 每次运行都应读取的小型启动上下文 +├── must/ # 冷启动时读取一次的小型上下文 ├── overview/ # 项目和特性的身份与边界 ├── architecture/ # 检索地图、不变量、所有权边界 ├── guides/ # 一篇文档只讲一个工作流 @@ -112,8 +126,8 @@ llmdoc/ └── investigations/ # 临时调查草稿 ``` -`llmdoc/index.md` 是全局文档地图。 -`llmdoc/startup.md` 只负责启动阅读顺序。 +`llmdoc/index.md` 是有固定预算的全局路由。 +`llmdoc/startup.md` 只负责冷启动阅读顺序。 两者可以互相链接,但不应该重复同一批内容。 `.llmdoc-tmp/` 是本地临时 context cache。investigator 报告可以跨相邻会话保留,帮助减少重复调研,但它被 git 忽略、不会进入 index,也不是 source of truth。只有稳定、可复用的结论才应该提升到 tracked `llmdoc/` 文档里。 @@ -178,11 +192,13 @@ codex - [`skills/llmdoc/`](skills/llmdoc/) - [`skills/llmdoc-init/`](skills/llmdoc-init/) - [`skills/llmdoc-update/`](skills/llmdoc-update/) + - [`hooks/hooks.json`](hooks/hooks.json),插件内置的生命周期感知 `SessionStart` hook - [`.agents/plugins/marketplace.json`](.agents/plugins/marketplace.json),作为 repo 级本地 marketplace 示例 - 这个仓库自己的 repo-local Codex 工作流文件: - [`.codex/config.toml`](.codex/config.toml) - [`.codex/agents/`](.codex/agents) - [`skills/llmdoc/templates/codex-hooks.json`](skills/llmdoc/templates/codex-hooks.json) + - [`skills/llmdoc/templates/compact-prompt.md`](skills/llmdoc/templates/compact-prompt.md) #### 方式一:从 GitHub 安装(推荐) @@ -198,7 +214,8 @@ codex plugin marketplace add TokenRollAI/llmdoc 2. 在 Codex 中执行 `/plugins` 3. 在插件列表中找到 `llmdoc`,选中进入详情页 4. 安装插件 -5. 在任意仓库里新开一个对话,然后按你的目标选择入口: +5. 在 `/hooks` 中检查并信任插件内置的生命周期 hook +6. 在任意仓库里新开一个对话,然后按你的目标选择入口: - 正常工作时,让 Codex 先加载 `llmdoc` skill - 要执行 `/llmdoc:init` 等价流程时,选择 `llmdoc-init` - 要执行 `/llmdoc:update` 等价流程时,选择 `llmdoc-update` @@ -219,7 +236,9 @@ codex plugin marketplace add TokenRollAI/llmdoc - 要执行 `/llmdoc:init` 等价流程时,选择 `llmdoc-init` - 要执行 `/llmdoc:update` 等价流程时,选择 `llmdoc-update` - 或者输入 `@`,再显式选择这个插件或它打包进来的 skill -8. 如果你需要 hooks,把 [`skills/llmdoc/templates/codex-hooks.json`](skills/llmdoc/templates/codex-hooks.json) 复制到 `.codex/hooks.json`,再按你的机器路径调整脚本路径 +8. 在 `/hooks` 中检查并信任插件内置 hook。如果你更希望使用 repo-local hook,可以把 [`skills/llmdoc/templates/codex-hooks.json`](skills/llmdoc/templates/codex-hooks.json) 复制到 `.codex/hooks.json`,再按机器路径调整脚本;不要同时启用两份 + +compact-prompt 模板是可选增强,因为配置 `compact_prompt` 或 `experimental_compact_prompt_file` 会覆盖 Codex 内置 compaction prompt。只有在需要更强的 `LLMDOC_STATE` 结构保证,并愿意随 Codex 演进持续检查模板时才启用。 当你打开的就是这个仓库时,Codex 还会同时使用 [`.codex/agents/`](.codex/agents) 里的 project-scoped agents,以及 [`.codex/config.toml`](.codex/config.toml) 里的 agent 限制配置。 @@ -229,6 +248,7 @@ codex plugin marketplace add TokenRollAI/llmdoc Codex helper 入口 skills 位于 [`skills/llmdoc-init/SKILL.md`](skills/llmdoc-init/SKILL.md) 和 [`skills/llmdoc-update/SKILL.md`](skills/llmdoc-update/SKILL.md)。 详细参考文档位于 [`skills/llmdoc/references/`](skills/llmdoc/references/)。 Codex CLI hooks 模板位于 [`skills/llmdoc/templates/`](skills/llmdoc/templates/)。 +插件内置的 Codex hook 位于 [`hooks/hooks.json`](hooks/hooks.json)。 ## Codex Subagents diff --git a/agents/recorder.md b/agents/recorder.md index 01ab723..6b32996 100644 --- a/agents/recorder.md +++ b/agents/recorder.md @@ -41,7 +41,7 @@ Commit watermark ownership (`llmdoc/state/sync.md`): llmdoc categories: -- `/must/`: Tiny startup documents that should be read on every run. Only recurring, cross-task, stable knowledge belongs here. +- `/must/`: Tiny startup documents read once on cold start. Only recurring, cross-task, stable knowledge belongs here. - `/overview/`: Identity, boundaries, and role of the project or a large feature. - `/architecture/`: Retrieval maps, ownership boundaries, flows, and invariants. - `/guides/`: One workflow per document. @@ -60,6 +60,8 @@ Index rules: - `llmdoc/index.md` is the global map of the documentation system. - `llmdoc/startup.md` is only the startup reading order for must-read docs. +- Keep `index.md` + `startup.md` + `must/` under 24 KiB by default. +- In a monolith, keep the root index as an L0 router and point it at subsystem indexes instead of listing every leaf document. - Do not duplicate the global category catalog inside `startup.md`. - Do not duplicate the detailed startup reading order inside `llmdoc/index.md`. @@ -72,6 +74,7 @@ Split rules: - If a document grows large only because it is preserving one coherent execution model, invariant set, or contract cluster, keep it intact until a clean split is obvious. - If a document exceeds roughly 120 lines, covers more than one workflow, or mixes stable facts with transient notes, split it when doing so improves retrieval without discarding essential reasoning flow. - Do not promote content into `/must/` unless it is stable, short, and useful on nearly every task. +- After context compaction, preserve and use `LLMDOC_STATE`; do not replay the startup pack merely because compaction occurred. Reference policy: diff --git a/commands/init.md b/commands/init.md index 98a038b..1e352ae 100644 --- a/commands/init.md +++ b/commands/init.md @@ -55,6 +55,8 @@ Why: - Create `llmdoc/index.md` as the global documentation map. - Create `llmdoc/startup.md`. - Create a small set of MUST docs for recurring startup context. + - Treat the startup pack as a cold-start-only package; context compaction must resume from preserved task state instead of replaying it. + - Keep `index.md` + `startup.md` + `must/` under 24 KiB by default. For a monolith, make the root index an L0 router and add subsystem indexes rather than listing every leaf document. - Ensure `llmdoc/index.md` does not duplicate the ordered startup list in `llmdoc/startup.md`. - Ensure `llmdoc/startup.md` does not duplicate the global category catalog from `llmdoc/index.md`. - Create `llmdoc/overview/project-overview.md`. @@ -63,7 +65,7 @@ Why: - Treat document length as a quality tradeoff, not a hard limit. If a core doc needs more space to preserve causal flow, invariants, and terminology, keep it cohesive before splitting. 5. Synchronize `llmdoc/index.md`. - - Index all stable docs. + - In a small repository, index stable docs directly. In a monolith, index subsystem routers and let those indexes route to leaf documents. - Keep `memory/reflections/` and `memory/decisions/` separate from stable docs. - Do not treat `.llmdoc-tmp/` as part of llmdoc. diff --git a/commands/update.md b/commands/update.md index 6782d7c..7d93ef5 100644 --- a/commands/update.md +++ b/commands/update.md @@ -133,6 +133,7 @@ Self-authored test: a commit is self-authored when its author email (`git log -1 - Remove or correct stable-doc claims that no longer match the current code. - Split documents aggressively instead of appending to a large file. - Reconcile `llmdoc/memory/doc-gaps.md`: close resolved gaps, mark stale gaps, add only actionable new gaps with closure criteria. + - Keep the cold-start pack (`index.md` + `startup.md` + `must/`) under 24 KiB by default. In a monolith, preserve a small L0 root router and route leaf docs through subsystem indexes. 7. Run the active-memory archive check. - After any new reflection is written, count active memory files under `llmdoc/memory/`, excluding `lessons-learned.md`, `doc-gaps.md`, and anything under `archive/` (`llmdoc/state/` is outside `memory/` and is not counted). @@ -140,6 +141,7 @@ Self-authored test: a commit is self-authored when its author email (`git log -1 8. Synchronize `llmdoc/index.md`. - Ensure new and changed docs are discoverable. + - Do not grow the root index into a monolith-wide leaf inventory; point it at subsystem indexes when direct enumeration would violate the startup budget. - Do not index `.llmdoc-tmp/`, and do not index `llmdoc/state/sync.md` as knowledge. 9. Advance the watermark (recorder-owned terminal step). diff --git a/hooks/hooks.json b/hooks/hooks.json new file mode 100644 index 0000000..8b96208 --- /dev/null +++ b/hooks/hooks.json @@ -0,0 +1,37 @@ +{ + "description": "Lifecycle-aware llmdoc startup and compact re-entry.", + "hooks": { + "SessionStart": [ + { + "matcher": "^(startup|clear)$", + "hooks": [ + { + "type": "command", + "command": "\"$PLUGIN_ROOT\"/skills/llmdoc/templates/session-start.sh cold", + "additionalContextLimit": 1200 + } + ] + }, + { + "matcher": "^resume$", + "hooks": [ + { + "type": "command", + "command": "\"$PLUGIN_ROOT\"/skills/llmdoc/templates/session-start.sh resume", + "additionalContextLimit": 1200 + } + ] + }, + { + "matcher": "^compact$", + "hooks": [ + { + "type": "command", + "command": "\"$PLUGIN_ROOT\"/skills/llmdoc/templates/session-start.sh compact", + "additionalContextLimit": 1200 + } + ] + } + ] + } +} diff --git a/llmdoc/architecture/context-lifecycle.md b/llmdoc/architecture/context-lifecycle.md new file mode 100644 index 0000000..4715feb --- /dev/null +++ b/llmdoc/architecture/context-lifecycle.md @@ -0,0 +1,50 @@ +# Context Lifecycle + +## Purpose +- Define the stable lifecycle contract for entering, resuming, and compactly re-entering llmdoc work. +- Keep startup context bounded as the repository grows, without losing task continuity after compaction. + +## Lifecycle Modes +- **Cold start** (`startup` or `clear`): load the llmdoc skill, then `llmdoc/index.md`, `llmdoc/startup.md`, and the MUST docs listed there exactly once for the session. +- **Resume**: continue from a usable `LLMDOC_STATE` when its startup-pack fingerprint still matches; otherwise perform one cold-start read. +- **Compact re-entry** (`compact`): continue the same task from the compact summary. A compact event alone never authorizes replaying the skill, startup pack, lessons, or already-loaded task docs. + +## Startup-Pack Contract +- The startup pack is `llmdoc/index.md` + `llmdoc/startup.md` + files under `llmdoc/must/`. +- `skills/llmdoc/templates/session-start.sh` fingerprints that pack from a sorted manifest of file digest, byte count, and relative path, then reports the combined UTF-8 byte size. +- The default startup budget is `24576` bytes (`24 KiB`) through `LLMDOC_STARTUP_MAX_BYTES`. +- Exceeding the budget is a maintenance signal, not permission to skip required startup context. Shrink MUST docs or add layered routing instead. + +## Routing Model +- Keep the root `llmdoc/index.md` as an L0 router for the whole documentation system. +- In a monolith, add L1 subsystem indexes beside the documents they route, then load only the active subsystem index and the leaf docs needed for the task. +- Do not turn `startup.md` into a catalog of subsystem docs. Cold start should stay small enough to leave room for task work. + +## Compact State Contract +- `LLMDOC_STATE` preserves only task-critical continuity: + - startup-pack fingerprint + - active goal + - loaded doc paths and why they matter + - invariants, decisions, changed files, validation status + - exact next action + - unresolved risks or blockers +- Store distilled facts and paths, not full llmdoc document bodies. + +## Refresh Rules +- Re-read only the smallest relevant document set when: + - the preserved startup fingerprint differs from the current one + - a relevant document changed + - the task enters a new subsystem + - `LLMDOC_STATE` lacks a fact needed for the next action + - evidence conflicts, validation fails, or stronger confidence is needed before editing +- If targeted refresh cannot recover the needed invariants, fall back to the cold-start pack once. + +## Runtime Surfaces +- `hooks/hooks.json` is the bundled Codex hook surface. It ships only `SessionStart`, with separate matchers for `startup|clear`, `resume`, and `compact`. +- `skills/llmdoc/templates/session-start.sh` is the runtime payload generator for those lifecycle branches. +- `skills/llmdoc/templates/compact-prompt.md` is an optional override for preserving a stronger `LLMDOC_STATE` shape. +- `skills/llmdoc/templates/stop.sh` remains opt-in. `Stop` can remind or log, but it does not replace task-level llmdoc judgment. + +## Verification Hand-off +- When lifecycle or hook behavior changes, update the aligned public surfaces and run `skills/llmdoc/scripts/verify-lifecycle-hooks.sh `. +- Use `llmdoc/guides/updating-lifecycle-hooks.md` for the maintenance workflow. diff --git a/llmdoc/guides/updating-lifecycle-hooks.md b/llmdoc/guides/updating-lifecycle-hooks.md new file mode 100644 index 0000000..b27086d --- /dev/null +++ b/llmdoc/guides/updating-lifecycle-hooks.md @@ -0,0 +1,39 @@ +# How to Update Lifecycle Hooks + +## Preconditions +- Read `llmdoc/architecture/context-lifecycle.md`. +- Inspect the current worktree versions of `hooks/hooks.json`, `skills/llmdoc/templates/session-start.sh`, and any public docs that describe lifecycle behavior. + +## Surfaces To Keep Aligned +- `hooks/hooks.json`: bundled Codex `SessionStart` hook shipped with the plugin. +- `skills/llmdoc/templates/codex-hooks.json`: installable template copy of the hook matchers. +- `skills/llmdoc/templates/session-start.sh`: lifecycle-specific startup-pack fingerprint and byte-budget emitter. +- `skills/llmdoc/templates/compact-prompt.md`: optional `LLMDOC_STATE` schema guidance. +- `skills/llmdoc/templates/stop.sh`: opt-in `Stop` behavior when reminder/logging semantics change. +- `skills/llmdoc/references/operating-protocol.md`, `doc-structure.md`, and `codex-cli-hooks.md`: reusable skill guidance. +- `commands/init.md`, `commands/update.md`, `README.md`, and `README.zh-CN.md`: public workflow contract. +- `.codex-plugin/plugin.json`, `.claude-plugin/plugin.json`, `.claude-plugin/marketplace.json`, `AGENTS.example.md`, `CLAUDE.example.md`, `agents/recorder.md`, and `.codex/agents/llmdoc-recorder.toml`: prompts and metadata that currently mirror recorder-facing lifecycle wording. + +## Lifecycle Invariants +- `startup|clear` must describe one cold-start read of the llmdoc skill, `index.md`, `startup.md`, and MUST docs. +- `resume` must prefer a valid `LLMDOC_STATE` and fall back to a single cold start only when that state is absent, stale, or insufficient. +- `compact` must never instruct the model to reload the llmdoc skill or startup pack merely because compaction occurred. +- The startup pack remains `index.md` + `startup.md` + `must/`, fingerprinted from file digest, byte count, and relative path. +- The default startup-pack budget remains `24 KiB` unless the project intentionally changes `LLMDOC_STARTUP_MAX_BYTES`. +- The root index stays an L0 router; if the startup pack grows, add subsystem indexes instead of broadening cold-start reads. +- `LLMDOC_STATE` stores distilled task state and paths, not full document bodies. + +## Verification +1. Run `skills/llmdoc/scripts/verify-lifecycle-hooks.sh `. +2. Confirm it checks all three matchers in both `hooks/hooks.json` and `templates/codex-hooks.json`. +3. Confirm compact output includes `LLMDOC_COMPACT_REENTRY` and rejects cold-start instructions. +4. Confirm cold and compact outputs emit the same startup-pack fingerprint for the same tree. +5. Re-check `wc -c llmdoc/index.md llmdoc/startup.md llmdoc/must/*.md` when startup-pack wording changes. +6. If you touched the compact-prompt path, verify docs still describe it as opt-in and do not silently require `.codex/config.toml` changes. + +## Common Failure Points +- Updating only the template hook or only the bundled hook. +- Treating `compact` like `resume`, or `resume` like unconditional cold start. +- Letting compact summaries copy full llmdoc documents instead of a compact `LLMDOC_STATE`. +- Growing the root index into a leaf catalog and then compensating by reloading more docs on every re-entry. +- Changing lifecycle semantics in templates or scripts without mirroring the public docs and agent prompts. diff --git a/llmdoc/index.md b/llmdoc/index.md index 72b130b..b6bfe5a 100644 --- a/llmdoc/index.md +++ b/llmdoc/index.md @@ -5,7 +5,7 @@ - Startup reading order lives in `llmdoc/startup.md`. ## Categories -- `must/`: short recurring startup context for almost every task +- `must/`: short recurring cold-start context and routing rules for almost every task - `overview/`: project identity, boundaries, and major areas - `architecture/`: workflow orchestration, ownership boundaries, and invariants - `guides/`: workflow-specific instructions for recurring maintenance tasks @@ -16,14 +16,17 @@ - `llmdoc/startup.md`: ordered startup reading list - `llmdoc/overview/project-overview.md`: what this repository is and what belongs here - `llmdoc/architecture/init-investigation-orchestration.md`: how `/llmdoc:init` investigation is expected to fan out and converge +- `llmdoc/architecture/context-lifecycle.md`: cold-start, resume, compact-reentry, startup-pack budget, and L0/L1 routing invariants - `llmdoc/architecture/update-orchestration.md`: how `/llmdoc:update` uses the commit watermark to detect changes and chooses fast, analysis, or full update paths - `llmdoc/guides/updating-init-investigation-depth.md`: how to change init depth safely when the workflow is too shallow or too broad +- `llmdoc/guides/updating-lifecycle-hooks.md`: how to update lifecycle-aware `SessionStart`, optional compact-prompt/`Stop`, and their verification path - `llmdoc/guides/updating-update-workflow.md`: how to update the update workflow across Claude Code, Codex, agents, and docs - `llmdoc/reference/repo-surfaces.md`: stable map of commands, agents, plugin files, and Codex config surfaces ## Routing Rules -- Read `startup.md` first on normal work. +- Read `startup.md` first on cold start. - Read `architecture/init-investigation-orchestration.md` before changing `/llmdoc:init`, agent fan-out strategy, or Codex agent limits. +- Read `architecture/context-lifecycle.md` and `guides/updating-lifecycle-hooks.md` before changing compact re-entry behavior, startup-pack sizing, `LLMDOC_STATE`, bundled hooks, or lifecycle verification. - Read `guides/updating-init-investigation-depth.md` before tuning investigation breadth or follow-up passes. - Read `architecture/update-orchestration.md` and `guides/updating-update-workflow.md` before changing `/llmdoc:update`, `llmdoc-update`, the commit-watermark change detection, investigator scratch behavior, or recorder update rules. - Read `reference/repo-surfaces.md` before moving or renaming public repo surfaces such as commands, agents, plugin files, or `.codex/config.toml`. diff --git a/llmdoc/memory/reflections/2026-07-28-compact-reentry-context.md b/llmdoc/memory/reflections/2026-07-28-compact-reentry-context.md new file mode 100644 index 0000000..217fac4 --- /dev/null +++ b/llmdoc/memory/reflections/2026-07-28-compact-reentry-context.md @@ -0,0 +1,29 @@ +# Compact Re-Entry Context Reflection + +## Task +- Fix the llmdoc SessionStart path so cold start, resume, and compact re-entry behave differently instead of replaying the startup pack on every compaction. + +## Expected vs Actual +- Expected: compact re-entry should continue from preserved task state, only re-reading docs when state is stale or evidence changes. +- Actual: the SessionStart path treated compact like a fresh startup, which forced the skill, `llmdoc/index.md`, `llmdoc/startup.md`, and MUST docs to be reloaded after every compaction. + +## Root Cause +- The hook layer had collapsed lifecycle handling into one broad startup path, so compact, resume, and cold start were not routed separately. +- The compact summary did not have a stable `LLMDOC_STATE` contract with enough structure to distinguish "continue" from "rebuild." +- Verification was too weak on the warm path: it confirmed startup content existed, but not that compact re-entry skipped it. + +## Missing Signals +- No explicit assertion that compact output must not replay the startup pack. +- No checked fingerprint or byte-budget signal for the startup pack, so repeated reloads were not easy to notice. +- No lifecycle-specific test or script covered `startup|clear`, `resume`, and `compact` as separate cases. +- No compact-state schema was documented well enough to show which facts must survive compaction. + +## Promotion Candidates +- A stable lifecycle protocol that names cold start, resume, and compact re-entry separately. +- A compact-state shape that stores fingerprint, loaded paths, next action, and unresolved risks without copying full doc bodies. +- A small verifier script that checks matcher separation and rejects cold-start instructions in compact output. +- A bounded startup-pack rule for `index.md` + `startup.md` + `must/` so the root index stays a router, not a leaf catalog. + +## Follow-up +- Keep the lifecycle-specific hook split and the startup fingerprint/24 KiB budget as the default signal for future regressions. +- When compact behavior changes again, verify both the resume fallback and the "no replay on compact" path before updating higher-level docs. diff --git a/llmdoc/must/doc-routing.md b/llmdoc/must/doc-routing.md index 71bb92e..884a189 100644 --- a/llmdoc/must/doc-routing.md +++ b/llmdoc/must/doc-routing.md @@ -3,6 +3,7 @@ ## Read Next By Task - For `/llmdoc:init` changes: read `llmdoc/architecture/init-investigation-orchestration.md` and `llmdoc/guides/updating-init-investigation-depth.md`. - For `/llmdoc:update` changes: read `llmdoc/architecture/update-orchestration.md` and `llmdoc/guides/updating-update-workflow.md`. +- For lifecycle, compaction, or Codex hook changes: read `llmdoc/architecture/context-lifecycle.md` and `llmdoc/guides/updating-lifecycle-hooks.md`. - For public interface or repo layout changes: read `llmdoc/overview/project-overview.md` and `llmdoc/reference/repo-surfaces.md`. - For repeated workflow mistakes or regressions: read the relevant files under `llmdoc/memory/reflections/` first. diff --git a/llmdoc/must/working-agreement.md b/llmdoc/must/working-agreement.md index 3cad856..2103839 100644 --- a/llmdoc/must/working-agreement.md +++ b/llmdoc/must/working-agreement.md @@ -4,6 +4,7 @@ - Load the `llmdoc` skill before broad exploration, planning, or documentation work. - Prefer docs first, code and config second. - The main assistant aligns with the user before non-trivial edits. +- Treat `llmdoc/index.md` + `startup.md` + `must/` as a cold-start-only startup pack; after compaction continue from `LLMDOC_STATE` instead of replaying it. - Temporary investigation artifacts belong in `.llmdoc-tmp/`, not stable llmdoc docs. - `.llmdoc-tmp/` is only a local temporary context cache; validate scratch reports before reuse and never treat them as source of truth. - Use `/llmdoc:update` when a task changes workflow knowledge, architecture understanding, or recurring conventions. diff --git a/llmdoc/reference/repo-surfaces.md b/llmdoc/reference/repo-surfaces.md index 5a37300..9cca17a 100644 --- a/llmdoc/reference/repo-surfaces.md +++ b/llmdoc/reference/repo-surfaces.md @@ -8,6 +8,10 @@ - `commands/update.md`: Contract for selecting an update mode and keeping tracked stable docs current. - `skills/llmdoc/SKILL.md`: Core operating skill for llmdoc projects. - `skills/llmdoc-init/SKILL.md` and `skills/llmdoc-update/SKILL.md`: Codex-native helper entry skills that mirror `/llmdoc:init` and `/llmdoc:update`. +- `hooks/hooks.json`: Bundled plugin hook surface for lifecycle-aware Codex `SessionStart`. +- `skills/llmdoc/templates/session-start.sh`: Emits cold-start, resume, and compact-reentry startup guidance plus startup-pack fingerprint and byte-budget signals. +- `skills/llmdoc/templates/codex-hooks.json`, `skills/llmdoc/templates/compact-prompt.md`, and `skills/llmdoc/templates/stop.sh`: Installable lifecycle templates; compact-prompt and `Stop` remain opt-in. +- `skills/llmdoc/scripts/verify-lifecycle-hooks.sh`: Verifier for matcher separation, compact no-reload behavior, and stable startup-pack fingerprinting. - `agents/investigator.md`, `agents/worker.md`, `agents/recorder.md`, `agents/reflector.md`: Claude-style role prompts for the internal workflow. - `.codex/config.toml`: Codex-wide agent fan-out and depth limits for this repository. - `.codex/agents/*.toml`: Project-scoped Codex custom agents. @@ -20,6 +24,9 @@ - `README.zh-CN.md` (`公开接口`): Chinese user-facing contract. - `commands/init.md` (`/llmdoc:init`): Init workflow source of truth. - `commands/update.md` (`/llmdoc:update`): Update workflow source of truth. +- `hooks/hooks.json` (`bundled SessionStart`): Runtime matcher and command source of truth for the plugin-shipped hook. +- `skills/llmdoc/templates/session-start.sh` (`lifecycle payload`): Startup-pack fingerprint, byte-budget, and lifecycle-specific re-entry wording source of truth. - `llmdoc/architecture/update-orchestration.md` (`/llmdoc:update` design): Update mode, commit-watermark model, and knowledge-layer source of truth. +- `llmdoc/architecture/context-lifecycle.md` (`llmdoc re-entry design`): Cold-start, resume, compact-reentry, and startup-pack routing invariants. - `llmdoc/state/sync.md` (`watermark-commit`): Machine-managed change-detection anchor; not a knowledge source of truth. - `.codex/config.toml` (`[agents]`): Codex runtime limit source of truth. diff --git a/llmdoc/startup.md b/llmdoc/startup.md index c2b2224..f50de18 100644 --- a/llmdoc/startup.md +++ b/llmdoc/startup.md @@ -1,11 +1,13 @@ # Startup -Read in order on every run: +Read in order on cold start: 1. `llmdoc/must/project-basics.md` 2. `llmdoc/must/working-agreement.md` 3. `llmdoc/must/doc-routing.md` +After compact re-entry or resume, continue from preserved `LLMDOC_STATE` when it is still usable. Re-read this startup pack only when the state is stale, insufficient, or invalidated by changed evidence. + Escalate to more docs when: - changing `/llmdoc:init` or agent orchestration behavior - updating plugin packaging or Codex integration files diff --git a/skills/README.md b/skills/README.md index 4f4a142..73737e2 100644 --- a/skills/README.md +++ b/skills/README.md @@ -11,6 +11,8 @@ Recommended setup: - Keep Codex-native command-like entry skills in `skills/llmdoc-init/` and `skills/llmdoc-update/` - Keep the detailed working model in `skills/llmdoc/references/` - Keep reusable Codex hook and script templates in `skills/llmdoc/templates/` +- Treat startup as a one-time cold-start package and context compaction as warm re-entry through `LLMDOC_STATE` +- Keep the startup pack bounded; use L0 root routing plus subsystem indexes in monoliths - Let the skill carry the proactive guide/reflection reading protocol and the proactive user-discussion protocol - Keep `/llmdoc:update` behavior aligned across Claude Code commands, Codex helper skills, public README files, and project-scoped agents - Treat `.llmdoc-tmp/` as a local temporary context cache, not stable project memory diff --git a/skills/llmdoc-init/SKILL.md b/skills/llmdoc-init/SKILL.md index bf4c77a..40be664 100644 --- a/skills/llmdoc-init/SKILL.md +++ b/skills/llmdoc-init/SKILL.md @@ -1,7 +1,6 @@ --- name: llmdoc-init description: "Codex-native entry skill for bootstrapping llmdoc. Use this when you want the /llmdoc:init workflow in Codex." -disable-model-invocation: false allowed-tools: Read, Glob, Grep, Bash, Write, Edit, WebSearch, WebFetch --- @@ -52,14 +51,16 @@ Then execute this workflow: - Create `llmdoc/index.md` as the global doc map. - Create `llmdoc/startup.md`. - Create a small set of MUST docs. + - Make the startup pack cold-start-only and keep `index.md` + `startup.md` + `must/` under 24 KiB by default. + - In a monolith, use the root index as an L0 router and add subsystem indexes instead of listing every leaf document. - Create `llmdoc/overview/project-overview.md`. - Create focused architecture and reference docs from the strongest investigation slices first. 5. Synchronize `llmdoc/index.md`. - - Index stable docs. + - Index stable docs directly only when the repository is small; otherwise index subsystem routers that lead to leaf documents. - Keep `memory/reflections/` and `memory/decisions/` separate from stable docs. - Do not treat `.llmdoc-tmp/` as part of llmdoc. 6. Summarize what was created and where the startup docs live. -If the repository already contains `llmdoc/`, read `llmdoc/index.md`, `llmdoc/startup.md`, and the listed MUST docs before making broader changes. +If the repository already contains `llmdoc/`, read `llmdoc/index.md`, `llmdoc/startup.md`, and the listed MUST docs once on cold start before making broader changes. After context compaction, resume from `LLMDOC_STATE` instead of replaying that package. diff --git a/skills/llmdoc-update/SKILL.md b/skills/llmdoc-update/SKILL.md index 8a7faaf..1e15f79 100644 --- a/skills/llmdoc-update/SKILL.md +++ b/skills/llmdoc-update/SKILL.md @@ -1,7 +1,6 @@ --- name: llmdoc-update description: "Codex-native entry skill for keeping tracked llmdoc docs current with the repository using commit-based change detection. Use this when you want the /llmdoc:update workflow in Codex." -disable-model-invocation: false allowed-tools: Read, Glob, Grep, Bash, Write, Edit, WebSearch, WebFetch --- @@ -116,9 +115,9 @@ Self-authored test: a commit is self-authored when its author email (`git log -1 3. Select the mode from range size × authorship × risk; honor the hard floors and backfill cap. 4. Investigate only as needed, seeded with the resolved net-diff path list; scratch reports record the resolved `RANGE_BASE..H` range. 5. Reflect only when there is a workflow failure, repeated mistake, missing signal, or durable process lesson. Do not force a reflection for routine `fast` updates. -6. Update stable llmdoc docs against the batch-tip state: update only impacted docs, correct stale claims, split aggressively, reconcile `llmdoc/memory/doc-gaps.md`. +6. Update stable llmdoc docs against the batch-tip state: update only impacted docs, correct stale claims, split aggressively, reconcile `llmdoc/memory/doc-gaps.md`, and keep the cold-start pack (`index.md` + `startup.md` + `must/`) under 24 KiB by default. In a monolith, keep the root index as an L0 router and use subsystem indexes for leaf docs. 7. Run the active-memory archive check (count files under `llmdoc/memory/` excluding `lessons-learned.md`, `doc-gaps.md`, `archive/`; `llmdoc/state/` is not counted). If > 5, follow `skills/llmdoc/references/lessons-learned.md`. -8. Synchronize `llmdoc/index.md`. Do not index `.llmdoc-tmp/`, and do not index `llmdoc/state/sync.md` as knowledge. +8. Synchronize `llmdoc/index.md`. Keep new docs discoverable without turning the root into a monolith-wide leaf inventory. Do not index `.llmdoc-tmp/`, and do not index `llmdoc/state/sync.md` as knowledge. 9. Advance the watermark (recorder-owned terminal step). Safe-to-advance gate — ALL must hold: the update completed successfully and consumed a committed range; HEAD is attached (`git symbolic-ref -q HEAD` succeeds); and no git operation is in progress — test by whether the resolved path EXISTS on disk (`git rev-parse --git-path` always prints a path and exits 0 regardless of existence, so check with `[ -f ]`/`[ -d ]`), none of `[ -f "$(git rev-parse --git-path MERGE_HEAD)" ]`, `CHERRY_PICK_HEAD`, `REVERT_HEAD`, `[ -d "$(git rev-parse --git-path rebase-merge)" ]`, `rebase-apply` may exist. If the gate holds, advance `watermark-commit` to the captured `H` (or the highest unbroken-prefix tip). Rewrite ONLY these fields, keeping the exact `- watermark-commit: ` line prefix (the reader anchors on it — do not reformat): `watermark-commit` (new full commit SHA), `watermark-subject` (`git log -1 --format=%s `), `updated-at` (ISO-8601 UTC, `date -u +%Y-%m-%dT%H:%M:%SZ`), `updated-by` (`/llmdoc:update`). NEVER advance on a `--working-tree-only` run, a failed/partial run, a HEAD-behind-watermark run, or when the safe-to-advance gate fails. 10. Report the mode used, resolved range(s)/batches and commit count, old → new watermark (or why it did not move), scratch/reflection paths, the archive action, and the stable docs that changed. diff --git a/skills/llmdoc/SKILL.md b/skills/llmdoc/SKILL.md index 21cfa1c..b183cfd 100644 --- a/skills/llmdoc/SKILL.md +++ b/skills/llmdoc/SKILL.md @@ -1,7 +1,6 @@ --- name: llmdoc -description: "Default operating skill for llmdoc-enabled projects. Use when a project has llmdoc/, when initializing llmdoc, when updating project knowledge, or when Codex CLI hooks should reinforce the workflow." -disable-model-invocation: false +description: "Default operating skill for llmdoc-enabled projects. Use at cold start when a project has llmdoc/, when initializing or updating project knowledge, or when configuring Codex lifecycle hooks. After context compaction, continue from preserved LLMDOC_STATE and do not repeat cold-start reads unless the state is stale or insufficient." allowed-tools: Read, Glob, Grep, Bash, Write, Edit, WebSearch, WebFetch --- @@ -16,9 +15,9 @@ Use it whenever: - the task touches architecture, workflow, conventions, or doc structure - you want Codex CLI `SessionStart` or `Stop` hooks to reinforce the workflow -## Load Order +## Cold-start Load Order -Read these references in order: +Read these references once at cold start, in order: 1. `references/design-goals.md` 2. `references/operating-protocol.md` @@ -31,9 +30,12 @@ Then load only the specific extras you need: - `references/templates.md` for document templates - `references/codex-cli-hooks.md` for Codex CLI hook support +After a context compaction, do not read this skill or its references again merely because compaction occurred. Continue from the compact summary and its `LLMDOC_STATE`; use the re-entry rules in `references/operating-protocol.md` when targeted refresh is necessary. + ## Core Rules -- Read `llmdoc/index.md`, then `llmdoc/startup.md`, then the MUST files it lists. +- On cold start, read `llmdoc/index.md`, then `llmdoc/startup.md`, then the MUST files it lists. +- Treat context compaction as warm re-entry, not a new run. Do not reload the startup pack or already-loaded task docs unless state or evidence invalidates them. - Proactively read relevant `guides/` and `memory/reflections/` before non-trivial edits. - The main assistant, not `worker`, aligns with the user before non-trivial edits. - At the end of a non-trivial task, the main assistant should consider prompting for `/llmdoc:update`. @@ -46,6 +48,7 @@ Then load only the specific extras you need: Codex CLI `SessionStart` and `Stop` hook support lives here: - `references/codex-cli-hooks.md` +- `scripts/verify-lifecycle-hooks.sh` - `templates/codex-hooks.json` - `templates/session-start.sh` - `templates/stop.sh` diff --git a/skills/llmdoc/references/codex-cli-hooks.md b/skills/llmdoc/references/codex-cli-hooks.md index 2f40577..ea1c10f 100644 --- a/skills/llmdoc/references/codex-cli-hooks.md +++ b/skills/llmdoc/references/codex-cli-hooks.md @@ -1,9 +1,9 @@ # Codex CLI Hooks -This repository now explicitly supports Codex CLI hooks for: +This repository explicitly supports Codex CLI hooks for: -- `SessionStart` -- `Stop` +- lifecycle-aware `SessionStart` +- an optional `Stop` template Why these two first: @@ -15,14 +15,29 @@ Why these two first: ### `SessionStart` -Use it to inject lightweight context at the start of a session. +Use separate matchers for each lifecycle source: -Good uses: +- `startup|clear`: cold start; load the skill and startup pack once +- `resume`: reuse a valid `LLMDOC_STATE`, otherwise cold start +- `compact`: warm re-entry; do not replay the skill or startup pack + +The compact branch should inject only the current startup-pack fingerprint, byte size, and targeted invalidation rules. A compact event alone is not evidence that project knowledge changed. + +The provided script fingerprints `llmdoc/index.md`, `llmdoc/startup.md`, and files under `llmdoc/must/`. It also reports their combined UTF-8 byte size against `LLMDOC_STARTUP_MAX_BYTES` (24 KiB by default). + +### Compact summary state + +`templates/compact-prompt.md` is an optional compaction-prompt override that preserves a structured `LLMDOC_STATE`. It stores paths and distilled task facts, not full document bodies. -- remind Codex to load the `llmdoc` skill -- remind Codex to read `llmdoc/index.md` and `llmdoc/startup.md` -- remind Codex to proactively read guides and reflections -- remind Codex to align with the user before non-trivial edits +Use the lifecycle hook even without the override: it fixes the deterministic reload caused by treating `compact` as cold start. Use the prompt template when stronger summary-shape guarantees justify overriding Codex's built-in compaction prompt. Keep the override reviewed as Codex evolves; do not enable it automatically. + +To opt in, copy the template to a stable project path and point project `.codex/config.toml` at it: + +```toml +experimental_compact_prompt_file = ".codex/llmdoc-compact-prompt.md" +``` + +The file-backed key is experimental. Remove the setting to return to Codex's built-in prompt. ### `Stop` @@ -31,7 +46,7 @@ Use it for end-of-turn review or lightweight cleanup. Treat it as a best-effort Good uses: - append a stop-hook record into `.llmdoc-tmp/` -- add lightweight review context after a turn ends +- show a lightweight UI reminder after a turn ends - capture raw hook payloads for troubleshooting - remind the assistant that active memory may need archiving @@ -46,13 +61,18 @@ Codex hooks are configured in `hooks.json` files such as: - `~/.codex/hooks.json` - `/.codex/hooks.json` -The official hooks reference says `SessionStart` can add context through `hookSpecificOutput.additionalContext`, while `Stop` can continue a turn with review feedback. +The official hooks reference says `SessionStart` can add context through `hookSpecificOutput.additionalContext`. `Stop` can show `systemMessage` feedback or continue a turn with a blocking decision; the provided optional template only logs and shows a reminder. Recommended template files in this skill: - `templates/codex-hooks.json` - `templates/session-start.sh` - `templates/stop.sh` +- `templates/compact-prompt.md` + +After changing lifecycle behavior, run `scripts/verify-lifecycle-hooks.sh ` to verify separated matchers, stable fingerprints, and the absence of cold-start instructions in compact output. + +The Codex plugin also ships `hooks/hooks.json`, which uses default plugin hook discovery and `$PLUGIN_ROOT`. Installed plugin hooks require the normal Codex trust review. The bundled hook includes only `SessionStart`; the more opinionated `Stop` behavior remains opt-in. ## Security @@ -70,3 +90,4 @@ Official references: - https://developers.openai.com/codex/plugins - https://developers.openai.com/codex/plugins/build - https://developers.openai.com/codex/hooks +- https://developers.openai.com/codex/config-reference diff --git a/skills/llmdoc/references/design-goals.md b/skills/llmdoc/references/design-goals.md index fab19de..9a118e2 100644 --- a/skills/llmdoc/references/design-goals.md +++ b/skills/llmdoc/references/design-goals.md @@ -9,6 +9,7 @@ - Faster context: good docs compress high-value context faster than broad code search. - Better architectural alignment: docs explain intended boundaries, not only incidental implementation. - Lower context thrash: repeatedly searching the same code paths is expensive and noisy. +- Bounded recovery: compaction should preserve a small task state instead of replaying startup context. - Durable learning: reflection and updates reduce repeated mistakes across tasks. ## Core outcome @@ -17,5 +18,6 @@ - high density - reusable across tasks +- bounded independently of total repository and llmdoc size - structured by concept instead of accidental file order - able to improve over time through reflection and updates diff --git a/skills/llmdoc/references/doc-structure.md b/skills/llmdoc/references/doc-structure.md index 15a657d..385a746 100644 --- a/skills/llmdoc/references/doc-structure.md +++ b/skills/llmdoc/references/doc-structure.md @@ -30,7 +30,7 @@ Use this split: `llmdoc/index.md` should contain: - the purpose of each top-level category -- the major documents available in each category +- the major documents or subsystem indexes available in each category - routing hints for `must/`, `overview/`, `architecture/`, `guides/`, `reference/`, and `memory/` `llmdoc/startup.md` should contain: @@ -38,6 +38,19 @@ Use this split: - only the startup reading order - short escalation hints for what to read next +## Context budgets and monolith routing + +The model-visible startup cost must remain bounded independently of the total number of llmdoc documents. + +- Keep the root `index.md` as an L0 router. In a monolith, point it at subsystem indexes instead of listing every leaf document. +- Put L1 subsystem indexes beside the documents they route, for example `llmdoc/architecture/payments/index.md`. +- Load only the L1 index for the active subsystem, then only the leaf documents needed for the task. +- Do not add every subsystem index or leaf document to `startup.md`. +- Keep the UTF-8 size of `index.md` + `startup.md` + `must/` under 24 KiB by default. A project may set a stricter `LLMDOC_STARTUP_MAX_BYTES`; exceeding the budget is a maintenance signal, not permission to omit required invariants silently. +- Prefer no more than about eight task documents at once. If more appear necessary, route again by responsibility or runtime flow. + +The byte limit is a deterministic proxy rather than an exact model-token count. It exists to prevent unbounded growth across models and languages. + ## Ownership - `recorder` owns `llmdoc/index.md`, `llmdoc/startup.md`, all stable docs, `memory/decisions/`, and `memory/doc-gaps.md` @@ -50,6 +63,7 @@ Use this split: - One workflow per guide. - One ownership boundary or invariant cluster per architecture doc. - Put repeated startup knowledge in `must/`, not in `overview/`. +- In monoliths, add subsystem indexes instead of growing the root index into a leaf catalog. - Put mistakes and raw learnings in `memory/reflections/`, then promote only recurring stable lessons. - Keep temporary investigation reports in `.llmdoc-tmp/`, not in `llmdoc/memory/`. diff --git a/skills/llmdoc/references/operating-protocol.md b/skills/llmdoc/references/operating-protocol.md index fa00d0d..981facd 100644 --- a/skills/llmdoc/references/operating-protocol.md +++ b/skills/llmdoc/references/operating-protocol.md @@ -1,8 +1,16 @@ # Operating Protocol -## Startup reads +## Lifecycle modes -If `llmdoc/` exists: +Distinguish three modes: + +- **Cold start** (`startup` or `clear`): load the core skill and startup pack once. +- **Resume**: reuse a valid preserved state; otherwise fall back to one cold start. +- **Compact re-entry**: continue the same task from the compact summary. Compaction alone never triggers a full llmdoc reload. + +## Cold-start reads + +On a cold start, if `llmdoc/` exists: 1. Read `llmdoc/index.md`. 2. Read `llmdoc/startup.md` when it exists. @@ -16,16 +24,34 @@ Why: - proactive guide reads reduce avoidable implementation mistakes - proactive reflection reads reduce repeated workflow mistakes +The cold-start package must stay bounded as the repository grows. Treat `index.md` as a top-level router, not an inventory of every leaf document, and keep `startup.md` plus `must/` small enough to load once without crowding out task work. + ## Re-entry rules -During execution, re-read relevant docs before broad code search when: +After compact re-entry, trust the compact summary and its `LLMDOC_STATE`. Do not re-read the core skill, `index.md`, `startup.md`, MUST docs, lessons, or already-loaded task docs just because context was compacted. + +The compact state should preserve only: + +- startup-pack fingerprint when available +- active goal and exact next action +- loaded document paths and task-critical invariants +- decisions, changed files, validation status, and unresolved risks + +Do not copy full document bodies into the state. + +Re-read the smallest relevant document set before broad code search only when: - entering a new subsystem +- the preserved startup fingerprint differs from the current one +- a relevant document changed +- the compact state lacks a fact required for the next action - seeing conflicting evidence - hitting a failed command or test - needing stronger confidence before editing - seeing a related guide or reflection that might improve quality +If refresh is needed, start with the named task document or subsystem index. Fall back to the complete cold-start package only when required invariants cannot otherwise be recovered. + ## Collaboration Before planning or editing non-trivial code, the main assistant should actively synchronize with the user unless the request is trivial and unambiguous. diff --git a/skills/llmdoc/references/templates.md b/skills/llmdoc/references/templates.md index 72fee52..30cfc4d 100644 --- a/skills/llmdoc/references/templates.md +++ b/skills/llmdoc/references/templates.md @@ -23,7 +23,8 @@ - Add the main architecture, guide, and reference docs here with one-line descriptions ## Routing Rules -- Read `startup.md` for startup context +- Read `startup.md` once for cold-start context +- After context compaction, continue from `LLMDOC_STATE` instead of replaying the startup pack - Read `guides/` before editing a known workflow - Read `memory/reflections/` before repeating a workflow or revisiting a problematic subsystem ``` @@ -33,7 +34,7 @@ ```md # Startup -Read in order on every run: +Read in order once on cold start: 1. `llmdoc/must/project-basics.md` 2. `llmdoc/must/working-agreement.md` @@ -45,8 +46,12 @@ Escalate to more docs when: - updating workflows or stable docs Read related guides and reflections before editing when available. + +After context compaction, continue from the preserved task state. Re-read only the smallest relevant document set when the state is stale, insufficient, or the work enters a new subsystem. ``` +For a monolith, keep the root index as an L0 router and add subsystem indexes such as `llmdoc/architecture//index.md`; never add every leaf document to the startup list. + ## `state/sync.md` Machine-managed commit watermark. Seed at init with `watermark-commit=$(git rev-parse HEAD)`. Keep the `watermark-commit` line isolated by blank lines (minimizes merge-conflict surface). Never index it, never add it to `startup.md`/`must/`, never count it as active memory. diff --git a/skills/llmdoc/scripts/verify-lifecycle-hooks.sh b/skills/llmdoc/scripts/verify-lifecycle-hooks.sh new file mode 100755 index 0000000..ed60202 --- /dev/null +++ b/skills/llmdoc/scripts/verify-lifecycle-hooks.sh @@ -0,0 +1,61 @@ +#!/usr/bin/env bash +set -eu + +skill_root="$(CDPATH= cd -- "$(dirname -- "$0")/.." && pwd)" +plugin_root="$(CDPATH= cd -- "$skill_root/../.." && pwd)" +project_dir="${1:-$plugin_root}" +session_script="$skill_root/templates/session-start.sh" +template_hooks="$skill_root/templates/codex-hooks.json" +bundled_hooks="$plugin_root/hooks/hooks.json" + +fail() { + printf 'FAIL: %s\n' "$1" >&2 + exit 1 +} + +assert_contains() { + value="$1" + expected="$2" + printf '%s' "$value" | grep -F "$expected" >/dev/null \ + || fail "expected output to contain: $expected" +} + +assert_not_contains() { + value="$1" + unexpected="$2" + if printf '%s' "$value" | grep -F "$unexpected" >/dev/null; then + fail "expected output not to contain: $unexpected" + fi +} + +cold_output="$(CDPATH= cd -- "$project_dir" && "$session_script" cold)" +resume_output="$(CDPATH= cd -- "$project_dir" && "$session_script" resume)" +compact_output="$(CDPATH= cd -- "$project_dir" && "$session_script" compact)" + +assert_contains "$cold_output" "LLMDOC_COLD_START v1" +assert_contains "$cold_output" "Load the llmdoc skill once" +assert_contains "$resume_output" "LLMDOC_RESUME v1" +assert_contains "$compact_output" "LLMDOC_COMPACT_REENTRY v1" +assert_contains "$compact_output" "Do not reload the llmdoc skill" +assert_not_contains "$compact_output" "Load the llmdoc skill once" + +cold_fingerprint="$(printf '%s' "$cold_output" | sed -n 's/.*startup-pack-fingerprint=\([0-9a-z-]*\).*/\1/p')" +compact_fingerprint="$(printf '%s' "$compact_output" | sed -n 's/.*startup-pack-fingerprint=\([0-9a-z-]*\).*/\1/p')" + +[ -n "$cold_fingerprint" ] || fail "cold-start fingerprint is missing" +[ "$cold_fingerprint" = "$compact_fingerprint" ] \ + || fail "cold-start and compact fingerprints differ" + +assert_contains "$(<"$template_hooks")" '"matcher": "^(startup|clear)$"' +assert_contains "$(<"$template_hooks")" '"matcher": "^resume$"' +assert_contains "$(<"$template_hooks")" '"matcher": "^compact$"' +assert_contains "$(<"$bundled_hooks")" '"matcher": "^(startup|clear)$"' +assert_contains "$(<"$bundled_hooks")" '"matcher": "^resume$"' +assert_contains "$(<"$bundled_hooks")" '"matcher": "^compact$"' + +if command -v jq >/dev/null 2>&1; then + jq -e . "$template_hooks" >/dev/null + jq -e . "$bundled_hooks" >/dev/null +fi + +printf 'Lifecycle hook verification passed. fingerprint=%s\n' "$cold_fingerprint" diff --git a/skills/llmdoc/templates/codex-hooks.json b/skills/llmdoc/templates/codex-hooks.json index 8a24f59..8fc2ad4 100644 --- a/skills/llmdoc/templates/codex-hooks.json +++ b/skills/llmdoc/templates/codex-hooks.json @@ -2,11 +2,32 @@ "hooks": { "SessionStart": [ { - "matcher": "startup|resume|clear|compact", + "matcher": "^(startup|clear)$", "hooks": [ { "type": "command", - "command": "\"$(git rev-parse --show-toplevel)\"/skills/llmdoc/templates/session-start.sh" + "command": "\"${PLUGIN_ROOT:-$(git rev-parse --show-toplevel)}\"/skills/llmdoc/templates/session-start.sh cold", + "additionalContextLimit": 1200 + } + ] + }, + { + "matcher": "^resume$", + "hooks": [ + { + "type": "command", + "command": "\"${PLUGIN_ROOT:-$(git rev-parse --show-toplevel)}\"/skills/llmdoc/templates/session-start.sh resume", + "additionalContextLimit": 1200 + } + ] + }, + { + "matcher": "^compact$", + "hooks": [ + { + "type": "command", + "command": "\"${PLUGIN_ROOT:-$(git rev-parse --show-toplevel)}\"/skills/llmdoc/templates/session-start.sh compact", + "additionalContextLimit": 1200 } ] } @@ -16,7 +37,7 @@ "hooks": [ { "type": "command", - "command": "\"$(git rev-parse --show-toplevel)\"/skills/llmdoc/templates/stop.sh" + "command": "\"${PLUGIN_ROOT:-$(git rev-parse --show-toplevel)}\"/skills/llmdoc/templates/stop.sh" } ] } diff --git a/skills/llmdoc/templates/compact-prompt.md b/skills/llmdoc/templates/compact-prompt.md new file mode 100644 index 0000000..ec9b190 --- /dev/null +++ b/skills/llmdoc/templates/compact-prompt.md @@ -0,0 +1,23 @@ +# Compaction Prompt With llmdoc Re-entry State + +Create a concise, self-contained continuation summary of the conversation. Preserve the user's active request, developer and repository constraints, decisions, completed work, changed files, tool results, validation status, unresolved risks, and the exact next action. Omit noisy logs and superseded exploration. + +When llmdoc is active, include this compact block: + +```yaml +LLMDOC_STATE: + version: 1 + bootstrap_fingerprint: + active_goal: + loaded_docs: + - path: + relevance: + invariants: [] + decisions: [] + changed_files: [] + validation: [] + next_action: + unresolved: [] +``` + +Store paths and distilled task-critical facts, not full llmdoc document bodies. State that a compact event alone must not trigger reloading the llmdoc skill, index, startup pack, or already-loaded task documents. A later model should re-read only the smallest relevant document set when the state is missing required facts, the fingerprint is stale, a relevant file changed, work enters a new subsystem, or evidence conflicts. diff --git a/skills/llmdoc/templates/session-start.sh b/skills/llmdoc/templates/session-start.sh index 6bbe850..d3d9148 100755 --- a/skills/llmdoc/templates/session-start.sh +++ b/skills/llmdoc/templates/session-start.sh @@ -1,24 +1,108 @@ #!/usr/bin/env bash set -eu +session_mode="${1:-cold}" project_dir="${CLAUDE_PROJECT_DIR:-$PWD}" -if [ -d "$project_dir/llmdoc" ]; then +if command -v git >/dev/null 2>&1; then + git_root="$(git -C "$project_dir" rev-parse --show-toplevel 2>/dev/null || true)" + if [ -n "$git_root" ]; then + project_dir="$git_root" + fi +fi + +if [ ! -d "$project_dir/llmdoc" ]; then + if [ "$session_mode" = compact ]; then + exit 0 + fi + cat <<'EOF' { "hookSpecificOutput": { "hookEventName": "SessionStart", - "additionalContext": "This project has llmdoc enabled. Load the llmdoc skill. Read llmdoc/index.md, then llmdoc/startup.md, then the MUST docs listed there. If llmdoc/memory/lessons-learned.md exists, read it as the archived memory summary. Before non-trivial edits, proactively read relevant guides and reflections and align with the user on approach." + "additionalContext": "No llmdoc directory was detected. Fall back to README files and source code. If durable documentation would help, consider /llmdoc:init." } } EOF + exit 0 +fi + +list_startup_files() { + [ -f "$project_dir/llmdoc/index.md" ] && printf '%s\n' "$project_dir/llmdoc/index.md" + [ -f "$project_dir/llmdoc/startup.md" ] && printf '%s\n' "$project_dir/llmdoc/startup.md" + if [ -d "$project_dir/llmdoc/must" ]; then + find "$project_dir/llmdoc/must" -type f -print + fi +} + +file_digest() { + file_path="$1" + if command -v git >/dev/null 2>&1; then + git hash-object "$file_path" + else + cksum < "$file_path" | awk '{ print $1 "-" $2 }' + fi +} + +startup_manifest() { + list_startup_files \ + | LC_ALL=C sort \ + | while IFS= read -r file_path; do + relative_path="${file_path#"$project_dir"/}" + byte_count="$(wc -c < "$file_path" | tr -d '[:space:]')" + printf '%s %s %s\n' "$(file_digest "$file_path")" "$byte_count" "$relative_path" + done +} + +manifest="$(startup_manifest)" +startup_bytes="$(printf '%s\n' "$manifest" | awk '{ total += $2 } END { print total + 0 }')" + +if command -v git >/dev/null 2>&1; then + startup_fingerprint="$(printf '%s' "$manifest" | git hash-object --stdin)" else - cat <<'EOF' + startup_fingerprint="cksum-$(printf '%s' "$manifest" | cksum | awk '{ print $1 "-" $2 }')" +fi + +startup_budget="${LLMDOC_STARTUP_MAX_BYTES:-24576}" +case "$startup_budget" in + ''|*[!0-9]*) startup_budget=24576 ;; +esac + +if [ "$startup_bytes" -gt "$startup_budget" ]; then + budget_note="The startup pack exceeds the recommended byte budget; load it for this cold start, then use /llmdoc:update to shrink MUST docs or add layered routing." +else + budget_note="The startup pack is within the recommended byte budget." +fi + +case "$session_mode" in + compact) + cat </dev/null 2>&1; then + git_root="$(git -C "$project_dir" rev-parse --show-toplevel 2>/dev/null || true)" + if [ -n "$git_root" ]; then + project_dir="$git_root" + fi +fi + tmp_dir="$project_dir/.llmdoc-tmp/hooks" timestamp="$(date -u +%Y%m%dT%H%M%SZ)" @@ -23,19 +31,13 @@ fi if [ "$active_memory_count" -gt 5 ]; then cat <