Skip to content

Commit 53ad230

Browse files
committed
fix: tighten validation workflow observability
1 parent e13798b commit 53ad230

22 files changed

Lines changed: 331 additions & 41 deletions

‎changelogs/unreleased.md‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@
1313

1414
- **本地候选修复 — 派生资产新鲜度与默认验证漏检**:刷新 `content/skills/portfolio.json`,并将 `skill-portfolio-current` 纳入 changed/iterative 默认验证不变量,使 clean-tree `npm test` 计划覆盖 portfolio freshness;同步 validation DAG 断言与 Windows 中文文件名负向测试夹具,避免默认验证对派生资产 stale false-green。本项仅为本地源码候选,未 tag、未 publish、未刷新 MCP。
1515
- **本地候选修复 — 验证入口、产物状态投影与 Profile 当前真相刷新**:收紧 active-root 产物 stale CP 状态检测,避免把“待用户确认是否提交”等非 CP 文案误判为 CP1/CP2 阻断;默认 `test:fast/full/delivery/boundary/profile-deploy/package-release` 改为 `human-cli --plan` 入口并补充 `:ai` 严格变体,避免 Codex/AI 环境缺 `contextEpoch` 时本地 plan 命令误红;新增 `profile-current:refresh` 标准命令,用于更新 active Profile `ProfileCurrentTruthV1` 的当前源码 HEAD 与 source candidate 身份。本项仅为本地源码候选,未 tag、未 publish、未刷新 MCP。
16+
- **本地候选修复 — 验证入口派生资产与 Profile 边界**:将默认 `test` / `test:changed` / scoped route 入口恢复为执行型验证,新增显式 `test:plan` / `test:*:plan` 作为零执行预览入口,避免 plan-only 被误读为测试已执行;把 `scripts/refresh-profile-current-truth.js` 纳入 profile boundary 与 `profile-governance` 输入闭包,确保修改 Profile current truth refresh 脚本时触发 profile 验证;刷新 `content/skills/portfolio.json`。本项仅为本地源码候选,未 tag、未 publish、未刷新 MCP。
17+
- **本地候选修复 — 运行态可观测性与历史债务收敛**:`validate.js` 现在对每个 V 探针输出 start/done heartbeat,V7 改为有界 fast-path smoke,避免聚合校验无界卡死;visible reply evidence 绑定当前 promptCount,无 payload 时不再继承旧 precheck;`memory_status` 增加 `summaryCurrentProjection`,显式区分 append-only SUMMARY 历史与当前折叠状态;artifact checker 新增 digest-bound `HistoricalArtifactIssueDispositionV1`,用于精确处置历史 unknown/stale/conflict 产物噪声。本项仅为本地源码候选,未 tag、未 publish、未刷新 MCP。
1618

1719
- **v1.20.0 发布候选 — 任务连续性与工程控制面升级**:汇总 Stage A/B 的任务写入代际、合法 CP 演进续代、taskless 恢复、项目优先 Profile、多语言连续性、分层 SkillRoute、宿主原生多 Agent 编排合同、验证执行提速与修复批次收敛。完整说明见 [`changelogs/releases/v1.20.0.md`](./releases/v1.20.0.md)。正式发布事实仍只由 tag、registry、GitHub Release 与发布后安装回读共同成立。
1820
- **两阶段精确制品发布防护**:tag 资格阶段只生成一个正式精确 tarball,对同一制品完成隔离 consumer/HOME/prefix/cache/workspace 安装并保存;本机复用该制品完成真实 Codex G1→G2 后,才通过 `publish-qualified` 续发 npm 与 GitHub Release,避免 tag 触发后未经真实宿主验收即发生不可逆发布。

