Skip to content

Commit 5bda28d

Browse files
committed
feat(lifecycle): wire dialogue-primary closeout into Stop evidence
Surface dialogueNarrativeStatus and analysisDeliveryStatus from Stop/PreCompact when assistant text is observed, remind on missing narrative or link-only-thin analysis delivery, and keep unverified when payload is absent.
1 parent 28c5aa0 commit 5bda28d

5 files changed

Lines changed: 115 additions & 3 deletions

File tree

changelogs/unreleased.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77

88
- **GlobalOnly 全局 adapter 本地刷新 CLI(R1a)**:新增 `devcodex global-adapters apply [--dry-run] [--json]``npm run global-adapters:apply*`,从当前包根复用 `applyGlobalHostConfig` 刷新用户级五宿主 adapter(不 pack/不 publish)。nextStep/PC5/audit/V8/doctor/help/website intro 按源码 vs 已发布分流;次选 R1b `npm install -g .`、R2 pack+tarball、R3 `npm update -g`;bare `devcodex update` 仅 workspace。`package.json` files 已纳入 `global-adapters-cli` / guidance 模块,避免 pack 后 require 断裂。**Profile 01/02/04/05/06/07 已同步 R1a~R4**(PI-173)。任务:`bugs/GlobalOnly刷全局副本命令口径漂移修复`
99
- **R1a 复审收口**`host-parity-scorecard` repairSteps / recommendedEntry / userVisibleSummary 接入 `describeGlobalAdapterRefresh*`(源码 → `global-adapters apply`,已发布 → `npm -g`);`global-adapters apply` 在主机事务已提交但 Grok 集成失败时返回 `GLOBAL_ADAPTERS_HOSTS_COMMITTED_GROK_FAILED` + `partialState`;npm pack `files` 不再包含 `test-global-adapters-cli.js`
10-
- **Dialogue-Primary Closeout B1**:新增 `classifyDialogueNarrativeSample` / `hasReadableNarrativeSnippet`(完成态/分析收敛须对话内可读叙事)`classifyAnalysisArtifactDeliverySample` 对「仅报告链接」返回 `link-only-thin`同步 user-visible-output-contract、report、13-analyze、report-analysis promptV102 锚点与单测。lifecycle 接线留 B2。需求:`完成态对话内可读收口` CP2。
10+
- **Dialogue-Primary Closeout B1+B2**:新增 `classifyDialogueNarrativeSample` / `hasReadableNarrativeSnippet``classifyAnalysisArtifactDeliverySample` 返回 `link-only-thin``lifecycle-visible-reply` Stop/PreCompact 有正文时写入 `dialogueNarrativeStatus`/`analysisDeliveryStatus` 并在 dev+reportTouched 时 reminder;无正文仍 unverified。同步 Skill/13-analyze/16-report/prompt/V102/单测。需求:`完成态对话内可读收口` CP2。
1111
- **文档站依赖审计加固**:Rspress 提升到 2.0.18,`brace-expansion` 提升到 5.0.8;`test:audit` 改为 fail-closed 结构化审计。当前唯一例外为仅影响 unstable RSC 的 `GHSA-qwww-vcr4-c8h2`,本站静态运行面禁止 RSC 标记,例外于 2026-08-15 到期;任何新 advisory、依赖链变化、RSC 使用或到期都会重新阻断。
1212
- **Grok 兼容导入去重**:Grok 原生会话兼容加载 `~/.claude/settings.json` 时,DevCodex Claude adapter 识别 Grok 的 camelCase payload 与 snake_case 事件并安全跳过重复 lifecycle;Grok 专用 plugin 保持唯一生命周期 owner,消除 `user_prompt_submit` / `stop` hook 报错和双重状态写入。
1313
- **五宿主全局 adapter 运行态修复**`status`/`doctor` 从 receipt/files 假就绪升级为 `configured/adapterReady/contractStatus/nativeStatus/operationalState/ready/issues` 分层;五个可执行 adapter 均增加有界契约探针,只有原生 CLI 深探针通过才可标记 operational ready。doctor 增加原生版本与 Grok list/inspect、installed Hook contract、MCP initialize。Grok plugin source 改为用户级 runtime + cwd workspace 发现,同名受管 identity 经官方 CLI drain-all 后收敛到唯一 canonical;未知来源 mutation 前阻断,首次安装与多 identity 失败均保留/恢复原注册状态,受管恢复来源可继续收敛。Copilot CLI 按官方用户级目录部署 instructions、Hooks、MCP 与 Skills;Copilot IDE 仍保持独立 instruction-fallback ceiling。

