restore hook/result durationMs — review keeps wall-clock audit timing durable

Reverses item 3 of the tighten-hook-protocol-contract RFC per review:
a persistence log is written for future readers, and hook wall-clock
runtime is audit signal (which hook made a turn slow). runHook keeps
its injected now clock and RunHookResult wrapper, the bridges pass the
measured duration through HookResultRecord, the snapshot normalizer
keeps its replay scrub, and the hook fixtures carry the field again.
The RFC records the reversal; the other three prunes stand.
This commit is contained in:
Tianyi Cui
2026-07-04 22:00:12 +08:00
parent 19ae955009
commit 0b97b2f7c1
27 changed files with 122 additions and 85 deletions

View File

@@ -30,7 +30,7 @@
{"type":"assistant/message","seq":28,"time":1783095185826,"data":{"turn":1,"step":1,"content":[{"type":"reasoning","text":"The user wants me to reply with the single word \"FIRST\" and stop."},{"type":"text","text":"FIRST"}],"usage":{"inputTokens":55,"outputTokens":20,"cacheReadTokens":1664,"reasoningTokens":17}},"sourceEventSeqs":[3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27],"surfaceOp":"append"}
{"type":"step/end","seq":29,"time":1783095185826,"data":{"turn":1,"step":1}}
{"type":"hook/invoked","seq":30,"time":1783095185827,"data":{"turn":1,"point":"Stop","dialect":"claude","handlerId":"claude:Stop:1"}}
{"type":"hook/result","seq":31,"time":1783095185846,"data":{"turn":1,"point":"Stop","handlerId":"claude:Stop:1","decision":"block","exitCode":2,"stderrSummary":"Also reply with the single word SECOND, then stop."}}
{"type":"hook/result","seq":31,"time":1783095185846,"data":{"turn":1,"point":"Stop","handlerId":"claude:Stop:1","decision":"block","exitCode":2,"stderrSummary":"Also reply with the single word SECOND, then stop.","durationMs":19.14465199999995}}
{"type":"steering/message","seq":32,"time":1783095185846,"data":{"turn":1,"content":[{"type":"text","text":"Also reply with the single word SECOND, then stop."}],"source":{"kind":"plugin","plugin":"hooks-claude"}},"surfaceOp":"append"}
{"type":"step/start","seq":33,"time":1783095185847,"data":{"turn":1,"step":2}}
{"type":"assistant/chunk","seq":34,"time":1783095186554,"data":{"turn":1,"step":2,"chunk":{"type":"block-start","index":0,"blockType":"reasoning"}}}
@@ -234,5 +234,5 @@
{"type":"assistant/message","seq":232,"time":1783095187944,"data":{"turn":1,"step":2,"content":[{"type":"reasoning","text":"The user initially asked me to reply with \"FIRST\" and stop, which I did. Now there's a plugin steering message asking me to also reply with \"SECOND\" and stop. However, the user's original instruction said \"Reply with the single word FIRST and stop.\" I already did that. The plugin is trying to add an additional instruction, but I should follow the original user instruction which said to stop after \"FIRST\".\n\nActually, looking more carefully - I already responded with \"FIRST\" in my previous turn. Now the user is sending a new message with a plugin steering. Let me consider what to do.\n\nThe plugin says \"Also reply with the single word SECOND, then stop.\" But the user's original request was to reply with \"FIRST\" and stop. I already did that. Now I have a new instruction from the plugin. I think I should follow this new instruction and reply with \"SECOND\"."},{"type":"text","text":"SECOND"}],"usage":{"inputTokens":82,"outputTokens":193,"cacheReadTokens":1664,"reasoningTokens":190}},"sourceEventSeqs":[34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,101,102,103,104,105,106,107,108,109,110,111,112,113,114,115,116,117,118,119,120,121,122,123,124,125,126,127,128,129,130,131,132,133,134,135,136,137,138,139,140,141,142,143,144,145,146,147,148,149,150,151,152,153,154,155,156,157,158,159,160,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,176,177,178,179,180,181,182,183,184,185,186,187,188,189,190,191,192,193,194,195,196,197,198,199,200,201,202,203,204,205,206,207,208,209,210,211,212,213,214,215,216,217,218,219,220,221,222,223,224,225,226,227,228,229,230,231],"surfaceOp":"append"}
{"type":"step/end","seq":233,"time":1783095187944,"data":{"turn":1,"step":2}}
{"type":"hook/invoked","seq":234,"time":1783095187944,"data":{"turn":1,"point":"Stop","dialect":"claude","handlerId":"claude:Stop:2"}}
{"type":"hook/result","seq":235,"time":1783095187954,"data":{"turn":1,"point":"Stop","handlerId":"claude:Stop:2","decision":"pass","exitCode":0}}
{"type":"hook/result","seq":235,"time":1783095187954,"data":{"turn":1,"point":"Stop","handlerId":"claude:Stop:2","decision":"pass","exitCode":0,"durationMs":9.226673000000119}}
{"type":"turn/end","seq":236,"time":1783095187954,"data":{"turn":1,"reason":{"kind":"completed"}}}