feat(verification): acceptance-check layer + executor fix-loop; hide phase-checkpoint walls; guaranteed turn-end summary. Verification gated behind VIBN_VERIFICATION_ENABLED.

This commit is contained in:
2026-06-10 19:43:36 -07:00
parent 46291becd3
commit 39cb9194a5
9 changed files with 1263 additions and 31 deletions

View File

@@ -551,9 +551,17 @@ function ThinkingBubble({ thoughts }: { thoughts: string }) {
function stripRawToolLogs(text: string): string {
if (!text) return text;
return text
.replace(/(?:\r?\n)*\[tools executed this turn:[\s\S]*?\]/g, "")
.trim();
let out = text.replace(
/(?:\r?\n)*\[tools executed this turn:[\s\S]*?\]/g,
"",
);
// Safety net: strip the internal "Phase Checkpoint" planning block
// (Goal / Current Findings / Suspected Cause / Verification Plan) if it
// ever reaches a user-facing message. This is loop-control machinery, not
// something the end user should read. We drop from the heading to the end
// of that block (until a blank line followed by non-bulleted prose, or EOF).
out = out.replace(/(?:^|\n)\s*#{0,3}\s*Phase Checkpoint[\s\S]*$/i, "").trim();
return out.trim();
}
const MessageBubble = React.memo(function MessageBubble({
@@ -748,32 +756,8 @@ function Timeline({ entries }: { entries: TimelineEntry[] }) {
);
}
if (item.kind === "checkpoint") {
return (
<div
key={i}
style={{
margin: "6px 0 12px",
padding: "12px 14px",
background: "oklch(0.20 0.04 35 / 0.15)",
border: "1px dashed var(--accent)",
borderRadius: 8,
fontSize: "0.75rem",
color: "var(--fg-mute)",
fontFamily: "var(--font-mono), monospace",
}}
>
<div
style={{
color: "var(--accent)",
fontWeight: "bold",
marginBottom: 4,
}}
>
[Checkpoint Logged]
</div>
<div style={{ opacity: 0.8 }}>{item.goal}</div>
</div>
);
// Internal loop-control machinery — never shown to the user.
return null;
}
return (
<TimelineToolGroup