From 57b9e1ca61561504867b75f5d3b30009dba2b8e4 Mon Sep 17 00:00:00 2001 From: rocky Date: Sat, 24 Oct 2026 00:03:12 +0800 Subject: [PATCH 1/2] fix(resume): validate semanticDecision against the full snapshot envelope when the resume stub is identity-only --- changelogs/unreleased.md | 1 + mcp/memory-server.js | 36 ++++++++++++++++++++++++++++++++++- scripts/test-grok-bot-host.js | 34 +++++++++++++++++++++++++++++++++ 3 files changed, 70 insertions(+), 1 deletion(-) diff --git a/changelogs/unreleased.md b/changelogs/unreleased.md index 18112196..746f1e00 100644 --- a/changelogs/unreleased.md +++ b/changelogs/unreleased.md @@ -13,6 +13,7 @@ TaskLifecycle 规范能力吸纳、源码操作状态恢复及本次入口/CP/ ## Grok Bot 宿主驱动(grok-bot) +- 续接修复:续接旧任务时 `profile_context_plan` 携带 semanticDecision,`profile_load` 后续接存根因超出 8 KiB 被压缩为仅含身份的指令信封(`minimal-identity-only`),准入用该 5 字段存根校验语义来源而报 `FINALIZED_TASK_RESUME_SESSION_MISMATCH`。现改为从内容寻址、摘要校验的准入 ingress 快照取回身份完全一致的完整信封再做原有全部校验(不放宽);新增复现回归测试。 - 会话隔离:Grok Bot 多会话共用连接器与 `grok-bot-session` 时会互相覆盖同一项目的 lifecycle-state。现驱动 `turn-begin`/`observe-tool` 支持 `--session-id`,连接器每次调用可携带 `hostSession`(校验格式与 contextBinding 归属,逐调用切换会话);仅对 grok-bot 驱动宿主,lifecycle/恢复/准入/路由状态按 `.memory/hooks//.sessions//` 分区,其他宿主不变。新增 `hooks/_runtime/host-session-partition.cjs` 与双会话交错/任务恢复不越界测试。 - 新增 `grok-bot` hostVariant(`grok-bot/driver-synthesized-local-stdio`,UNVERIFIED、非默认):`host-adapter-identity.cjs` 注册变体/别名,`DEVCODEX_AGENT=grok-bot` 归一为 `grok` agent,不再落入 `unknown-agent`。 - `lifecycle-host-adapters.cjs grok-bot` 复用 Grok 事件映射,事件与输出均标记 `devcodexEventProvenance=driver-synthesized`,不冒充原生宿主事件。 diff --git a/mcp/memory-server.js b/mcp/memory-server.js index d93b43a3..39ab1bff 100644 --- a/mcp/memory-server.js +++ b/mcp/memory-server.js @@ -5339,6 +5339,33 @@ function readServerOwnedLifecycleProjection(target) { return { state, statePath, raw } } +// Rehydrate the full ActualInstructionEnvelope for a resume stub compacted to its +// identity. Only an exact, digest-verified snapshot whose envelope identity equals +// the stub (id, digest, epoch, expiry) is returned; anything else yields null. +function identityOnlyResumeEnvelope(target, state, envelope) { + const recovery = state?.workflowIngressRecovery || {} + if (state?.recoveryCompaction !== 'minimal-identity-only' || recovery.authorityMode !== 'identity-only' || + envelope?.instructionAuthority !== undefined || !envelope?.envelopeId) return null + if (recovery.envelopeId !== envelope.envelopeId || recovery.envelopeDigest !== envelope.envelopeDigest) return null + const scopeKey = LAYOUT.enabled ? assertSingleSegment(target.project, 'project') : 'legacy' + const read = readAdmissionIngressSnapshot({ + metaDir: path.join(target.activeRoot, '.memory', 'hooks', scopeKey, ...hostSessionPartitionSegments()), + ingressRef: { + schemaVersion: 'WorkflowIngressProjectionRefV1', + envelopeId: recovery.envelopeId, + envelopeDigest: recovery.envelopeDigest, + decisionDigest: recovery.decisionDigest, + routeRevision: recovery.routeRevision + }, + project: target.project, + activeRoot: target.activeRoot + }, { fs, nowMs: Date.now() }) + const full = read.status === 'fresh' ? read.snapshot?.actualInstructionEnvelope : null + if (!full || full.envelopeId !== envelope.envelopeId || full.envelopeDigest !== envelope.envelopeDigest || + full.contextEpoch !== envelope.contextEpoch || full.expiresAt !== envelope.expiresAt) return null + return full +} + function verifiedResumeLifecycleContext(target, binding) { let projection try { @@ -5388,8 +5415,15 @@ function verifiedResumeLifecycleContext(target, binding) { // A direct MCP reader has no native PostToolUse route projection. Consume only // its separately verified, same-source semantic decision, never fallback prose. const semanticDecision = acquisition.plan?.semanticDecision + // An over-budget resume stub keeps only the envelope identity (recoveryCompaction + // 'minimal-identity-only'). Validate the semantic source against the exact full + // envelope from the content-addressed admission ingress snapshot instead of the + // identity stub; every trusted-source check still runs on that full envelope. + const semanticEnvelope = semanticDecision + ? identityOnlyResumeEnvelope(target, state, envelope) || envelope + : envelope const semanticValidation = validateIntentSemanticDecision(semanticDecision, { - envelope, contextEpoch: binding.contextEpoch, hostSessionDigest, requireSource: true + envelope: semanticEnvelope, contextEpoch: binding.contextEpoch, hostSessionDigest, requireSource: true }) if (semanticDecision && !semanticValidation.valid) { return { status: 'invalid', reasonCode: 'lifecycle-semantic-source-invalid', errors: semanticValidation.errors } diff --git a/scripts/test-grok-bot-host.js b/scripts/test-grok-bot-host.js index c345f4db..73192a88 100644 --- a/scripts/test-grok-bot-host.js +++ b/scripts/test-grok-bot-host.js @@ -439,6 +439,40 @@ async function concurrentSessionsE2E () { assert.ok(!own.result.isError, JSON.stringify(own.body).slice(0, 800)) assert.strictEqual(readState(b).taskRecoveryBinding?.taskId, admitB.body.taskId, 'closing A leaves B bound') console.log(' ✓ task recovery / terminal close do not cross sessions') + // Regression: resume an older still-active task with a semanticDecision in a session that has + // no bound task. After profile_load the resume stub keeps only the envelope identity + // ('minimal-identity-only'); admission must validate the semantic source against the exact full + // envelope from the admission ingress snapshot instead of failing with + // FINALIZED_TASK_RESUME_SESSION_MISMATCH. + { + const promptT2 = '修复会话B的第二个问题' + const beginT2 = begin(b, promptT2) + const admitT2 = await routeAndAdmit(b, beginT2, await plan(b, beginT2), promptT2) + assert.ok(!admitT2.result.isError, JSON.stringify(admitT2.body).slice(0, 600)) + await new Promise(resolve => setTimeout(resolve, 1100)) + const closeT2 = await call(b, 'devcodex-memory', 'memory_turn_closeout_v1', closeArgs(admitT2.body.taskId, admitT2.body.taskRootRelative)) + assert.ok(!closeT2.result.isError, JSON.stringify(closeT2.body).slice(0, 600)) + const resumePrompt = `继续 ${admitB.body.taskRootRelative} 任务` + const beginR = begin(b, resumePrompt) + const planR = await call(b, 'devcodex-profile', 'profile_context_plan', { + project, intent: 'resume', changeTypes: ['docs'], contextEpoch: beginR.contextEpoch, confidence: 0.9, + semanticDecision: { schemaVersion: 'IntentSemanticDecisionV1', sourceRef: beginR.semanticDecisionSourceRef, workflowFacts: { targetKnown: true }, executionDecision: 'confirm' } + }) + assert.strictEqual(planR.post.routeStatus, 'selected', JSON.stringify(planR.body).slice(0, 300)) + const bindingR = planR.body.contextBinding + await call(b, 'devcodex-profile', 'profile_load', { project, contextBinding: bindingR, files: planR.body.profile.selectedFiles }) + for (const q of planR.body.memory?.requiredQueries || []) await call(b, 'devcodex-memory', q, { project, contextBinding: bindingR }) + const stub = readState(b) + assert.strictEqual(stub.recoveryCompaction, 'minimal-identity-only', `reproduces the identity-only resume stub: ${stub.recoveryKind}/${stub.recoveryCompaction}`) + assert.strictEqual(stub.actualInstructionEnvelope.instructionAuthority, undefined) + const bind = await call(b, 'devcodex-memory', 'memory_task_admit_v2', { + operation: 'bind', project, resumeContextBinding: bindingR, + task: { taskId: admitB.body.taskId, taskKind: 'bugs', entryVariant: 'continue', taskRootRelative: admitB.body.taskRootRelative }, + overview: { content: resumePrompt } + }) + assert.ok(!bind.result.isError, JSON.stringify(bind.body).slice(0, 600)) + console.log(' ✓ resume bind with semanticDecision validates against the full snapshot envelope') + } // Strict identity: invalid hostSession is rejected; driver refuses a bracket/args session mismatch. const bad = await servers['devcodex-memory'].call('memory_status', { project, hostSession: '../a' }) assert.strictEqual(bad.isError, true) From 95e66f48fc88b4701167abbb407cca14b799288e Mon Sep 17 00:00:00 2001 From: rocky Date: Sat, 24 Oct 2026 00:04:17 +0800 Subject: [PATCH 2/2] governance(C22): require cleanup of AI-created temporary verification artifacts (TemporaryArtifactCleanup, PI-001) --- changelogs/unreleased.md | 4 ++++ content/duplication-inventory.json | 2 +- content/instructions.md | 2 +- content/instructions/01-common.instructions.md | 2 +- hooks/_runtime/workflow-root-registry.v1.json | 4 ++-- hooks/_runtime/workflow-root-registry.v2.json | 6 +++--- host-projections/AGENTS.md | 4 ++-- host-projections/copilot-instructions.md | 4 ++-- host-projections/coverage.json | 14 +++++++------- scripts/lib/validate-governance-quality.js | 4 ++-- 10 files changed, 25 insertions(+), 21 deletions(-) diff --git a/changelogs/unreleased.md b/changelogs/unreleased.md index 746f1e00..1d527922 100644 --- a/changelogs/unreleased.md +++ b/changelogs/unreleased.md @@ -11,6 +11,10 @@ TaskLifecycle 规范能力吸纳、源码操作状态恢复及本次入口/CP/ 修复和边界见 [v1.20.5候选说明](releases/v1.20.5.md)。当前仅准备本地候选,不表示已发布或最终资格通过。下方既有历史条目保留,不批量移入新版本。 +## 治理:临时验证产物清理(C22 扩展,PI-001) + +- C22 增补 TemporaryArtifactCleanup:收尾前删除 AI 为测试/验证自建的临时文件与状态并在报告列出;用户要求保留时记录原因与位置;不得删除用户拥有或已提交的文件。源为 `content/instructions.md` 与 `content/instructions/01-common.instructions.md`,投影已再生成,validate-governance-quality 增加锚点。 + ## Grok Bot 宿主驱动(grok-bot) - 续接修复:续接旧任务时 `profile_context_plan` 携带 semanticDecision,`profile_load` 后续接存根因超出 8 KiB 被压缩为仅含身份的指令信封(`minimal-identity-only`),准入用该 5 字段存根校验语义来源而报 `FINALIZED_TASK_RESUME_SESSION_MISMATCH`。现改为从内容寻址、摘要校验的准入 ingress 快照取回身份完全一致的完整信封再做原有全部校验(不放宽);新增复现回归测试。 diff --git a/content/duplication-inventory.json b/content/duplication-inventory.json index 1d53c9cf..7e9b07dd 100644 --- a/content/duplication-inventory.json +++ b/content/duplication-inventory.json @@ -1,6 +1,6 @@ { "schemaVersion": "ControlContentDuplicationInventoryV1", - "sourceBundleDigest": "034cda1c943f913bfcb24cc58203d4256e35e0d0e797193206d3cfde6f2bb785", + "sourceBundleDigest": "aa050f84949ce439e2e10eee0474bea4d9364a7eba35acf412be8aba6e4d80fc", "thresholds": { "minParagraphChars": 100, "sectionThreshold": 0.94, diff --git a/content/instructions.md b/content/instructions.md index 422675e4..82b6f2b9 100644 --- a/content/instructions.md +++ b/content/instructions.md @@ -68,7 +68,7 @@ | C19 | 确认后前置复审 | 每次显式或 Auto CP 决定后、进入下一阶段前,必须执行 `PostConfirmationReviewScopeGate` 并输出 **ReviewGradeCard**:映射 **轻量=R1**(低风险单文件/纯文案,须 `skipReason`)、**标准=R2**(默认)、**全面=R3**(高风险/多模块/公共 API·配置/安全/package·adapter/文档消费者/控制面/多真相源 + 冻结清单)、**发布安全=R4**;命中控制面 / 多文件联动 / 真相源同步 / 模板-示例-校验链须追加交叉验证。阻断项先修正并回到结构化意图重算;不得把复审失败、receipt 过期或同任务范围变化自行投影成重复确认。ECR 默认 R2,不得钉死为「永远轻量」 | | C20 | 官方文档证据前置 | 新增/升级第三方依赖、框架、SDK、平台 API 或外部模块前,必须先读取官方使用文档/官方参考资料并形成 `OfficialDocsEvidence`;缺失证据时不得进入编码 | | C21 | Profile 联动判定 | dev/fix 修改项目技术栈、目录边界、脚本、测试/发布路线、分发面、配置项、长期连接或本地 overlay schema 时,必须执行 `ProfileImpactCheck`:更新 Profile 或写明跳过理由 | -| C22 | AI 自启动服务清理(ServiceLifecycleCleanup) | AI 为验证启动 dev server、文档站、本地 API/mock、数据库代理、SSH 隧道、Playwright/Cypress server、压测 target 等长运行进程时,必须记录启动命令、cwd、PID/job、端口/URL;验证完成、失败或中断收尾前主动停止仅由 AI 启动的服务并核验端口释放;不得杀用户既有进程;用户明确要求保留时记录保留原因、PID/端口和关闭方式 | +| C22 | AI 自启动服务清理(ServiceLifecycleCleanup) | AI 为验证启动 dev server、文档站、本地 API/mock、数据库代理、SSH 隧道、Playwright/Cypress server、压测 target 等长运行进程时,必须记录启动命令、cwd、PID/job、端口/URL;验证完成、失败或中断收尾前主动停止仅由 AI 启动的服务并核验端口释放;不得杀用户既有进程;用户明确要求保留时记录保留原因、PID/端口和关闭方式;临时验证产物清理(TemporaryArtifactCleanup):收尾前删除 AI 为测试/验证自建的临时文件与状态(草稿脚本、探针、参数/响应临时文件、临时 worktree/stash、测试会话目录等),并在报告中列出已删除项;用户明确要求保留时记录保留原因与位置;不得删除用户拥有或已提交的文件 | ### ConcurrencyPolicy(C07) diff --git a/content/instructions/01-common.instructions.md b/content/instructions/01-common.instructions.md index 1426b02a..146dcf19 100644 --- a/content/instructions/01-common.instructions.md +++ b/content/instructions/01-common.instructions.md @@ -74,7 +74,7 @@ version: 1.20.7 | C19 | 确认后前置复审 | 每次显式或 Auto CP 决定后、进入下一阶段前,必须执行 `PostConfirmationReviewScopeGate` 并输出 **ReviewGradeCard**:映射 **轻量=R1**(须 `skipReason`)、**标准=R2**(默认)、**全面=R3**(高风险+冻结清单)、**发布安全=R4**;命中控制面 / 多文件联动 / 真相源同步 / 模板-示例-校验链必须追加交叉验证;阻断项先修正并回到结构化意图重算,不得由复审器直接要求重复确认。ECR 默认 R2,禁止「永远轻量」口径 | | C20 | 官方文档证据前置 | 新增/升级依赖、框架、SDK、平台 API 或外部模块前必须形成 `OfficialDocsEvidence`;缺失证据不得进入编码 | | C21 | Profile 联动判定 | dev/fix 项目事实变化后必须执行 `ProfileImpactCheck`:更新 Profile 或写明跳过理由 | -| C22 | AI 自启动服务清理(ServiceLifecycleCleanup) | AI 为验证启动 dev server、文档站、本地 API/mock、数据库代理、SSH 隧道、Playwright/Cypress server、压测 target 等长运行进程时,必须记录启动命令、cwd、PID/job、端口/URL;验证完成、失败或中断收尾前主动停止仅由 AI 启动的服务并核验端口释放;不得杀用户既有进程;用户明确要求保留时记录保留原因、PID/端口和关闭方式 | +| C22 | AI 自启动服务清理(ServiceLifecycleCleanup) | AI 为验证启动 dev server、文档站、本地 API/mock、数据库代理、SSH 隧道、Playwright/Cypress server、压测 target 等长运行进程时,必须记录启动命令、cwd、PID/job、端口/URL;验证完成、失败或中断收尾前主动停止仅由 AI 启动的服务并核验端口释放;不得杀用户既有进程;用户明确要求保留时记录保留原因、PID/端口和关闭方式;临时验证产物清理(TemporaryArtifactCleanup):收尾前删除 AI 为测试/验证自建的临时文件与状态(草稿脚本、探针、参数/响应临时文件、临时 worktree/stash、测试会话目录等),并在报告中列出已删除项;用户明确要求保留时记录保留原因与位置;不得删除用户拥有或已提交的文件 | ### ConcurrencyPolicy(C07) diff --git a/hooks/_runtime/workflow-root-registry.v1.json b/hooks/_runtime/workflow-root-registry.v1.json index dde0e2d9..743ba6e9 100644 --- a/hooks/_runtime/workflow-root-registry.v1.json +++ b/hooks/_runtime/workflow-root-registry.v1.json @@ -3,7 +3,7 @@ "sourceEvidence": [ { "ref": "content:instructions/01-common.instructions.md", - "digest": "931fd0f2541a91c024535acbf268055ad8ddeb46be6df5989f2a30dd4fe4cbc5" + "digest": "a612e188c1e4b54acc5162262360bdb09248cbf28ca59fd5f22eaf166fa396b6" }, { "ref": "content:skills/routing/SKILL.md", @@ -14,7 +14,7 @@ "digest": "e3e64094eaed8fd55432d67befc5135ab321c21d59b1f679b65982f8bf585381" } ], - "sourceDigest": "920582706c85065731db1bb2f648f09613531b323a48d2089174cfd6e06a7681", + "sourceDigest": "cc4aedfb3dc3d3d223b57a5ad3c2e20e870499e2fe76fba9ad93e79e8ed8a44f", "baseBundles": { "chat": [], "resume": [ diff --git a/hooks/_runtime/workflow-root-registry.v2.json b/hooks/_runtime/workflow-root-registry.v2.json index f126eee8..e028ff2b 100644 --- a/hooks/_runtime/workflow-root-registry.v2.json +++ b/hooks/_runtime/workflow-root-registry.v2.json @@ -1100,7 +1100,7 @@ "sourceEvidence": [ { "ref": "content:instructions/01-common.instructions.md", - "digest": "931fd0f2541a91c024535acbf268055ad8ddeb46be6df5989f2a30dd4fe4cbc5" + "digest": "a612e188c1e4b54acc5162262360bdb09248cbf28ca59fd5f22eaf166fa396b6" }, { "ref": "content:skills/routing/SKILL.md", @@ -1115,7 +1115,7 @@ "digest": "e3e64094eaed8fd55432d67befc5135ab321c21d59b1f679b65982f8bf585381" } ], - "sourceDigest": "dbe8bd852580b7b56d776dfd876786660077b97c47728e7f4cf521565bfa2cbb", + "sourceDigest": "2d799a5e86cbc0828a7f1933ebb26ae786b0e2da0ecb71cf45336b8e2a106e4d", "routeRevision": "82670f3bca8316fcdb62984b9360d40d32a1f7f8671a1be3ddc7f268ff7547c5", - "registryDigest": "3961a16107dfe92e0c46337a4ff5aa1e0ea3ff554f225d09fe378146e44ed329" + "registryDigest": "a210b4611e8bc8aa21aaf80d71309de802c0d3933b957310f6a0825306e0e0fc" } diff --git a/host-projections/AGENTS.md b/host-projections/AGENTS.md index b97d112a..8d97583e 100644 --- a/host-projections/AGENTS.md +++ b/host-projections/AGENTS.md @@ -1,7 +1,7 @@ # DevCodex — Shared Host Kernel(generated) > Generated from the unified DevCodex instructions source. Do not edit this file directly. -> sourceDigest: 87ca32b07dd994b3b7047f290b55b494862b8ce8c30623b6e365efa99a11a110 +> sourceDigest: e82b40b4c770244ef04b0f676186acd44b164619726efb47898e3772dca42937 > configDigest: 9a6dafe5365c4d43a9a1255e1b5cffa83b7f9b8144b060cb205a8d98beab9457 > Full fallback: user://agents/devcodex/instructions.full.md > Host surface: Codex / Claude import / Gemini import / Grok native AGENTS.md @@ -36,7 +36,7 @@ - C19:确认后前置复审:每次显式或 Auto CP 决定后、进入下一阶段前,必须执行 `PostConfirmationReviewScopeGate` 并输出 ReviewGradeCard:映射 轻量=R1(低风险单文件/纯文案,须 `skipReason`)、标准=R2(默认)、全面=R3(高风险/多模块/公共 API·配置/安全/package·adapter/文档消费者/控制面/多真相源 + 冻结清单)、发布安全=R4;命中控制面 / 多文件联动 / 真相源同步 / 模板-示例-校验链须追加交叉验证。阻断项先修正并回到结构化意图重算;不得把复审失败、receipt 过期或同任务范围变化自行投影成重复确认。ECR 默认 R2,不得钉死为「永远轻量」 - C20:官方文档证据前置:新增/升级第三方依赖、框架、SDK、平台 API 或外部模块前,必须先读取官方使用文档/官方参考资料并形成 `OfficialDocsEvidence`;缺失证据时不得进入编码 - C21:Profile 联动判定:dev/fix 修改项目技术栈、目录边界、脚本、测试/发布路线、分发面、配置项、长期连接或本地 overlay schema 时,必须执行 `ProfileImpactCheck`:更新 Profile 或写明跳过理由 -- C22:AI 自启动服务清理(ServiceLifecycleCleanup):AI 为验证启动 dev server、文档站、本地 API/mock、数据库代理、SSH 隧道、Playwright/Cypress server、压测 target 等长运行进程时,必须记录启动命令、cwd、PID/job、端口/URL;验证完成、失败或中断收尾前主动停止仅由 AI 启动的服务并核验端口释放;不得杀用户既有进程;用户明确要求保留时记录保留原因、PID/端口和关闭方式 +- C22:AI 自启动服务清理(ServiceLifecycleCleanup):AI 为验证启动 dev server、文档站、本地 API/mock、数据库代理、SSH 隧道、Playwright/Cypress server、压测 target 等长运行进程时,必须记录启动命令、cwd、PID/job、端口/URL;验证完成、失败或中断收尾前主动停止仅由 AI 启动的服务并核验端口释放;不得杀用户既有进程;用户明确要求保留时记录保留原因、PID/端口和关闭方式;临时验证产物清理(TemporaryArtifactCleanup):收尾前删除 AI 为测试/验证自建的临时文件与状态(草稿脚本、探针、参数/响应临时文件、临时 worktree/stash、测试会话目录等),并在报告中列出已删除项;用户明确要求保留时记录保留原因与位置;不得删除用户拥有或已提交的文件 ## 意图与路由 diff --git a/host-projections/copilot-instructions.md b/host-projections/copilot-instructions.md index f8941171..97c64d49 100644 --- a/host-projections/copilot-instructions.md +++ b/host-projections/copilot-instructions.md @@ -1,7 +1,7 @@ # DevCodex — Copilot Host Kernel(generated) > Generated from the unified DevCodex instructions source. Do not edit this file directly. -> sourceDigest: 87ca32b07dd994b3b7047f290b55b494862b8ce8c30623b6e365efa99a11a110 +> sourceDigest: e82b40b4c770244ef04b0f676186acd44b164619726efb47898e3772dca42937 > configDigest: 9a6dafe5365c4d43a9a1255e1b5cffa83b7f9b8144b060cb205a8d98beab9457 > Full fallback: user://agents/devcodex/instructions.full.md > Host surface: GitHub Copilot CLI user-global instructions; Hooks/MCP/Skills are deployed separately @@ -36,7 +36,7 @@ - C19:确认后前置复审:每次显式或 Auto CP 决定后、进入下一阶段前,必须执行 `PostConfirmationReviewScopeGate` 并输出 ReviewGradeCard:映射 轻量=R1(低风险单文件/纯文案,须 `skipReason`)、标准=R2(默认)、全面=R3(高风险/多模块/公共 API·配置/安全/package·adapter/文档消费者/控制面/多真相源 + 冻结清单)、发布安全=R4;命中控制面 / 多文件联动 / 真相源同步 / 模板-示例-校验链须追加交叉验证。阻断项先修正并回到结构化意图重算;不得把复审失败、receipt 过期或同任务范围变化自行投影成重复确认。ECR 默认 R2,不得钉死为「永远轻量」 - C20:官方文档证据前置:新增/升级第三方依赖、框架、SDK、平台 API 或外部模块前,必须先读取官方使用文档/官方参考资料并形成 `OfficialDocsEvidence`;缺失证据时不得进入编码 - C21:Profile 联动判定:dev/fix 修改项目技术栈、目录边界、脚本、测试/发布路线、分发面、配置项、长期连接或本地 overlay schema 时,必须执行 `ProfileImpactCheck`:更新 Profile 或写明跳过理由 -- C22:AI 自启动服务清理(ServiceLifecycleCleanup):AI 为验证启动 dev server、文档站、本地 API/mock、数据库代理、SSH 隧道、Playwright/Cypress server、压测 target 等长运行进程时,必须记录启动命令、cwd、PID/job、端口/URL;验证完成、失败或中断收尾前主动停止仅由 AI 启动的服务并核验端口释放;不得杀用户既有进程;用户明确要求保留时记录保留原因、PID/端口和关闭方式 +- C22:AI 自启动服务清理(ServiceLifecycleCleanup):AI 为验证启动 dev server、文档站、本地 API/mock、数据库代理、SSH 隧道、Playwright/Cypress server、压测 target 等长运行进程时,必须记录启动命令、cwd、PID/job、端口/URL;验证完成、失败或中断收尾前主动停止仅由 AI 启动的服务并核验端口释放;不得杀用户既有进程;用户明确要求保留时记录保留原因、PID/端口和关闭方式;临时验证产物清理(TemporaryArtifactCleanup):收尾前删除 AI 为测试/验证自建的临时文件与状态(草稿脚本、探针、参数/响应临时文件、临时 worktree/stash、测试会话目录等),并在报告中列出已删除项;用户明确要求保留时记录保留原因与位置;不得删除用户拥有或已提交的文件 ## 意图与路由 diff --git a/host-projections/coverage.json b/host-projections/coverage.json index 99dae4ba..5dff7ad1 100644 --- a/host-projections/coverage.json +++ b/host-projections/coverage.json @@ -1,12 +1,12 @@ { "schemaVersion": "HostInstructionCoverageReceiptV1", "sourceFile": "content/instructions.md", - "sourceDigest": "87ca32b07dd994b3b7047f290b55b494862b8ce8c30623b6e365efa99a11a110", + "sourceDigest": "e82b40b4c770244ef04b0f676186acd44b164619726efb47898e3772dca42937", "configDigest": "9a6dafe5365c4d43a9a1255e1b5cffa83b7f9b8144b060cb205a8d98beab9457", "mode": "kernel", "fallback": { "destination": "user://agents/devcodex/instructions.full.md", - "sourceDigest": "87ca32b07dd994b3b7047f290b55b494862b8ce8c30623b6e365efa99a11a110", + "sourceDigest": "e82b40b4c770244ef04b0f676186acd44b164619726efb47898e3772dca42937", "available": true }, "budgets": { @@ -305,14 +305,14 @@ }, "outputs": { "host-projections/AGENTS.md": { - "bytes": 11913, + "bytes": 12282, "lines": 91, - "digest": "89e8ed0ef9da9630436fbcc1ce330b51549b276d5963484c4f60c31d8e135a55" + "digest": "5b4597cb14ca62a9f4c8bd9089cf8d616bea123a854bec07208fe5e69b1c3bdd" }, "host-projections/copilot-instructions.md": { - "bytes": 11938, + "bytes": 12307, "lines": 91, - "digest": "81dcdbc33a5816fc54af1418970083a7536ba8208e8b4672906b89f229aa92ca" + "digest": "7a4c84f8009e490b85799647a5a4aea07eab242c3a4c81a988880088c10c9edf" }, "host-projections/CLAUDE.md": { "bytes": 520, @@ -330,5 +330,5 @@ "valid": true, "errors": [] }, - "receiptId": "host-instruction-coverage-9b40100bbd8828617bed9ca11a601628d92618a7a4c8b059aa7d8bbe174fe7a6" + "receiptId": "host-instruction-coverage-0531b9db1555512d9be65c52daccf3904e637faf37e056ded77a27bc40573754" } diff --git a/scripts/lib/validate-governance-quality.js b/scripts/lib/validate-governance-quality.js index cdfe3b16..6fa27347 100644 --- a/scripts/lib/validate-governance-quality.js +++ b/scripts/lib/validate-governance-quality.js @@ -14,8 +14,8 @@ function buildGovernanceQualityChecks(ctx) { function checkV55() { const probes = [ - { file: 'instructions.md', needles: ['C22', 'ServiceLifecycleCleanup', 'AI 自启动服务清理', '不得杀用户既有进程'] }, - { file: 'instructions/01-common.instructions.md', needles: ['C22', 'AI 自启动服务清理', 'ServiceLifecycleCleanup'] }, + { file: 'instructions.md', needles: ['C22', 'ServiceLifecycleCleanup', 'AI 自启动服务清理', '不得杀用户既有进程', 'TemporaryArtifactCleanup', '不得删除用户拥有或已提交的文件'] }, + { file: 'instructions/01-common.instructions.md', needles: ['C22', 'AI 自启动服务清理', 'ServiceLifecycleCleanup', 'TemporaryArtifactCleanup'] }, { file: 'instructions/10-dev.instructions.md', needles: ['AI 自启动服务清理', '端口释放'] }, { file: 'instructions/11-fix.instructions.md', needles: ['AI 自启动服务清理', '端口释放'] }, { file: 'skills/test-router/SKILL.md', needles: ['ServiceLifecycleCleanup', 'cleanupEvidence', '不得杀用户既有进程'] },