hooks/_runtime/lifecycle-visible-reply.cjs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -267,6 +267,10 @@ function buildLifecycleVisibleReplyUtils(ctx) {
267267
state.visible.artifactMissingItems = ['visible-payload-unobserved', 'semantic-artifact-items']
268268
state.visible.finalValidationSummaryStatus = 'unverified'
269269
state.visible.finalValidationSummaryMissingItems = ['visible-payload-unobserved', 'final-validation-summary']
270+
state.visible.dialogueNarrativeStatus = 'unverified'
271+
state.visible.dialogueNarrativeMissingItems = ['visible-payload-unobserved', 'dialogue-narrative']
272+
state.visible.analysisDeliveryStatus = 'unverified'
273+
state.visible.analysisDeliveryMissingItems = ['visible-payload-unobserved', 'analysis-delivery']
270274
state.visible.stopProbe = {
271275
schemaVersion: 'StopPayloadProbeV1',
272276
observed: false,
@@ -319,6 +323,52 @@ function buildLifecycleVisibleReplyUtils(ctx) {
319323
state.visible.finalValidationSummaryStatus = 'unverified'
320324
state.visible.finalValidationSummaryMissingItems = ['classifier-unavailable', 'final-validation-summary']
321325
}
326+
// DPC B2: dialogue-primary narrative + analysis delivery (link-only-thin) when payload observed
327+
try {
328+
const { analyzeDialogueNarrativeSample } = require('./visible-output-contract.cjs')
329+
const narrativeEvidence = analyzeDialogueNarrativeSample(text)
330+
state.visible.dialogueNarrative = narrativeEvidence
331+
if (narrativeEvidence.classification === 'not-claimed') {
332+
state.visible.dialogueNarrativeStatus = 'not-claimed'
333+
state.visible.dialogueNarrativeMissingItems = []
334+
} else if (narrativeEvidence.classification === 'narrative-missing') {
335+
state.visible.dialogueNarrativeStatus = 'verified-missing'
336+
state.visible.dialogueNarrativeMissingItems = ['dialogue-narrative']
337+
} else {
338+
// present | waived
339+
state.visible.dialogueNarrativeStatus = 'verified-present'
340+
state.visible.dialogueNarrativeMissingItems = []
341+
}
342+
} catch {
343+
state.visible.dialogueNarrativeStatus = 'unverified'
344+
state.visible.dialogueNarrativeMissingItems = ['classifier-unavailable', 'dialogue-narrative']
345+
}
346+
try {
347+
const {
348+
classifyAnalysisArtifactDeliverySample
349+
} = require('../../scripts/lib/optimization-backlog-evidence.js')
350+
const analysisClass = classifyAnalysisArtifactDeliverySample(text)
351+
state.visible.analysisDeliveryClass = analysisClass
352+
if (analysisClass === 'not-analysis-delivery') {
353+
state.visible.analysisDeliveryStatus = 'not-claimed'
354+
state.visible.analysisDeliveryMissingItems = []
355+
} else if (analysisClass === 'ready') {
356+
state.visible.analysisDeliveryStatus = 'verified-present'
357+
state.visible.analysisDeliveryMissingItems = []
358+
} else if (analysisClass === 'link-only-thin') {
359+
state.visible.analysisDeliveryStatus = 'verified-missing'
360+
state.visible.analysisDeliveryMissingItems = ['analysis-link-only-thin']
361+
} else if (analysisClass === 'chat-only') {
362+
state.visible.analysisDeliveryStatus = 'verified-missing'
363+
state.visible.analysisDeliveryMissingItems = ['analysis-chat-only']
364+
} else {
365+
state.visible.analysisDeliveryStatus = 'verified-missing'
366+
state.visible.analysisDeliveryMissingItems = [`analysis-delivery:${analysisClass}`]
367+
}
368+
} catch {
369+
state.visible.analysisDeliveryStatus = 'unverified'
370+
state.visible.analysisDeliveryMissingItems = ['classifier-unavailable', 'analysis-delivery']
371+
}
322372
const artifactEvidence = analyzeArtifactDelivery(text)
323373
state.visible.artifactStatus = artifactEvidence.status
324374
state.visible.artifactMissingItems = artifactEvidence.missingItems
@@ -407,6 +457,19 @@ function buildLifecycleVisibleReplyUtils(ctx) {
407457
items.push(`无法验证最终验证摘要(DevModeCompletionCheckDetailGate:status=unverified;missingItems=${missing})`)
408458
}
409459
}
460+
// DPC B2: dialogue narrative + analysis delivery closeout reminders (payload observed only)
461+
if (eventName === 'Stop' && state.mode === 'dev' && state.reportTouched) {
462+
const narrativeStatus = state.visible?.dialogueNarrativeStatus
463+
if (narrativeStatus === 'verified-missing') {
464+
const missing = (state.visible.dialogueNarrativeMissingItems || []).join(', ') || 'dialogue-narrative'
465+
items.push(`对话内可读收口不完整(Dialogue-Primary:missingItems=${missing};须结果句+要点,禁止仅详见报告)`)
466+
}
467+
const analysisStatus = state.visible?.analysisDeliveryStatus
468+
if (analysisStatus === 'verified-missing') {
469+
const missing = (state.visible.analysisDeliveryMissingItems || []).join(', ') || 'analysis-delivery'
470+
items.push(`分析交付不完整(AnalysisArtifactDelivery:missingItems=${missing};class=${state.visible.analysisDeliveryClass || 'unknown'})`)
471+
}
472+
}
410473
const artifactStatus = state.visible?.artifactStatus || (state.visible?.artifactPaths ? 'verified-present' : 'unverified')
411474
if (eventName === 'Stop' && state.mode === 'dev' && state.reportTouched && artifactStatus === 'verified-missing') {
412475
const missing = (state.visible.artifactMissingItems || []).join(', ') || 'artifact-delivery-manifest'

scripts/lib/validate-visible-output-controls.js

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,14 @@ function buildVisibleOutputControlChecks(ctx) {
3333
requireAnchors('skills/user-visible-output-contract/SKILL.md', ['ArtifactAnchorProjectionV1', 'ArtifactAnchorProjectionGate', 'FinalValidationSummaryGate', 'classifyFinalValidationSummarySample', 'classifyDialogueNarrativeSample', 'Dialogue-Primary'])
3434
requireAnchors('prompts/precheck-status.prompt.md', ['DevCodexVisibleEnvelopeV1', 'PC0~PC7'])
3535
requireAnchors('hooks/_runtime/visible-output-contract.cjs', ['ArtifactAnchorV1', 'projectArtifactAnchorsFromManifest', 'analyzeFinalValidationSummarySample', 'classifyFinalValidationSummarySample', 'classifyDialogueNarrativeSample', 'hasReadableNarrativeSnippet'])
36-
requireAnchors('hooks/_runtime/lifecycle-visible-reply.cjs', ['finalValidationSummaryStatus', 'DevModeCompletionCheckDetailGate'])
36+
requireAnchors('hooks/_runtime/lifecycle-visible-reply.cjs', [
37+
'finalValidationSummaryStatus',
38+
'DevModeCompletionCheckDetailGate',
39+
'dialogueNarrativeStatus',
40+
'analysisDeliveryStatus',
41+
'analysis-link-only-thin',
42+
'Dialogue-Primary'
43+
])
3744

3845
try {
3946
JSON.parse(read(path.join(ROOT, 'skills/user-visible-output-contract/visible-output-contract.schema.json')))

scripts/test-visible-output-contract.js

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,48 @@ assert.strictEqual(analyzeDialogueNarrativeSample('分析完成。').classificat
401401
const present = replyUtils.analyzeArtifactDelivery(withPath)
402402
assert.strictEqual(present.pathColumnClass, 'present')
403403
assert.ok(!present.missingItems.includes('missing-path-column'))
404+
405+
// DPC B2: lifecycle wires narrative + analysis delivery into visible state
406+
const { buildLifecyclePayloadUtils } = require('../hooks/_runtime/lifecycle-payload-utils.cjs')
407+
const payloadUtils = buildLifecyclePayloadUtils({ fs: require('fs') })
408+
const replyUtilsWired = buildLifecycleVisibleReplyUtils({
409+
fs: require('fs'),
410+
getVisibleReplyEvidence: payloadUtils.getVisibleReplyEvidence,
411+
collectInterestingStrings: payloadUtils.collectInterestingStrings,
412+
getStatePaths: () => ({ dir: '', finalPayloadFlag: '', finalPayloadLog: '' }),
413+
buildGovernanceIntakeReminderItem: () => ''
414+
})
415+
const state = {
416+
mode: 'dev',
417+
reportTouched: true,
418+
visible: {}
419+
}
420+
const narrativeMissingBody = [
421+
'### DevCodex · 完成检查',
422+
'详见报告:[r](reports/x.md)',
423+
'#### 完成交付文件',
424+
'- [报告](x.md) — 用途说明;路径:`.devcodex/x.md`;操作:深读时打开归档报告',
425+
'`DevCodexVisibleEnvelopeV1 · completion-check · PASS · ' + 'c'.repeat(64) + '`'
426+
].join('\n')
427+
replyUtilsWired.updateVisibleReplyState(state, { assistantMessage: narrativeMissingBody }, 'Stop')
428+
assert.strictEqual(state.visible.dialogueNarrativeStatus, 'verified-missing')
429+
assert.ok(state.visible.dialogueNarrativeMissingItems.includes('dialogue-narrative'))
430+
const reminder = replyUtilsWired.buildClosureReminder(
431+
{ mode: 'dev', reportTouched: true, visible: state.visible, mutated: false },
432+
'Stop'
433+
)
434+
assert.match(String(reminder || ''), /Dialogue-Primary|/)
435+
436+
const analysisThinState = { mode: 'dev', reportTouched: true, visible: {} }
437+
const analysisThinBody = '分析完成。[报告](./reports/analysis/grok/20260721/04--审阅.md)'
438+
replyUtilsWired.updateVisibleReplyState(
439+
analysisThinState,
440+
{ assistantMessage: analysisThinBody },
441+
'Stop'
442+
)
443+
assert.strictEqual(analysisThinState.visible.analysisDeliveryClass, 'link-only-thin')
444+
assert.strictEqual(analysisThinState.visible.analysisDeliveryStatus, 'verified-missing')
445+
assert.ok(analysisThinState.visible.analysisDeliveryMissingItems.includes('analysis-link-only-thin'))
404446
}
405447

406448
const failedEnvelope = createVisibleEnvelope({ ...baseInput, linkCapability: failed })

skills/user-visible-output-contract/SKILL.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,7 @@ Rich clickable 只显示一个语义 Markdown 链接作为名称主表示,**
149149

150150
负向:仅报告链接、仅「详见报告」、仅验证矩阵字段。
151151
正向:结果句 + 要点;analyze 须同时满足报告落盘(见 PF-169 / `link-only-thin`)。
152-
**B1** 提供 classifier + Skill/单测;**B2** 再接线 lifecycle 生产 missingItems(无正文宿主仍 unverified)。
152+
**B1** classifier + Skill/单测;**B2** `lifecycle-visible-reply` 在 Stop/PreCompact **有正文时**写入 `dialogueNarrativeStatus` / `analysisDeliveryStatus`,dev+reportTouched 时进入 closure reminder;无正文仍 `unverified`(Grok 诚实上限)。
153153

154154
默认 userAction 写「深读时打开归档报告」;**禁止**「请用 Typora/浏览器打开预览」作为默认动作。
155155

0 commit comments

Comments
 (0)