fix: 强化 Workbench Kafka 重放探针

This commit is contained in:
Codex
2026-07-10 15:58:51 +02:00
parent d69b436226
commit 6297a2ea14
10 changed files with 619 additions and 55 deletions
@@ -90,8 +90,26 @@ test("observe status renderer exposes bounded Workbench Kafka debug replay evide
traceId: "trc_fixture",
topic: "hwlab.event.debug.v1",
groupId: "hwlab-v03-workbench-isolated-debug-123-fixture",
phase: "terminal",
code: "terminal_complete",
receivedCount: 35,
appliedCount: 35,
layerCounts: {
server: { scanned: 35, matched: 35, delivered: 35 },
client: { received: 35, decoded: 35, applied: 35 },
},
traceTimeline: {
contract: "trace-sequence-authority-v1",
sourceAuthorityRowCount: 35,
readableSourceRowCount: 35,
},
rawHwlabEventWindow: {
available: true,
samePage: true,
unfilteredContractPresent: true,
productEventSourceRequestCount: 0,
counts: { received: 35, retained: 30, rejected: 0, evicted: 5, bytes: 8192 },
},
terminalStatus: "completed",
reportSha256: "sha256:report",
screenshot: { sha256: "sha256:image" },
@@ -105,7 +123,10 @@ test("observe status renderer exposes bounded Workbench Kafka debug replay evide
const text = String(rendered.renderedText);
assert.match(text, /Workbench Kafka debug replay:/u);
assert.match(text, /trc_fixture.*hwlab\.event\.debug\.v1/u);
assert.match(text, /terminal.*terminal_complete/u);
assert.match(text, /35.*35.*completed.*sha256:image/u);
assert.match(text, /SERVER_SCANNED.*CLIENT_APPLIED/u);
assert.match(text, /trace-sequence-authority-v1.*35.*35.*true.*true.*true.*35.*30.*0.*5.*8192.*0/u);
});
test("web-probe script render warns to promote repeated scripts into commands", () => {
@@ -86,8 +86,26 @@ test("observe status preserves bounded Workbench Kafka debug replay evidence", a
topic: "hwlab.event.debug.v1",
groupId: "hwlab-v03-workbench-isolated-debug-123-fixture",
groupPrefix: "hwlab-v03-workbench-isolated-debug",
phase: "terminal",
code: "terminal_complete",
receivedCount: 35,
appliedCount: 35,
layerCounts: {
server: { scanned: 35, matched: 35, delivered: 35 },
client: { received: 35, decoded: 35, applied: 35 },
},
traceTimeline: {
contract: "trace-sequence-authority-v1",
sourceAuthorityRowCount: 35,
readableSourceRowCount: 35,
},
rawHwlabEventWindow: {
available: true,
samePage: true,
unfilteredContractPresent: true,
productEventSourceRequestCount: 0,
counts: { received: 35, retained: 30, rejected: 0, evicted: 5, bytes: 8192 },
},
terminalStatus: "completed",
reportPath: "/tmp/report.json",
reportSha256: "sha256:report",
@@ -102,5 +120,10 @@ test("observe status preserves bounded Workbench Kafka debug replay evidence", a
assert.equal(status.exactCommand.result.groupPrefix, "hwlab-v03-workbench-isolated-debug");
assert.equal(status.exactCommand.result.receivedCount, 35);
assert.equal(status.exactCommand.result.appliedCount, 35);
assert.equal(status.exactCommand.result.phase, "terminal");
assert.equal(status.exactCommand.result.code, "terminal_complete");
assert.equal(status.exactCommand.result.layerCounts.server.scanned, 35);
assert.equal(status.exactCommand.result.traceTimeline.sourceAuthorityRowCount, 35);
assert.equal(status.exactCommand.result.rawHwlabEventWindow.counts.retained, 30);
assert.equal(status.exactCommand.result.screenshot.sha256, "sha256:image");
});
@@ -60,6 +60,7 @@ export function nodeWebObserveStatusNodeScript(tailLines: number, node: string,
const compactSample=(item)=>({seq:item.seq,ts:item.ts,path:item.path,routeSessionId:item.routeSessionId||null,activeSessionId:item.activeSessionId||null,messageCount:Array.isArray(item.messages)?item.messages.length:0,traceRowCount:Array.isArray(item.traceRows)?item.traceRows.length:0,loadingCount:Array.isArray(item.loadings)?item.loadings.length:0,loadingOwners:Array.isArray(item.loadings)?Array.from(new Set(item.loadings.map((loading)=>loading.ownerLabel||loading.ownerKind||loading.ownerKey||'loading'))).slice(0,4):[],sessionRail:item.sessionRail?{visibleCount:item.sessionRail.visibleCount??null,fallbackTitleCount:item.sessionRail.fallbackTitleCount??null,fallbackTitleRatio:item.sessionRail.fallbackTitleRatio??null,fallbackItems:Array.isArray(item.sessionRail.fallbackItems)?item.sessionRail.fallbackItems.slice(0,4).map((fallback)=>({sessionIdPrefix:fallback.sessionIdPrefix??null,titlePreview:fallback.titlePreview??null,active:fallback.active===true})):[]}:null});
const compactNetwork=(item)=>({ts:item.ts,type:item.type,method:item.method,url:short(item.url),status:item.status||null,failure:item.failure?short(item.failure):null});
const commandDir=(name)=>path.join(dir,'commands',name);
const compactReplayEvidence=(value)=>{const item=value&&typeof value==='object'?value:null;if(!item)return{};const layers=item.layerCounts&&typeof item.layerCounts==='object'?item.layerCounts:null;const server=layers&&layers.server&&typeof layers.server==='object'?layers.server:null;const client=layers&&layers.client&&typeof layers.client==='object'?layers.client:null;const trace=item.traceTimeline&&typeof item.traceTimeline==='object'?item.traceTimeline:null;const raw=item.rawHwlabEventWindow&&typeof item.rawHwlabEventWindow==='object'?item.rawHwlabEventWindow:null;const rawCounts=raw&&raw.counts&&typeof raw.counts==='object'?raw.counts:null;return{phase:item.phase||null,code:item.code||null,layerCounts:layers?{server:server?{scanned:server.scanned??null,matched:server.matched??null,delivered:server.delivered??null}:null,client:client?{received:client.received??null,decoded:client.decoded??null,applied:client.applied??null}:null,valuesRedacted:true}:null,traceTimeline:trace?{contract:trace.contract||null,authorityAvailable:trace.authorityAvailable??null,rowCount:trace.rowCount??null,sourceAuthorityRowCount:trace.sourceAuthorityRowCount??null,projectedAuthorityRowCount:trace.projectedAuthorityRowCount??null,readableSourceRowCount:trace.readableSourceRowCount??null,valuesRedacted:true}:null,rawHwlabEventWindow:raw?{available:raw.available??null,contract:raw.contract||null,reason:raw.reason||null,samePage:raw.samePage??null,productEventsPath:raw.productEventsPath||null,productEventSourceRequestCount:raw.productEventSourceRequestCount??null,sourceContractPresent:raw.sourceContractPresent??null,unfilteredContractPresent:raw.unfilteredContractPresent??null,textBytes:raw.textBytes??null,textHash:raw.textHash||null,textPresent:raw.textPresent??null,counts:rawCounts?{received:rawCounts.received??null,retained:rawCounts.retained??null,decoded:rawCounts.decoded??null,rejected:rawCounts.rejected??null,evicted:rawCounts.evicted??null,bytes:rawCounts.bytes??null}:null,valuesRedacted:true}:null}};
const commandSummary=(name)=>{
const root=commandDir(name); let entries=[];
try{entries=fs.readdirSync(root).filter((item)=>item.endsWith('.json')).sort();}catch{}
@@ -74,7 +75,7 @@ export function nodeWebObserveStatusNodeScript(tailLines: number, node: string,
if(!parsed)continue;
const result=parsed.result&&typeof parsed.result==='object'?parsed.result:null;
const error=parsed.error&&typeof parsed.error==='object'?parsed.error:null;
return {commandId:exactCommandId,phase:bucket,ok:parsed.ok??null,type:parsed.type||null,createdAt:parsed.createdAt||null,completedAt:parsed.completedAt||null,failedAt:parsed.failedAt||null,result:result?{ok:result.ok??null,status:result.status||null,profile:result.profile||null,sessionId:result.sessionId||null,traceId:result.traceId||null,traceIds:Array.isArray(result.traceIds)?result.traceIds.slice(0,4):[],runIds:Array.isArray(result.runIds)?result.runIds.slice(0,4):[],commandIds:Array.isArray(result.commandIds)?result.commandIds.slice(0,4):[],topic:result.topic||null,groupId:result.groupId||null,groupPrefix:result.groupPrefix||null,receivedCount:result.receivedCount??null,appliedCount:result.appliedCount??null,terminalStatus:result.terminalStatus||null,warmRunnerReused:result.warmRunnerReused??null,forbiddenRequestCount:result.forbiddenRequestCount??null,replayedEventCount:result.replayedEventCount??null,reportPath:result.reportPath||null,reportSha256:result.reportSha256||null,screenshot:result.screenshot&&typeof result.screenshot==='object'?{path:result.screenshot.path||null,sha256:result.screenshot.sha256||null,valuesRedacted:true}:null,valuesRedacted:true}:null,error:error?{name:error.name||null,message:short(error.message,200),details:error.details&&typeof error.details==='object'?{profile:error.details.profile||null,reportPath:error.details.reportPath||null,reportSha256:error.details.reportSha256||null,valuesRedacted:true}:null}:null,valuesRedacted:true};
return {commandId:exactCommandId,phase:bucket,ok:parsed.ok??null,type:parsed.type||null,createdAt:parsed.createdAt||null,completedAt:parsed.completedAt||null,failedAt:parsed.failedAt||null,result:result?{ok:result.ok??null,status:result.status||null,profile:result.profile||null,sessionId:result.sessionId||null,traceId:result.traceId||null,traceIds:Array.isArray(result.traceIds)?result.traceIds.slice(0,4):[],runIds:Array.isArray(result.runIds)?result.runIds.slice(0,4):[],commandIds:Array.isArray(result.commandIds)?result.commandIds.slice(0,4):[],topic:result.topic||null,groupId:result.groupId||null,groupPrefix:result.groupPrefix||null,receivedCount:result.receivedCount??null,appliedCount:result.appliedCount??null,terminalStatus:result.terminalStatus||null,...compactReplayEvidence(result),warmRunnerReused:result.warmRunnerReused??null,forbiddenRequestCount:result.forbiddenRequestCount??null,replayedEventCount:result.replayedEventCount??null,reportPath:result.reportPath||null,reportSha256:result.reportSha256||null,screenshot:result.screenshot&&typeof result.screenshot==='object'?{path:result.screenshot.path||null,sha256:result.screenshot.sha256||null,valuesRedacted:true}:null,valuesRedacted:true}:null,error:error?{name:error.name||null,message:short(error.message,200),details:error.details&&typeof error.details==='object'?{profile:error.details.profile||null,...compactReplayEvidence(error.details),traceId:error.details.traceId||null,topic:error.details.topic||null,groupId:error.details.groupId||null,receivedCount:error.details.receivedCount??null,appliedCount:error.details.appliedCount??null,terminalStatus:error.details.terminalStatus||null,reportPath:error.details.reportPath||null,reportSha256:error.details.reportSha256||null,valuesRedacted:true}:null}:null,valuesRedacted:true};
}
return {commandId:exactCommandId,phase:'not-found',ok:false,valuesRedacted:true};
};
@@ -35,6 +35,9 @@ test("Workbench Kafka debug replay runner contract is derived from owning YAML",
topic: "hwlab.event.debug.v1",
groupPrefix: "hwlab-v03-workbench-isolated-debug",
completionTimeoutMs: 30_000,
productEventsPath: "/v1/workbench/events",
debugEventsPath: "/v1/workbench/debug/kafka-sse/events",
requestObservationQuietMs: 3_000,
valuesRedacted: true,
});
});
@@ -51,7 +51,17 @@ export function nodeWebProbeRealtimeFanoutProfiles(spec: HwlabRuntimeLaneSpec):
export function nodeWebProbeWorkbenchKafkaDebugReplay(spec: HwlabRuntimeLaneSpec): Record<string, unknown> | null {
const config = spec.webProbe?.workbenchKafkaDebugReplay;
if (config === undefined) return null;
return { ...config, valuesRedacted: true };
const pureKafkaLive = spec.webProbe?.realtimeFanoutProfiles?.["pure-kafka-live"];
if (pureKafkaLive === undefined) {
throw new Error("workbenchKafkaDebugReplay requires the pure-kafka-live realtimeFanoutProfile in the owning YAML");
}
return {
...config,
productEventsPath: pureKafkaLive.productEventsPath,
debugEventsPath: pureKafkaLive.debugEventsPath,
requestObservationQuietMs: pureKafkaLive.reconnectQuietMs,
valuesRedacted: true,
};
}
export function resolveWebObserveActionJson(