‎content/skills/portfolio.json‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@
1313
"publicTaxonomyDigest": "b673f27fa58c2d7a57b9aa906562faed2aca094a727c8686d941c39a5490f030",
1414
"portfolioEvidence": "content/skills/portfolio-evidence.json",
1515
"portfolioEvidenceDigest": "8364a73c66ed2ec9787cdb9300223278d128a45b689a2b8deda0d1f03189f42b",
16-
"consumerInventoryFileCount": 718,
17-
"consumerInventoryDigest": "90be8bb27ca78eb1fcd8151601ec839c087e6b8727e1f326b7d54897530c0431",
16+
"consumerInventoryFileCount": 719,
17+
"consumerInventoryDigest": "d16b31fa502b07accabc7e87b05930f22fbbedfa03db8f27d2e932a70ee1421f",
1818
"consumerProjectionDigest": "2a9023e04a434de68bc88eac365e483307cabfb5f768bbb85151cd24376e34e4",
19-
"portfolioInputDigest": "c681c19b07d6611b5e4c2d04d1d27f4961e1834985e05a244048044e9408ec6a"
19+
"portfolioInputDigest": "9d5e556f66f0184b7acf174b3c560ece48165e7df247225581d2b3aeb532a3d0"
2020
},
2121
"ordering": "skills.id asc; graph edges from/to asc",
2222
"summary": {

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

Lines changed: 51 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,11 +284,34 @@ function buildLifecycleVisibleReplyUtils(ctx) {
284284
state.visible.workflowCompletion = projectWorkflowCompletionVisibleState(completionProjection)
285285
}
286286
const evidence = getVisibleReplyEvidence(payload)
287+
const visibleObservedAt = new Date().toISOString()
288+
state.visible.observedPromptCount = Number(state.promptCount || 0)
289+
state.visible.observedEventName = eventName
290+
state.visible.observedAt = visibleObservedAt
291+
state.visible.sampleFreshness = 'current'
292+
state.visible.staleReason = ''
287293
state.visible.replyEvidence = evidence.observed ? 'verified-present' : 'unverified'
288294
state.visible.replySource = evidence.source || ''
289295
state.visible.artifactEvidenceSource = evidence.source || ''
290296
if (!evidence.observed) {
291297
// PF-163: unobserved payload cannot invent delivery; still surface semantic-artifact gap for completion evidence
298+
state.visible.payloadObserved = false
299+
state.visible.precheck = false
300+
state.visible.precheckStatus = 'unverified'
301+
state.visible.precheckMissingItems = []
302+
state.visible.entryCheckCompleteness = {
303+
claimed: false,
304+
complete: false,
305+
status: 'not-claimed',
306+
missingPcs: [],
307+
missingItems: [],
308+
foldedRanges: [],
309+
presentPcs: [],
310+
sourceSchemaVersion: null,
311+
migrationStatus: 'not-claimed'
312+
}
313+
state.visible.sampleFreshness = 'unverified'
314+
state.visible.staleReason = 'visible-payload-unobserved'
292315
state.visible.artifactStatus = 'unverified'
293316
state.visible.artifactMissingItems = ['visible-payload-unobserved', 'semantic-artifact-items']
294317
state.visible.finalValidationSummaryStatus = 'unverified'
@@ -436,7 +459,29 @@ function buildLifecycleVisibleReplyUtils(ctx) {
436459
if (eventName === 'Stop') fs.unlinkSync(statePaths.finalPayloadFlag)
437460
}
438461

462+
function getVisibleReplyFreshness(state) {
463+
const visible = state?.visible
464+
if (!visible || visible.payloadObserved !== true) return { status: 'unverified', reason: 'no-visible-payload' }
465+
const currentPromptCount = Number(state.promptCount || 0)
466+
const observedPromptCount = Number(visible.observedPromptCount || 0)
467+
if (!observedPromptCount || observedPromptCount !== currentPromptCount) {
468+
visible.sampleFreshness = 'stale'
469+
visible.staleReason = 'prompt-count-mismatch'
470+
return {
471+
status: 'stale',
472+
reason: 'prompt-count-mismatch',
473+
observedPromptCount,
474+
currentPromptCount
475+
}
476+
}
477+
visible.sampleFreshness = 'current'
478+
visible.staleReason = ''
479+
return { status: 'current', reason: '' }
480+
}
481+
439482
function getPrecheckEvidenceStatus(state) {
483+
const freshness = getVisibleReplyFreshness(state)
484+
if (freshness.status === 'stale') return 'unverified'
440485
if (state.visible?.precheck) return 'verified-present'
441486
if (state.visible?.precheckStatus === 'verified-missing') return 'verified-missing'
442487
if (state.visible?.payloadObserved) return 'verified-missing'
@@ -488,7 +533,11 @@ function buildLifecycleVisibleReplyUtils(ctx) {
488533
items.push('entry check block 未输出(S07/C18:首条用户可见回复必须含 PC0~PC10 入口检查块)')
489534
}
490535
} else if (eventName === 'Stop' && precheckStatus === 'unverified') {
491-
items.push(`无法验证最终用户可见回复是否包含入口检查块(Stop/PreCompact 未提供可解析 assistant 内容;如需取证请创建 ${getStatePaths(state).finalPayloadFlag} 后重试)`)
536+
const freshness = getVisibleReplyFreshness(state)
537+
const reason = freshness.status === 'stale'
538+
? `可见回复样本已过期(${freshness.reason},observedPromptCount=${freshness.observedPromptCount || 0}, currentPromptCount=${freshness.currentPromptCount || 0})`
539+
: `Stop/PreCompact 未提供可解析 assistant 内容;如需取证请创建 ${getStatePaths(state).finalPayloadFlag} 后重试`
540+
items.push(`无法验证最终用户可见回复是否包含入口检查块(${reason})`)
492541
}
493542
settleS07OrderStatus(state, eventName)
494543
if (eventName === 'Stop' && state.visible?.s07OrderStatus === 'late') {
@@ -573,6 +622,7 @@ function buildLifecycleVisibleReplyUtils(ctx) {
573622
analyzeEntryCheckCompleteness,
574623
updateVisibleReplyState,
575624
captureFinalPayloadSample,
625+
getVisibleReplyFreshness,
576626
getPrecheckEvidenceStatus,
577627
settleS07OrderStatus,
578628
buildClosureReminder,

‎mcp/memory-server.js‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ const {
116116
const {
117117
currentActiveSessionIds,
118118
rowsByCurrentState,
119+
summarizeSummaryCurrentProjection,
119120
summaryStateConflicts
120121
} = require('../scripts/lib/memory-summary-state.js')
121122
const {
@@ -3107,6 +3108,7 @@ function handleMemoryStatus(args) {
31073108
latestRows: indexed.latestRows,
31083109
activeSessionIds,
31093110
conflicts,
3111+
summaryCurrentProjection: indexed.summaryCurrentProjection || null,
31103112
warnings: [...boundWarnings, ...indexed.warnings].slice(0, 20),
31113113
indexReceipt: indexed.envelope.receipt,
31123114
coverage: indexed.envelope.coverage,
@@ -3154,6 +3156,7 @@ function handleMemoryStatus(args) {
31543156
latestRows,
31553157
activeSessionIds,
31563158
conflicts,
3159+
summaryCurrentProjection: summarizeSummaryCurrentProjection(parsed.rows),
31573160
warnings: [...boundWarnings, ...parsed.warnings].slice(0, 20),
31583161
indexReceipt: memoryIndexFallbackReceipt('summary', indexed),
31593162
coverage: {

‎package.json‎

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,13 @@
1313
"global-adapters:remove": "node index.js global-adapters remove --apply",
1414
"global-adapters:remove:dry-run": "node index.js global-adapters remove --dry-run",
1515
"test:global-adapters-cli": "node scripts/test-global-adapters-cli.js",
16-
"test": "node scripts/run-validation.js --route changed --actor human-cli --plan",
17-
"test:fast": "node scripts/run-validation.js --route fast --actor human-cli --plan",
16+
"test": "npm run test:changed",
17+
"test:plan": "npm run test:changed:plan",
18+
"test:fast": "node scripts/run-validation.js --route fast",
19+
"test:fast:plan": "node scripts/run-validation.js --route fast --actor human-cli --plan",
1820
"test:fast:ai": "node scripts/run-validation.js --route fast --actor ai-hook",
19-
"test:full": "node scripts/run-validation.js --route full --actor human-cli --plan",
21+
"test:full": "node scripts/run-validation.js --route full",
22+
"test:full:plan": "node scripts/run-validation.js --route full --actor human-cli --plan",
2023
"test:full:ai": "node scripts/run-validation.js --route full --actor ai-hook",
2124
"test:all": "npm run test:full",
2225
"test:core": "node scripts/validate-versions.js && node scripts/validate.js",
@@ -152,15 +155,20 @@
152155
"test:ci-validation-plan": "node scripts/test-ci-validation-plan.js",
153156
"test:validation-authority": "node scripts/test-validation-execution-authority.js && node scripts/test-validation-budget-control.js && node scripts/test-validation-convergence.js",
154157
"test:artifact-authority": "node scripts/test-artifact-mutation-authority.js && npm run test:artifact-slot-template-coverage",
155-
"test:changed": "node scripts/run-validation.js --route changed --actor human-cli --plan",
158+
"test:changed": "node scripts/run-validation.js --route changed",
159+
"test:changed:plan": "node scripts/run-validation.js --route changed --actor human-cli --plan",
156160
"test:changed:ai": "node scripts/run-validation.js --route changed --actor ai-hook",
157-
"test:delivery": "node scripts/run-validation.js --route delivery --actor human-cli --plan",
161+
"test:delivery": "node scripts/run-validation.js --route delivery",
162+
"test:delivery:plan": "node scripts/run-validation.js --route delivery --actor human-cli --plan",
158163
"test:delivery:ai": "node scripts/run-validation.js --route delivery --actor ai-hook",
159-
"test:boundary": "node scripts/run-validation.js --route boundary --actor human-cli --plan",
164+
"test:boundary": "node scripts/run-validation.js --route boundary",
165+
"test:boundary:plan": "node scripts/run-validation.js --route boundary --actor human-cli --plan",
160166
"test:boundary:ai": "node scripts/run-validation.js --route boundary --actor ai-hook",
161-
"test:profile-deploy": "node scripts/run-validation.js --route profile-deploy --actor human-cli --plan",
167+
"test:profile-deploy": "node scripts/run-validation.js --route profile-deploy",
168+
"test:profile-deploy:plan": "node scripts/run-validation.js --route profile-deploy --actor human-cli --plan",
162169
"test:profile-deploy:ai": "node scripts/run-validation.js --route profile-deploy --actor ai-hook",
163-
"test:package-release": "node scripts/run-validation.js --route package-release --actor human-cli --plan",
170+
"test:package-release": "node scripts/run-validation.js --route package-release",
171+
"test:package-release:plan": "node scripts/run-validation.js --route package-release --actor human-cli --plan",
164172
"test:package-release:ai": "node scripts/run-validation.js --route package-release --actor ai-hook",
165173
"test:control-plane-module-contracts": "node scripts/test-control-plane-module-contracts.js",
166174
"test:closure-evidence": "node scripts/test-closure-evidence-controls.js",

‎scripts/lib/memory-index.js‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ const {
1414
foldSummaryRows,
1515
isCurrentProjectableRow,
1616
rowsByCurrentState,
17+
summarizeSummaryCurrentProjection,
1718
summaryStateConflicts
1819
} = require('./memory-summary-state.js')
1920

@@ -168,6 +169,7 @@ function buildSummaryPartitions(document, parsed) {
168169
nonCanonicalActiveCount: foldSummaryRows(rows).filter(row =>
169170
row.state === 'active' && !isCurrentProjectableRow(row)
170171
).length,
172+
summaryCurrentProjection: summarizeSummaryCurrentProjection(rows),
171173
warnings
172174
}
173175
const months = new Map()
@@ -496,6 +498,7 @@ function queryStatusIndex(input = {}) {
496498
activeSessionIds: payload.activeSessionIds,
497499
conflicts: payload.conflicts,
498500
nonCanonicalActiveCount: payload.nonCanonicalActiveCount,
501+
summaryCurrentProjection: payload.summaryCurrentProjection,
499502
warnings: payload.warnings,
500503
envelope: buildQueryEnvelope({
501504
status: 'fresh',

‎scripts/lib/memory-summary-state.js‎

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,11 +71,33 @@ function currentActiveSessionIds(rows = []) {
7171
.map(row => summarySessionKey(row))
7272
}
7373

74+
function summarizeSummaryCurrentProjection(rows = []) {
75+
const sourceRows = Array.isArray(rows) ? rows : []
76+
const projectableRows = sourceRows.filter(isCurrentProjectableRow)
77+
const currentRows = foldSummaryRows(projectableRows)
78+
const projectableActiveRows = projectableRows.filter(row => row.state === 'active')
79+
const currentActiveRows = currentRows.filter(row => row.state === 'active')
80+
const nonCanonicalActiveRows = sourceRows.filter(row => row.state === 'active' && !isCurrentProjectableRow(row))
81+
return {
82+
schemaVersion: 'MemorySummaryCurrentProjectionStatsV1',
83+
semantics: 'append-only SUMMARY history; current projection is the last canonical row per day/session',
84+
sourceRowCount: sourceRows.length,
85+
projectableRowCount: projectableRows.length,
86+
currentRowCount: currentRows.length,
87+
historicalProjectableRowCount: Math.max(0, projectableRows.length - currentRows.length),
88+
rawActiveRowCount: sourceRows.filter(row => row.state === 'active').length,
89+
currentActiveRowCount: currentActiveRows.length,
90+
historicalActiveRowCount: Math.max(0, projectableActiveRows.length - currentActiveRows.length),
91+
nonCanonicalActiveRowCount: nonCanonicalActiveRows.length
92+
}
93+
}
94+
7495
module.exports = {
7596
currentActiveSessionIds,
7697
foldSummaryRows,
7798
isCurrentProjectableRow,
7899
rowsByCurrentState,
100+
summarizeSummaryCurrentProjection,
79101
summarySessionKey,
80102
summaryStateConflicts
81103
}

‎scripts/lib/probe-registry.js‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,11 +86,15 @@ function createProbeRegistry(groups, { expectedIds = null } = {}) {
8686
return Object.freeze(ordered)
8787
}
8888

89-
function runProbeRegistry(registry, { afterRun = null } = {}) {
89+
function runProbeRegistry(registry, { beforeRun = null, afterRun = null } = {}) {
90+
if (beforeRun !== null && typeof beforeRun !== 'function') {
91+
throw new TypeError('beforeRun must be a function')
92+
}
9093
if (afterRun !== null && typeof afterRun !== 'function') {
9194
throw new TypeError('afterRun must be a function')
9295
}
9396
for (const descriptor of registry) {
97+
if (beforeRun) beforeRun(descriptor)
9498
descriptor.run()
9599
if (afterRun) afterRun(descriptor)
96100
}

0 commit comments

Comments
 (0)