fix(dream): ground memory audit records in the real git diff (#4673)
* fix(dream): ground commit messages and cursor advance in the real git diff Dream consolidation could emit a /dream-log audit record that did not match the actual file changes: build_dream_commit_message appended the LLM's unverified resp.content, dream_run_completed only checked the stop reason, and file contents were deliberately omitted from the prompt. The combination let a single-turn self-report become the durable audit record. - gitstore: add summarize_working_tree() — a structured, machine-derived summary (per-file +N/-M, totals, capped unified diff) of working-tree changes vs HEAD. Pure filesystem/git ground truth, never LLM narrative. - memory: build_dream_commit_message now takes the diff body instead of resp; dream_content_diff() exposes the real delta over SOUL/USER/MEMORY.md only (excludes .dream_cursor so cursor writes aren't mistaken for edits); build_dream_prompt embeds current file contents so the model edits reality, not a stale mental model. - builtin/cli: both Dream paths now compute the diff, gate cursor advance on a non-empty delta (no-op runs no longer swallow history), and commit with the diff-grounded message. Non-git workspaces fall back to the completion check. - dream.md: document that contents are embedded, and add a chain-of- verification guardrail so the model's summary cannot claim unmade edits. A regression test proves a lying resp.content never reaches the audit log while the real diff does. * fix(dream): mark non-UTF-8 memory files as binary in diff summary Address review feedback (Q1 on PR #4673): summarize_working_tree read working-tree files with errors="replace", which would emit U+FFFD replacement chars into the audit record if a memory file ever held invalid UTF-8 — misrepresenting the diff it is meant to make truthful. Switch to errors="strict" and catch UnicodeDecodeError: a non-UTF-8 (or binary/corrupt) file is now recorded as "{path}: binary or non-UTF-8 file changed" and omitted from the unified diff, so the audit record stays honest. An empty diff block is also suppressed when all changes are binary. Adds a defensive regression test asserting no replacement char leaks.
This commit is contained in:
@@ -359,6 +359,7 @@ async def cmd_dream(ctx: CommandContext) -> OutboundMessage:
|
||||
store = loop.context.memory
|
||||
content = ""
|
||||
resp = None
|
||||
diff_body = ""
|
||||
t0 = time.monotonic()
|
||||
try:
|
||||
result = store.build_dream_prompt()
|
||||
@@ -379,9 +380,17 @@ async def cmd_dream(ctx: CommandContext) -> OutboundMessage:
|
||||
on_progress=_silent,
|
||||
)
|
||||
elapsed = time.monotonic() - t0
|
||||
if MemoryStore.dream_run_completed(resp):
|
||||
# Ground truth: the real file delta, not the LLM's self-report.
|
||||
diff_body = store.dream_content_diff()
|
||||
productive = bool(diff_body) or (
|
||||
not store.git.is_initialized()
|
||||
and MemoryStore.dream_run_completed(resp)
|
||||
)
|
||||
if productive:
|
||||
store.set_last_dream_cursor(last_cursor)
|
||||
content = f"Dream completed in {elapsed:.1f}s."
|
||||
elif MemoryStore.dream_run_completed(resp):
|
||||
content = f"Dream completed in {elapsed:.1f}s; no memory changes."
|
||||
else:
|
||||
content = (
|
||||
f"Dream did not complete after {elapsed:.1f}s; "
|
||||
@@ -399,7 +408,7 @@ async def cmd_dream(ctx: CommandContext) -> OutboundMessage:
|
||||
timezone_name=getattr(loop.context, "timezone", None),
|
||||
)
|
||||
if store.git.is_initialized():
|
||||
commit_msg = build_dream_commit_message("dream: manual run", resp)
|
||||
commit_msg = build_dream_commit_message("dream: manual run", diff_body)
|
||||
sha = store.git.auto_commit(commit_msg)
|
||||
if sha:
|
||||
content += f" (commit {sha})"
|
||||
|
||||
Reference in New Issue
Block a user