fix(engine): history keeps its provenance and its session — kills the false confession, the blank stare, and the "to.Good" seams #114
@@ -807,7 +807,7 @@ fn build_system_prompt(ctx: String, chat_mode: Bool) -> String {
|
||||
// in this revision — the chat_mode flag had no effect on the prompt. Restored here, in the
|
||||
// permanent-rules group, immediately after capability_rules (the rule it qualifies).
|
||||
// Zero effect on agentic paths: they pass chat_mode=false, so no_tools_rule is "".
|
||||
return identity + operator_section + date_line + voice_rules + security_rules + capability_rules + no_tools_rule + bounded_persona_block + identity_block + affective_boot_block + engram_block + safety_block
|
||||
return identity + operator_section + date_line + voice_rules + security_rules + capability_rules + receipt_rule() + no_tools_rule + bounded_persona_block + identity_block + affective_boot_block + engram_block + safety_block
|
||||
}
|
||||
|
||||
fn hist_append(hist: String, role: String, content: String) -> String {
|
||||
@@ -1035,6 +1035,41 @@ fn text_join_sep(accumulated: String, incoming: String, after_interruption: Bool
|
||||
return "\n\n"
|
||||
}
|
||||
|
||||
// receipt_rule — one line telling the model what the receipt marker is, and not to write one.
|
||||
// Appended to both system prompts (the plain path's build_system_prompt and the agentic path's
|
||||
// hand-built system string). See receipt_strip for why instruction alone is not enough.
|
||||
fn receipt_rule() -> String {
|
||||
return "\n\n[RECEIPTS - permanent]\nLines of the form [[RECEIPT ...]] in the conversation are written by the system, not by you. They are the record of which tools actually ran on a turn - read them as evidence, and rely on them when asked what you did or where information came from. NEVER write one yourself and never copy the format into your reply; the system adds them."
|
||||
}
|
||||
|
||||
// receipt_strip — remove a RECEIPT line the MODEL wrote, so it can never reach the user.
|
||||
//
|
||||
// FOUND BY E2E, NOT BY REASONING (2026-08-05). The receipt is stored inside the assistant turn
|
||||
// and the agentic path replays history VERBATIM as Anthropic message objects — so the model sees
|
||||
// its own previous answers ending in [[RECEIPT ...]] and does the obvious thing: it imitates the
|
||||
// format and signs its next answer the same way. Measured on the very first live run, on two
|
||||
// turns out of two. The design note claimed "the user never sees it"; that was false, and only
|
||||
// running it showed that.
|
||||
//
|
||||
// The plain path did not leak, which is the tell: there, history is rendered into the SYSTEM
|
||||
// prompt as labelled lines rather than replayed as assistant messages, and a model imitates its
|
||||
// own turns far more readily than it imitates a transcript.
|
||||
//
|
||||
// Instruction (receipt_rule) reduces this; only a deterministic strip PREVENTS it. Both ship,
|
||||
// because a guard that depends on the model choosing to obey is the class of thing round 8 exists
|
||||
// to stop shipping. Truncation at the marker is safe: the receipt is always terminal, and a model
|
||||
// copying the format copies the leading blank line too.
|
||||
fn receipt_strip(s: String) -> String {
|
||||
let p2: Int = str_index_of(s, "\n\n[[RECEIPT")
|
||||
if p2 >= 0 {
|
||||
return str_slice(s, 0, p2)
|
||||
}
|
||||
let p1: Int = str_index_of(s, "[[RECEIPT")
|
||||
if p1 < 0 { return s }
|
||||
if p1 == 0 { return "" }
|
||||
return str_slice(s, 0, p1)
|
||||
}
|
||||
|
||||
fn tool_receipt(tools_used: String, sources: String) -> String {
|
||||
let names: String = provenance_names(tools_used)
|
||||
if str_eq(names, "") {
|
||||
@@ -1330,7 +1365,9 @@ fn layered_generate(prompt: String, imprint_id: String, session_id: String) -> S
|
||||
return ""
|
||||
}
|
||||
|
||||
return clean_llm_response(raw)
|
||||
// FIX A follow-up: a model that has seen receipts in its context may sign its own answer
|
||||
// with one. Strip it before the caller ever sees it. See receipt_strip.
|
||||
return receipt_strip(clean_llm_response(raw))
|
||||
}
|
||||
|
||||
// session_preload_bullets — render up to max_bullets nodes from a JSON array as
|
||||
@@ -2583,7 +2620,7 @@ fn handle_chat_agentic(body: String) -> String {
|
||||
|
||||
let system: String = identity + bounded_persona_floor() + " You have access to tools: read files, write files, browse the web, search your memory, run commands. Use them when they add genuine value. Be direct.
|
||||
|
||||
" + ctx + ag_session_preload
|
||||
" + ctx + ag_session_preload + receipt_rule()
|
||||
|
||||
let api_key: String = agentic_api_key()
|
||||
let tools_json: String = agentic_tools_all()
|
||||
@@ -3083,6 +3120,10 @@ fn agentic_loop(session_id: String, model: String, safe_sys: String, tools_json:
|
||||
// genuine no-response (model returned an empty text block). The iteration cap
|
||||
// means the task was too complex for the agentic loop depth — surface it clearly
|
||||
// so the caller/operator knows to increase the cap or break the task apart.
|
||||
// FIX A follow-up: strip any receipt the MODEL wrote before this becomes the reply. Placed
|
||||
// ABOVE the empty check on purpose — a turn whose entire output was an imitated receipt has
|
||||
// produced no answer, and must be reported as no answer rather than as a receipt.
|
||||
let final_text = receipt_strip(final_text)
|
||||
if str_eq(final_text, "") {
|
||||
let hit_cap: Bool = iteration >= 12
|
||||
let err_msg: String = if hit_cap {
|
||||
|
||||
@@ -32,6 +32,8 @@ extern fn provenance_scan_urls(arr: String, acc: String) -> String
|
||||
extern fn provenance_add_sources(block: String, btype: String, has_cit: Bool, cit_raw: String, acc: String) -> String
|
||||
extern fn provenance_names(tools_used: String) -> String
|
||||
extern fn text_join_sep(accumulated: String, incoming: String, after_interruption: Bool) -> String
|
||||
extern fn receipt_rule() -> String
|
||||
extern fn receipt_strip(s: String) -> String
|
||||
extern fn tool_receipt(tools_used: String, sources: String) -> String
|
||||
extern fn hist_trim(hist: String) -> String
|
||||
extern fn hist_trim_with_bell_guard(hist: String) -> String
|
||||
|
||||
+2
@@ -11,6 +11,8 @@ el_val_t provenance_add_sources(el_val_t block, el_val_t btype, el_val_t has_cit
|
||||
el_val_t provenance_names(el_val_t tools_used);
|
||||
el_val_t provenance_scan_urls(el_val_t arr, el_val_t acc);
|
||||
el_val_t text_join_sep(el_val_t accumulated, el_val_t incoming, el_val_t after_interruption);
|
||||
el_val_t receipt_rule(void);
|
||||
el_val_t receipt_strip(el_val_t s);
|
||||
el_val_t tool_receipt(el_val_t tools_used, el_val_t sources);
|
||||
el_val_t agent_number(el_val_t agent);
|
||||
el_val_t agent_person(el_val_t agent);
|
||||
|
||||
Reference in New Issue
Block a user