Skip to content

Commit 950a93d

Browse files
committed
fix(hooks): converge Stop-gate soft path and mode=dev acceptance
Skip entry soft-spam on pure chat, store assistant text for F-08 soft exemptions, strengthen mode=dev probe to full precheck/FVS green, and ship stop-gate scripts in package files. global-adapters apply verified digest match.
1 parent 0e09d48 commit 950a93d

3 files changed

Lines changed: 41 additions & 13 deletions

File tree

‎hooks/_runtime/lifecycle-visible-reply.cjs‎

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,8 @@ function buildLifecycleVisibleReplyUtils(ctx) {
283283
}
284284
const text = evidence.text
285285
state.visible.payloadObserved = true
286+
// Keep bounded sample for soft reminders / F-08 exemption (Stop path)
287+
state.visible.assistantTextSample = String(text || '').slice(0, 12000)
286288
// Accept both legacy and portable envelope precheck markers (W8).
287289
// PF-087: presence alone is not enough — require free-text PC0~PC7 column completeness.
288290
const entryCompleteness = analyzeEntryCheckCompleteness(text, { mode: state.mode })
@@ -432,8 +434,13 @@ function buildLifecycleVisibleReplyUtils(ctx) {
432434

433435
function buildClosureReminder(state, eventName) {
434436
const items = []
437+
const modeL = String(state.mode || '').toLowerCase()
438+
const chatIdle = (modeL === 'chat' || String(state.workflow || '').toLowerCase() === 'chat') && !state.mutated
435439
const precheckStatus = getPrecheckEvidenceStatus(state)
436-
if (eventName === 'Stop' && precheckStatus === 'verified-missing') {
440+
// Pure chat without mutation: do not soft-spam entry-check (gate already allows).
441+
if (eventName === 'Stop' && chatIdle) {
442+
// still allow governance intake reminder below
443+
} else if (eventName === 'Stop' && precheckStatus === 'verified-missing') {
437444
const completeness = state.visible?.entryCheckCompleteness
438445
if (completeness && completeness.claimed && !completeness.complete) {
439446
const detail = [
@@ -493,8 +500,20 @@ function buildLifecycleVisibleReplyUtils(ctx) {
493500
const missing = (state.visible.artifactMissingItems || []).join(', ') || 'semantic-artifact-items, visible-payload-unobserved'
494501
items.push(`无法验证最终用户可见回复的产物交付(Stop/PreCompact 未提供可解析 assistant 内容;状态只能为 unverified;missingItems=${missing})`)
495502
}
496-
if (state.mutated && !state.memoryTouched) items.push('记忆文件尚未写入(S05:会话结束前必须写入)')
497-
if (state.mutated && !state.reportTouched) items.push('报告文件尚未写入(chat 工作流豁免)')
503+
// F-08: soft path mirrors stop-gate artifactGapsExempt when final text was observed
504+
let artifactExempt = false
505+
try {
506+
const { artifactGapsExempt } = require('./lifecycle-stop-gate.cjs')
507+
artifactExempt = artifactGapsExempt(String(state.visible?.assistantTextSample || ''))
508+
} catch {
509+
artifactExempt = false
510+
}
511+
if (state.mutated && !state.memoryTouched && !artifactExempt && !chatIdle) {
512+
items.push('记忆文件尚未写入(S05:会话结束前必须写入)')
513+
}
514+
if (state.mutated && !state.reportTouched && !artifactExempt && !chatIdle) {
515+
items.push('报告文件尚未写入(chat 工作流豁免;可用 报告:N/A + skipReason)')
516+
}
498517
if (eventName === 'Stop' && state.visible?.workflowCompletion && !state.visible.workflowCompletion.workflowComplete) {
499518
items.push(`workflow completion 未提交(phase=${state.visible.workflowCompletion.completionPhase}; firstBlocker=${state.visible.workflowCompletion.firstBlocker || 'none'})`)
500519
}

‎package.json‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,12 @@
225225
"scripts/test-skill-resolution-hook-paths.js",
226226
"scripts/test-workspace-skill-auto-match.js",
227227
"scripts/replay-skill-automatch-accept.cjs",
228+
"scripts/test-lifecycle-stop-gate.js",
229+
"scripts/test-r10-control-plane-honesty.js",
230+
"scripts/replay-stop-gate-hook-matrix.js",
231+
"scripts/probe-mode-dev-completion.js",
228232
"hooks/_runtime/workspace-skill-auto-match.cjs",
233+
"hooks/_runtime/lifecycle-stop-gate.cjs",
229234
"skills/_schemas/devcodex-skill-contract.v1.schema.json",
230235
"scripts/lib/project-knowledge-store.js",
231236
"scripts/lib/execution-optimization.js",

‎scripts/probe-mode-dev-completion.js‎

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -53,14 +53,14 @@ run({
5353

5454
const body = [
5555
'### DevCodex · 入口检查',
56-
'- PC0 [PASS]',
57-
'- PC1 [PASS]',
58-
'- PC2 [PASS]',
59-
'- PC3 [PASS]',
60-
'- PC4 [N/A] skipReason=probe',
61-
'- PC5 [PASS]',
62-
'- PC6 [PASS]',
63-
'- PC7 [PASS]',
56+
'- PC0 [PASS] ContextReadPlan + receipt(probe)',
57+
'- PC1 [PASS] Intent=dev',
58+
'- PC2 [PASS] Session bound',
59+
'- PC3 [PASS] Project=devcodex-v1',
60+
'- PC4 [N/A] skipReason=probe-non-radar',
61+
'- PC5 [PASS] Host=grok Full',
62+
'- PC6 [PASS] Git clean scope',
63+
'- PC7 [PASS] Next=finish',
6464
'',
6565
'### DevCodex · 完成检查',
6666
'| 类型 | 命令 | exitCode | runId/计数 |',
@@ -99,13 +99,17 @@ const ok =
9999
summary.replyEvidence === 'verified-present' &&
100100
summary.replySource === 'lastAssistantMessage' &&
101101
summary.compliance === true &&
102+
summary.precheckStatus === 'verified-present' &&
103+
summary.fvs === 'verified-present' &&
104+
Array.isArray(summary.gaps) &&
105+
summary.gaps.length === 0 &&
102106
summary.stopDecision !== 'block'
103107

104108
if (!ok) {
105-
console.error('ACCEPTANCE_FAIL')
109+
console.error('ACCEPTANCE_FAIL', summary)
106110
process.exitCode = 1
107111
} else {
108-
console.log('ACCEPTANCE_PASS mode=dev lastAssistantMessage completion-check recognized')
112+
console.log('ACCEPTANCE_PASS mode=dev lastAssistantMessage completion-check + entry + FVS green')
109113
}
110114

111115
try { fs.unlinkSync(tmp) } catch { /* ignore */ }

0 commit comments

Comments
 (0)