Skip to content

Commit d8ea172

Browse files
authored
fix(resume): semanticDecision resume + C22 temporary artifact cleanup (#8)
* fix(resume): validate semanticDecision against the full snapshot envelope when the resume stub is identity-only * governance(C22): require cleanup of AI-created temporary verification artifacts (TemporaryArtifactCleanup, PI-001)
1 parent 601469d commit d8ea172

12 files changed

Lines changed: 95 additions & 22 deletions

‎changelogs/unreleased.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,13 @@ TaskLifecycle 规范能力吸纳、源码操作状态恢复及本次入口/CP/
1111

1212
修复和边界见 [v1.20.5候选说明](releases/v1.20.5.md)。当前仅准备本地候选,不表示已发布或最终资格通过。下方既有历史条目保留,不批量移入新版本。
1313

14+
## 治理:临时验证产物清理(C22 扩展,PI-001)
15+
16+
- C22 增补 TemporaryArtifactCleanup:收尾前删除 AI 为测试/验证自建的临时文件与状态并在报告列出;用户要求保留时记录原因与位置;不得删除用户拥有或已提交的文件。源为 `content/instructions.md` 与 `content/instructions/01-common.instructions.md`,投影已再生成,validate-governance-quality 增加锚点。
17+
1418
## Grok Bot 宿主驱动(grok-bot)
1519

20+
- 续接修复:续接旧任务时 `profile_context_plan` 携带 semanticDecision,`profile_load` 后续接存根因超出 8 KiB 被压缩为仅含身份的指令信封(`minimal-identity-only`),准入用该 5 字段存根校验语义来源而报 `FINALIZED_TASK_RESUME_SESSION_MISMATCH`。现改为从内容寻址、摘要校验的准入 ingress 快照取回身份完全一致的完整信封再做原有全部校验(不放宽);新增复现回归测试。
1621
- 会话隔离:Grok Bot 多会话共用连接器与 `grok-bot-session` 时会互相覆盖同一项目的 lifecycle-state。现驱动 `turn-begin`/`observe-tool` 支持 `--session-id`,连接器每次调用可携带 `hostSession`(校验格式与 contextBinding 归属,逐调用切换会话);仅对 grok-bot 驱动宿主,lifecycle/恢复/准入/路由状态按 `.memory/hooks/<project>/.sessions/<id>/` 分区,其他宿主不变。新增 `hooks/_runtime/host-session-partition.cjs` 与双会话交错/任务恢复不越界测试。
1722
- 新增 `grok-bot` hostVariant(`grok-bot/driver-synthesized-local-stdio`,UNVERIFIED、非默认):`host-adapter-identity.cjs` 注册变体/别名,`DEVCODEX_AGENT=grok-bot` 归一为 `grok` agent,不再落入 `unknown-agent`。
1823
- `lifecycle-host-adapters.cjs grok-bot` 复用 Grok 事件映射,事件与输出均标记 `devcodexEventProvenance=driver-synthesized`,不冒充原生宿主事件。

‎content/duplication-inventory.json‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"schemaVersion": "ControlContentDuplicationInventoryV1",
3-
"sourceBundleDigest": "034cda1c943f913bfcb24cc58203d4256e35e0d0e797193206d3cfde6f2bb785",
3+
"sourceBundleDigest": "aa050f84949ce439e2e10eee0474bea4d9364a7eba35acf412be8aba6e4d80fc",
44
"thresholds": {
55
"minParagraphChars": 100,
66
"sectionThreshold": 0.94,

‎content/instructions.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@
6868
| C19 | 确认后前置复审 | 每次显式或 Auto CP 决定后、进入下一阶段前,必须执行 `PostConfirmationReviewScopeGate` 并输出 **ReviewGradeCard**:映射 **轻量=R1**(低风险单文件/纯文案,须 `skipReason`)、**标准=R2**(默认)、**全面=R3**(高风险/多模块/公共 API·配置/安全/package·adapter/文档消费者/控制面/多真相源 + 冻结清单)、**发布安全=R4**;命中控制面 / 多文件联动 / 真相源同步 / 模板-示例-校验链须追加交叉验证。阻断项先修正并回到结构化意图重算;不得把复审失败、receipt 过期或同任务范围变化自行投影成重复确认。ECR 默认 R2,不得钉死为「永远轻量」 |
6969
| C20 | 官方文档证据前置 | 新增/升级第三方依赖、框架、SDK、平台 API 或外部模块前,必须先读取官方使用文档/官方参考资料并形成 `OfficialDocsEvidence`;缺失证据时不得进入编码 |
7070
| C21 | Profile 联动判定 | dev/fix 修改项目技术栈、目录边界、脚本、测试/发布路线、分发面、配置项、长期连接或本地 overlay schema 时,必须执行 `ProfileImpactCheck`:更新 Profile 或写明跳过理由 |
71-
| C22 | AI 自启动服务清理(ServiceLifecycleCleanup) | AI 为验证启动 dev server、文档站、本地 API/mock、数据库代理、SSH 隧道、Playwright/Cypress server、压测 target 等长运行进程时,必须记录启动命令、cwd、PID/job、端口/URL;验证完成、失败或中断收尾前主动停止仅由 AI 启动的服务并核验端口释放;不得杀用户既有进程;用户明确要求保留时记录保留原因、PID/端口和关闭方式 |
71+
| C22 | AI 自启动服务清理(ServiceLifecycleCleanup) | AI 为验证启动 dev server、文档站、本地 API/mock、数据库代理、SSH 隧道、Playwright/Cypress server、压测 target 等长运行进程时,必须记录启动命令、cwd、PID/job、端口/URL;验证完成、失败或中断收尾前主动停止仅由 AI 启动的服务并核验端口释放;不得杀用户既有进程;用户明确要求保留时记录保留原因、PID/端口和关闭方式;临时验证产物清理(TemporaryArtifactCleanup):收尾前删除 AI 为测试/验证自建的临时文件与状态(草稿脚本、探针、参数/响应临时文件、临时 worktree/stash、测试会话目录等),并在报告中列出已删除项;用户明确要求保留时记录保留原因与位置;不得删除用户拥有或已提交的文件 |
7272

7373
### ConcurrencyPolicy(C07)
7474

‎content/instructions/01-common.instructions.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ version: 1.20.7
7474
| C19 | 确认后前置复审 | 每次显式或 Auto CP 决定后、进入下一阶段前,必须执行 `PostConfirmationReviewScopeGate` 并输出 **ReviewGradeCard**:映射 **轻量=R1**(须 `skipReason`)、**标准=R2**(默认)、**全面=R3**(高风险+冻结清单)、**发布安全=R4**;命中控制面 / 多文件联动 / 真相源同步 / 模板-示例-校验链必须追加交叉验证;阻断项先修正并回到结构化意图重算,不得由复审器直接要求重复确认。ECR 默认 R2,禁止「永远轻量」口径 |
7575
| C20 | 官方文档证据前置 | 新增/升级依赖、框架、SDK、平台 API 或外部模块前必须形成 `OfficialDocsEvidence`;缺失证据不得进入编码 |
7676
| C21 | Profile 联动判定 | dev/fix 项目事实变化后必须执行 `ProfileImpactCheck`:更新 Profile 或写明跳过理由 |
77-
| C22 | AI 自启动服务清理(ServiceLifecycleCleanup) | AI 为验证启动 dev server、文档站、本地 API/mock、数据库代理、SSH 隧道、Playwright/Cypress server、压测 target 等长运行进程时,必须记录启动命令、cwd、PID/job、端口/URL;验证完成、失败或中断收尾前主动停止仅由 AI 启动的服务并核验端口释放;不得杀用户既有进程;用户明确要求保留时记录保留原因、PID/端口和关闭方式 |
77+
| C22 | AI 自启动服务清理(ServiceLifecycleCleanup) | AI 为验证启动 dev server、文档站、本地 API/mock、数据库代理、SSH 隧道、Playwright/Cypress server、压测 target 等长运行进程时,必须记录启动命令、cwd、PID/job、端口/URL;验证完成、失败或中断收尾前主动停止仅由 AI 启动的服务并核验端口释放;不得杀用户既有进程;用户明确要求保留时记录保留原因、PID/端口和关闭方式;临时验证产物清理(TemporaryArtifactCleanup):收尾前删除 AI 为测试/验证自建的临时文件与状态(草稿脚本、探针、参数/响应临时文件、临时 worktree/stash、测试会话目录等),并在报告中列出已删除项;用户明确要求保留时记录保留原因与位置;不得删除用户拥有或已提交的文件 |
7878

7979
### ConcurrencyPolicy(C07)
8080

‎hooks/_runtime/workflow-root-registry.v1.json‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
"sourceEvidence": [
44
{
55
"ref": "content:instructions/01-common.instructions.md",
6-
"digest": "931fd0f2541a91c024535acbf268055ad8ddeb46be6df5989f2a30dd4fe4cbc5"
6+
"digest": "a612e188c1e4b54acc5162262360bdb09248cbf28ca59fd5f22eaf166fa396b6"
77
},
88
{
99
"ref": "content:skills/routing/SKILL.md",
@@ -14,7 +14,7 @@
1414
"digest": "e3e64094eaed8fd55432d67befc5135ab321c21d59b1f679b65982f8bf585381"
1515
}
1616
],
17-
"sourceDigest": "920582706c85065731db1bb2f648f09613531b323a48d2089174cfd6e06a7681",
17+
"sourceDigest": "cc4aedfb3dc3d3d223b57a5ad3c2e20e870499e2fe76fba9ad93e79e8ed8a44f",
1818
"baseBundles": {
1919
"chat": [],
2020
"resume": [

‎hooks/_runtime/workflow-root-registry.v2.json‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1100,7 +1100,7 @@
11001100
"sourceEvidence": [
11011101
{
11021102
"ref": "content:instructions/01-common.instructions.md",
1103-
"digest": "931fd0f2541a91c024535acbf268055ad8ddeb46be6df5989f2a30dd4fe4cbc5"
1103+
"digest": "a612e188c1e4b54acc5162262360bdb09248cbf28ca59fd5f22eaf166fa396b6"
11041104
},
11051105
{
11061106
"ref": "content:skills/routing/SKILL.md",
@@ -1115,7 +1115,7 @@
11151115
"digest": "e3e64094eaed8fd55432d67befc5135ab321c21d59b1f679b65982f8bf585381"
11161116
}
11171117
],
1118-
"sourceDigest": "dbe8bd852580b7b56d776dfd876786660077b97c47728e7f4cf521565bfa2cbb",
1118+
"sourceDigest": "2d799a5e86cbc0828a7f1933ebb26ae786b0e2da0ecb71cf45336b8e2a106e4d",
11191119
"routeRevision": "82670f3bca8316fcdb62984b9360d40d32a1f7f8671a1be3ddc7f268ff7547c5",
1120-
"registryDigest": "3961a16107dfe92e0c46337a4ff5aa1e0ea3ff554f225d09fe378146e44ed329"
1120+
"registryDigest": "a210b4611e8bc8aa21aaf80d71309de802c0d3933b957310f6a0825306e0e0fc"
11211121
}

‎host-projections/AGENTS.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# DevCodex — Shared Host Kernel(generated)
22

33
> Generated from the unified DevCodex instructions source. Do not edit this file directly.
4-
> sourceDigest: 87ca32b07dd994b3b7047f290b55b494862b8ce8c30623b6e365efa99a11a110
4+
> sourceDigest: e82b40b4c770244ef04b0f676186acd44b164619726efb47898e3772dca42937
55
> configDigest: 9a6dafe5365c4d43a9a1255e1b5cffa83b7f9b8144b060cb205a8d98beab9457
66
> Full fallback: user://agents/devcodex/instructions.full.md
77
> Host surface: Codex / Claude import / Gemini import / Grok native AGENTS.md
@@ -36,7 +36,7 @@
3636
- C19:确认后前置复审:每次显式或 Auto CP 决定后、进入下一阶段前,必须执行 `PostConfirmationReviewScopeGate` 并输出 ReviewGradeCard:映射 轻量=R1(低风险单文件/纯文案,须 `skipReason`)、标准=R2(默认)、全面=R3(高风险/多模块/公共 API·配置/安全/package·adapter/文档消费者/控制面/多真相源 + 冻结清单)、发布安全=R4;命中控制面 / 多文件联动 / 真相源同步 / 模板-示例-校验链须追加交叉验证。阻断项先修正并回到结构化意图重算;不得把复审失败、receipt 过期或同任务范围变化自行投影成重复确认。ECR 默认 R2,不得钉死为「永远轻量」
3737
- C20:官方文档证据前置:新增/升级第三方依赖、框架、SDK、平台 API 或外部模块前,必须先读取官方使用文档/官方参考资料并形成 `OfficialDocsEvidence`;缺失证据时不得进入编码
3838
- C21:Profile 联动判定:dev/fix 修改项目技术栈、目录边界、脚本、测试/发布路线、分发面、配置项、长期连接或本地 overlay schema 时,必须执行 `ProfileImpactCheck`:更新 Profile 或写明跳过理由
39-
- C22:AI 自启动服务清理(ServiceLifecycleCleanup):AI 为验证启动 dev server、文档站、本地 API/mock、数据库代理、SSH 隧道、Playwright/Cypress server、压测 target 等长运行进程时,必须记录启动命令、cwd、PID/job、端口/URL;验证完成、失败或中断收尾前主动停止仅由 AI 启动的服务并核验端口释放;不得杀用户既有进程;用户明确要求保留时记录保留原因、PID/端口和关闭方式
39+
- C22:AI 自启动服务清理(ServiceLifecycleCleanup):AI 为验证启动 dev server、文档站、本地 API/mock、数据库代理、SSH 隧道、Playwright/Cypress server、压测 target 等长运行进程时,必须记录启动命令、cwd、PID/job、端口/URL;验证完成、失败或中断收尾前主动停止仅由 AI 启动的服务并核验端口释放;不得杀用户既有进程;用户明确要求保留时记录保留原因、PID/端口和关闭方式;临时验证产物清理(TemporaryArtifactCleanup):收尾前删除 AI 为测试/验证自建的临时文件与状态(草稿脚本、探针、参数/响应临时文件、临时 worktree/stash、测试会话目录等),并在报告中列出已删除项;用户明确要求保留时记录保留原因与位置;不得删除用户拥有或已提交的文件
4040

4141
## 意图与路由
4242

‎host-projections/copilot-instructions.md‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# DevCodex — Copilot Host Kernel(generated)
22

33
> Generated from the unified DevCodex instructions source. Do not edit this file directly.
4-
> sourceDigest: 87ca32b07dd994b3b7047f290b55b494862b8ce8c30623b6e365efa99a11a110
4+
> sourceDigest: e82b40b4c770244ef04b0f676186acd44b164619726efb47898e3772dca42937
55
> configDigest: 9a6dafe5365c4d43a9a1255e1b5cffa83b7f9b8144b060cb205a8d98beab9457
66
> Full fallback: user://agents/devcodex/instructions.full.md
77
> Host surface: GitHub Copilot CLI user-global instructions; Hooks/MCP/Skills are deployed separately
@@ -36,7 +36,7 @@
3636
- C19:确认后前置复审:每次显式或 Auto CP 决定后、进入下一阶段前,必须执行 `PostConfirmationReviewScopeGate` 并输出 ReviewGradeCard:映射 轻量=R1(低风险单文件/纯文案,须 `skipReason`)、标准=R2(默认)、全面=R3(高风险/多模块/公共 API·配置/安全/package·adapter/文档消费者/控制面/多真相源 + 冻结清单)、发布安全=R4;命中控制面 / 多文件联动 / 真相源同步 / 模板-示例-校验链须追加交叉验证。阻断项先修正并回到结构化意图重算;不得把复审失败、receipt 过期或同任务范围变化自行投影成重复确认。ECR 默认 R2,不得钉死为「永远轻量」
3737
- C20:官方文档证据前置:新增/升级第三方依赖、框架、SDK、平台 API 或外部模块前,必须先读取官方使用文档/官方参考资料并形成 `OfficialDocsEvidence`;缺失证据时不得进入编码
3838
- C21:Profile 联动判定:dev/fix 修改项目技术栈、目录边界、脚本、测试/发布路线、分发面、配置项、长期连接或本地 overlay schema 时,必须执行 `ProfileImpactCheck`:更新 Profile 或写明跳过理由
39-
- C22:AI 自启动服务清理(ServiceLifecycleCleanup):AI 为验证启动 dev server、文档站、本地 API/mock、数据库代理、SSH 隧道、Playwright/Cypress server、压测 target 等长运行进程时,必须记录启动命令、cwd、PID/job、端口/URL;验证完成、失败或中断收尾前主动停止仅由 AI 启动的服务并核验端口释放;不得杀用户既有进程;用户明确要求保留时记录保留原因、PID/端口和关闭方式
39+
- C22:AI 自启动服务清理(ServiceLifecycleCleanup):AI 为验证启动 dev server、文档站、本地 API/mock、数据库代理、SSH 隧道、Playwright/Cypress server、压测 target 等长运行进程时,必须记录启动命令、cwd、PID/job、端口/URL;验证完成、失败或中断收尾前主动停止仅由 AI 启动的服务并核验端口释放;不得杀用户既有进程;用户明确要求保留时记录保留原因、PID/端口和关闭方式;临时验证产物清理(TemporaryArtifactCleanup):收尾前删除 AI 为测试/验证自建的临时文件与状态(草稿脚本、探针、参数/响应临时文件、临时 worktree/stash、测试会话目录等),并在报告中列出已删除项;用户明确要求保留时记录保留原因与位置;不得删除用户拥有或已提交的文件
4040

4141
## 意图与路由
4242

‎host-projections/coverage.json‎

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
{
22
"schemaVersion": "HostInstructionCoverageReceiptV1",
33
"sourceFile": "content/instructions.md",
4-
"sourceDigest": "87ca32b07dd994b3b7047f290b55b494862b8ce8c30623b6e365efa99a11a110",
4+
"sourceDigest": "e82b40b4c770244ef04b0f676186acd44b164619726efb47898e3772dca42937",
55
"configDigest": "9a6dafe5365c4d43a9a1255e1b5cffa83b7f9b8144b060cb205a8d98beab9457",
66
"mode": "kernel",
77
"fallback": {
88
"destination": "user://agents/devcodex/instructions.full.md",
9-
"sourceDigest": "87ca32b07dd994b3b7047f290b55b494862b8ce8c30623b6e365efa99a11a110",
9+
"sourceDigest": "e82b40b4c770244ef04b0f676186acd44b164619726efb47898e3772dca42937",
1010
"available": true
1111
},
1212
"budgets": {
@@ -305,14 +305,14 @@
305305
},
306306
"outputs": {
307307
"host-projections/AGENTS.md": {
308-
"bytes": 11913,
308+
"bytes": 12282,
309309
"lines": 91,
310-
"digest": "89e8ed0ef9da9630436fbcc1ce330b51549b276d5963484c4f60c31d8e135a55"
310+
"digest": "5b4597cb14ca62a9f4c8bd9089cf8d616bea123a854bec07208fe5e69b1c3bdd"
311311
},
312312
"host-projections/copilot-instructions.md": {
313-
"bytes": 11938,
313+
"bytes": 12307,
314314
"lines": 91,
315-
"digest": "81dcdbc33a5816fc54af1418970083a7536ba8208e8b4672906b89f229aa92ca"
315+
"digest": "7a4c84f8009e490b85799647a5a4aea07eab242c3a4c81a988880088c10c9edf"
316316
},
317317
"host-projections/CLAUDE.md": {
318318
"bytes": 520,
@@ -330,5 +330,5 @@
330330
"valid": true,
331331
"errors": []
332332
},
333-
"receiptId": "host-instruction-coverage-9b40100bbd8828617bed9ca11a601628d92618a7a4c8b059aa7d8bbe174fe7a6"
333+
"receiptId": "host-instruction-coverage-0531b9db1555512d9be65c52daccf3904e637faf37e056ded77a27bc40573754"
334334
}

‎mcp/memory-server.js‎

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5339,6 +5339,33 @@ function readServerOwnedLifecycleProjection(target) {
53395339
return { state, statePath, raw }
53405340
}
53415341

5342+
// Rehydrate the full ActualInstructionEnvelope for a resume stub compacted to its
5343+
// identity. Only an exact, digest-verified snapshot whose envelope identity equals
5344+
// the stub (id, digest, epoch, expiry) is returned; anything else yields null.
5345+
function identityOnlyResumeEnvelope(target, state, envelope) {
5346+
const recovery = state?.workflowIngressRecovery || {}
5347+
if (state?.recoveryCompaction !== 'minimal-identity-only' || recovery.authorityMode !== 'identity-only' ||
5348+
envelope?.instructionAuthority !== undefined || !envelope?.envelopeId) return null
5349+
if (recovery.envelopeId !== envelope.envelopeId || recovery.envelopeDigest !== envelope.envelopeDigest) return null
5350+
const scopeKey = LAYOUT.enabled ? assertSingleSegment(target.project, 'project') : 'legacy'
5351+
const read = readAdmissionIngressSnapshot({
5352+
metaDir: path.join(target.activeRoot, '.memory', 'hooks', scopeKey, ...hostSessionPartitionSegments()),
5353+
ingressRef: {
5354+
schemaVersion: 'WorkflowIngressProjectionRefV1',
5355+
envelopeId: recovery.envelopeId,
5356+
envelopeDigest: recovery.envelopeDigest,
5357+
decisionDigest: recovery.decisionDigest,
5358+
routeRevision: recovery.routeRevision
5359+
},
5360+
project: target.project,
5361+
activeRoot: target.activeRoot
5362+
}, { fs, nowMs: Date.now() })
5363+
const full = read.status === 'fresh' ? read.snapshot?.actualInstructionEnvelope : null
5364+
if (!full || full.envelopeId !== envelope.envelopeId || full.envelopeDigest !== envelope.envelopeDigest ||
5365+
full.contextEpoch !== envelope.contextEpoch || full.expiresAt !== envelope.expiresAt) return null
5366+
return full
5367+
}
5368+
53425369
function verifiedResumeLifecycleContext(target, binding) {
53435370
let projection
53445371
try {
@@ -5388,8 +5415,15 @@ function verifiedResumeLifecycleContext(target, binding) {
53885415
// A direct MCP reader has no native PostToolUse route projection. Consume only
53895416
// its separately verified, same-source semantic decision, never fallback prose.
53905417
const semanticDecision = acquisition.plan?.semanticDecision
5418+
// An over-budget resume stub keeps only the envelope identity (recoveryCompaction
5419+
// 'minimal-identity-only'). Validate the semantic source against the exact full
5420+
// envelope from the content-addressed admission ingress snapshot instead of the
5421+
// identity stub; every trusted-source check still runs on that full envelope.
5422+
const semanticEnvelope = semanticDecision
5423+
? identityOnlyResumeEnvelope(target, state, envelope) || envelope
5424+
: envelope
53915425
const semanticValidation = validateIntentSemanticDecision(semanticDecision, {
5392-
envelope, contextEpoch: binding.contextEpoch, hostSessionDigest, requireSource: true
5426+
envelope: semanticEnvelope, contextEpoch: binding.contextEpoch, hostSessionDigest, requireSource: true
53935427
})
53945428
if (semanticDecision && !semanticValidation.valid) {
53955429
return { status: 'invalid', reasonCode: 'lifecycle-semantic-source-invalid', errors: semanticValidation.errors }

0 commit comments

Comments
 (0)