Skip to content

Commit 4da8116

Browse files
committed
feat: add turn liveness recovery controls
1 parent 7f90c6f commit 4da8116

32 files changed

Lines changed: 1007 additions & 31 deletions

‎README.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ DevCodex 通过 `.github/`(Copilot)、`CLAUDE.md + .claude/ + .mcp.json`(C
4040
- **自动报告**: 每次会话自动写入报告,从不询问 — 直接执行
4141
- **安全底线**: S01~S07 七条不可覆盖的安全规则
4242
- **宿主生命周期护栏**: Claude Code 与 OpenAI Codex 在已支持的 Hook 事件上提供 runtime 护栏;Copilot / JetBrains / Cursor 等无等价本地 Hook 时降级为 instruction-fallback;默认 `safety-only` 仅对危险命令硬拦,流程项提醒放行,`strict` 模式才升级可阻断事件
43+
- **长任务 Turn Liveness**: `TurnLivenessRecoveryGate` 记录 `running / awaiting-continuation / suspect / stalled-recoverable / terminal` 状态、工具租约、continuation ACK 与恢复检查点;Hook 只能在事件到达时判断历史停滞,不能自行唤醒宿主、重放写操作或把 `PostToolUse` 当成任务完成
4344
- **全模式入口检查**: 所有模式在实质任务前显示 PC0~PC7;dev 模式额外执行 PC4 规范雷达与完整合规链
4445
- **项目现实扩展**: 先做语义意图初判,再结合目标项目 Profile、目录与当前任务上下文修正最终路由、产物落点和验证方式
4546
- **可配置并发策略**: Profile `config.json` 可配置 `extensions.devcodex.concurrency`;默认 `auto` 表示只读准备和隔离验证可并行、共享状态写入保持单写者,保守项目可设为 `serial`
@@ -497,6 +498,12 @@ DevCodex Hook runtime 不再把所有拦截都等同为“停止”。拦截会
497498
7. **不知道该跑哪个诊断命令**
498499
- `devcodex doctor` 看宿主 / Hook / Profile / 记忆状态
499500
- `devcodex help` 看 CLI 子命令与参数说明
501+
8. **长任务在工具输出后看起来一直挂着**
502+
- `PostToolUse` 只表示工具返回,Turn Liveness 会先进入 `awaiting-continuation`;120 秒后记为 `suspect`,300 秒后记为 `stalled-recoverable`
503+
- 有后续 Hook 事件时,runtime 会基于 checkpoint 生成一次性 `TurnRecoveryCard`;宿主没有继续派发事件时,Hook 本身无法主动唤醒任务
504+
- 工具或 Agent 仍持有有效长租约时不会按 120/300 秒误判;任何恢复都不得自动重放未知副作用的写操作
505+
- gray sidecar 可执行 `npm run check:turn-liveness -- --state <lifecycle-state.json> --json`;安装包消费者可直接运行 `node node_modules/@vextjs/devcodex/scripts/check-turn-liveness.js --state <lifecycle-state.json> --json`
506+
- sidecar 只做一次读取和分类,不 watch、不写状态、不唤醒宿主、不重放操作、不控制进程;输出 `sidecar-observed` 不能冒充 `host-native-verified`
500507

501508
## IDE 兼容性
502509

‎changelogs/unreleased.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
### 2026-07-15
1212

13+
- 实施 ISSUE-043 的 Turn Liveness P0/P1/P2:新增状态机、工具 lease、continuation ACK、checkpoint、`TurnRecoveryCard` 与跨重启重放,接入 `ai-agent-system-architecture` / `host-contract-verification` / `execution-contract` / `rework-prevention-engineering` / TestRoute / report / prompts / Profile,并以 V98、runtime replay、gray sidecar zero-write 与 package fixture 守门;Hook 仅具备事件到达时的历史停滞识别能力,sidecar 保持 one-shot gray,禁止自动唤醒、状态写入、进程控制和未知副作用重放。
1314
- 新增 gray `brand-visual-quality` 与 `BrandVisualQualityGate`,覆盖 MasterLineage、ThemeGeometryParity、MicroOpticalVariant、MonoMaster、VisualEvidencePack、VisualBlockerReset;以 V97 正负 fixture 和独立前向试用守门,未形成真实 WorkUnit 前不晋级 active。
1415
- `RuntimeStateTransitionProjectionGate` 将 append-only 历史迁移、当前权威投影、低权威 consumer drift 和真正 `CONFLICTING_CURRENT_STATE` 分离,保留 schema v1/read-only 兼容并降低 strict conflict 噪声。
1516
- `ProfileReleaseTruthAuthorityMatrixGate` 以 package/plugin 为 DevCodex release authority,对账 project 01/05/07 与 workspace current claim;历史 release/versioned docs 不被批量误判。

‎hooks/_runtime/lifecycle-bootstrap-state.cjs‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@ function buildLifecycleBootstrapStateUtils(ctx) {
2828
INTERCEPTION_ACTION,
2929
noopOutput,
3030
emptyGovernanceIntakeState,
31-
normalizeGovernanceIntakeState
31+
normalizeGovernanceIntakeState,
32+
createTurnLivenessState,
33+
normalizeTurnLivenessState
3234
} = ctx
3335

3436
function buildScopedNeedles(scopeRoot, segments) {
@@ -103,6 +105,7 @@ function buildLifecycleBootstrapStateUtils(ctx) {
103105
},
104106
cp3Runtime: {},
105107
governanceIntake: emptyGovernanceIntakeState(),
108+
turnLiveness: createTurnLivenessState(),
106109
mutated: false,
107110
reportTouched: false,
108111
memoryTouched: false,
@@ -138,6 +141,7 @@ function buildLifecycleBootstrapStateUtils(ctx) {
138141
stickyProject: { ...current.stickyProject, ...(saved.stickyProject || {}), ...(metaState?.stickyProject || {}) },
139142
cp3Runtime: { ...current.cp3Runtime, ...(saved.cp3Runtime || {}) },
140143
governanceIntake: normalizeGovernanceIntakeState(saved.governanceIntake),
144+
turnLiveness: normalizeTurnLivenessState(saved.turnLiveness),
141145
dangerousApprovals: { ...current.dangerousApprovals, ...(saved.dangerousApprovals || {}) }
142146
}
143147
}
@@ -170,6 +174,7 @@ function buildLifecycleBootstrapStateUtils(ctx) {
170174
state.stickyProject = { ...state.stickyProject, ...(previousState?.stickyProject || {}) }
171175
state.cp3Runtime = { ...(previousState?.cp3Runtime || {}) }
172176
state.governanceIntake = normalizeGovernanceIntakeState(previousState?.governanceIntake)
177+
state.turnLiveness = normalizeTurnLivenessState(previousState?.turnLiveness)
173178
state.dangerousApprovals = { ...(previousState?.dangerousApprovals || {}) }
174179
saveState(state)
175180
return state

0 commit comments

Comments
 (0)