Files
bikoret/vav_pipeline/lib/vav_pipeline/gate/shape.ex
T
Scott KarpandClaude Fable 5.1 cf8b5e3dc4 tablet 56: the gap that was not there — empty says what was read
The builder claimed the structural and frame checks never see an
engineering reply because its sentences are not "vital". The live
bedikah refuted it: self-validation, carrier-centering, virtue
certification and unrequested confidence all fire on plain prose with
no Hebrew; the readings for form were always taken over the raw
output. Nothing built for that; the tablet records it.

What was true: `empty` said "nothing to examine", and that under-
reported. Now the empty record carries checks.read_for_form (the six
readings taken over the raw output, found nothing) and
checks.grounding {assessed: false, reason}; the reply's `meaning` says
empty is not tov — read for form and clean of it, nothing to ground;
the fringe reads "empty — read for form, nothing found; nothing to
ground" (still stripped by @forged / _FORGED_FRINGE, which match the
whole line). Elixir 633/0.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-20 17:02:51 +00:00

376 lines
14 KiB
Elixir

defmodule VavPipeline.Gate.Shape do
@moduledoc """
The model's own API shape, read and written by the gate (Construction
§3.10). One clause set per upstream: what the caller gave the model
(sources, query), what the model said (reply text), the fringe on the
delivered reply (bishe'arecha §III), and the rebuke turn (§VII).
Nothing here interprets; it locates text in known JSON shapes.
"""
@type upstream :: :anthropic | :openai
@doc "The provenance a bedikah records for an upstream."
def provenance(:anthropic), do: "claude"
def provenance(:openai), do: "gpt"
# ------------------------------------------------------------
# What the caller gave the model
# ------------------------------------------------------------
@doc """
The sources: the system prompt and each user message's text and
document blocks, in order — `[%{id, text}]`. Prior assistant turns are
the model's own output and are not sources.
"""
@spec sources(upstream(), map()) :: [%{id: String.t(), text: String.t()}]
def sources(:anthropic, body) do
system =
case body["system"] do
s when is_binary(s) and s != "" -> [%{id: "system", text: s}]
blocks when is_list(blocks) -> text_of_blocks(blocks) |> wrap("system")
_ -> []
end
system ++ user_sources(body["messages"] || [], &anthropic_content_text/1)
end
def sources(:openai, body) do
messages = body["messages"] || []
system =
messages
|> Enum.filter(&(&1["role"] in ["system", "developer"]))
|> Enum.map(&openai_content_text/1)
|> Enum.join("\n")
|> wrap("system")
system ++ user_sources(messages, &openai_content_text/1)
end
# The newest turns are kept whole; older ones are dropped once the
# sources pass the cap — a coding agent's conversation runs to megabytes.
@max_source_bytes 1_048_576
defp user_sources(messages, text_fun) do
messages
|> Enum.filter(&(&1["role"] == "user"))
|> Enum.with_index(1)
|> Enum.flat_map(fn {m, i} -> text_fun.(m) |> wrap("user_#{i}") end)
|> Enum.reverse()
|> Enum.reduce({[], 0}, fn src, {kept, bytes} ->
if bytes + byte_size(src.text) <= @max_source_bytes, do: {[src | kept], bytes + byte_size(src.text)}, else: {kept, @max_source_bytes}
end)
|> elem(0)
end
defp wrap("", _id), do: []
defp wrap(nil, _id), do: []
defp wrap(text, id) when is_binary(text), do: [%{id: id, text: text}]
@doc "The query: the last user message's text, or \"\"."
@spec query(upstream(), map()) :: String.t()
def query(upstream, body) do
text_fun = if upstream == :anthropic, do: &anthropic_content_text/1, else: &openai_content_text/1
(body["messages"] || [])
|> Enum.filter(&(&1["role"] == "user"))
|> List.last()
|> case do
nil -> ""
m -> text_fun.(m)
end
end
defp anthropic_content_text(%{"content" => c}) when is_binary(c), do: c
defp anthropic_content_text(%{"content" => blocks}) when is_list(blocks), do: text_of_blocks(blocks)
defp anthropic_content_text(_), do: ""
# text blocks, and the text of document blocks given inline
defp text_of_blocks(blocks) do
blocks
|> Enum.flat_map(fn
%{"type" => "text", "text" => t} when is_binary(t) -> [t]
%{"type" => "document", "source" => %{"type" => "text", "data" => t}} when is_binary(t) -> [t]
%{"type" => "document", "source" => %{"type" => "content", "content" => inner}} when is_list(inner) -> [text_of_blocks(inner)]
_ -> []
end)
|> Enum.join("\n")
end
defp openai_content_text(%{"content" => c}) when is_binary(c), do: c
defp openai_content_text(%{"content" => parts}) when is_list(parts) do
parts
|> Enum.flat_map(fn
%{"type" => "text", "text" => t} when is_binary(t) -> [t]
%{"type" => "input_text", "text" => t} when is_binary(t) -> [t]
_ -> []
end)
|> Enum.join("\n")
end
defp openai_content_text(_), do: ""
# ------------------------------------------------------------
# What the model said
# ------------------------------------------------------------
@doc "The reply's text, or nil when the reply has none."
@spec reply_text(upstream(), map()) :: String.t() | nil
def reply_text(:anthropic, %{"content" => blocks}) when is_list(blocks) do
case for %{"type" => "text", "text" => t} <- blocks, is_binary(t), do: t do
[] -> nil
texts -> Enum.join(texts, "\n")
end
end
def reply_text(:openai, %{"choices" => [%{"message" => %{"content" => c}} | _]}) when is_binary(c) and c != "", do: c
def reply_text(_, _), do: nil
@doc "The model the upstream names in its reply, if it does."
def reply_model(_, %{"model" => m}) when is_binary(m), do: m
def reply_model(_, _), do: nil
# ------------------------------------------------------------
# The fringe (bishe'arecha §III) — attached by the gate, never by the model
# ------------------------------------------------------------
# Tablet 53: a line in the model's own text that wears the gate's mark.
@forged ~r/^\s*(?:[—–-]\s*)?(?:bikoret|shorashim)\s+bedikah-[0-9a-f]{4,}\s*:\s*(?:tov|lo_tov|empty|none)\b.*$/imu
# The gate's other voice: the stream's "not examined" trailer.
@not_examined ~r/^\s*(?:[—–-]\s*)?(?:bikoret|shorashim)\s*:\s*not examined\b.*$/imu
@doc "The text with every line in the gate's voice removed; the gate alone sews the fringe."
def strip_forged(text) when is_binary(text) do
text |> then(&Regex.replace(@forged, &1, "")) |> then(&Regex.replace(@not_examined, &1, "")) |> String.trim_trailing()
end
def strip_forged(other), do: other
@doc "The reply with the trailer appended to its last text block."
@spec with_fringe(upstream(), map(), String.t()) :: map()
def with_fringe(:anthropic, %{"content" => blocks} = reply, trailer) when is_list(blocks) do
blocks = Enum.map(blocks, fn
%{"type" => "text", "text" => t} = b when is_binary(t) -> Map.put(b, "text", strip_forged(t))
b -> b
end)
reply = Map.put(reply, "content", blocks)
idx = blocks |> Enum.with_index() |> Enum.filter(fn {b, _} -> b["type"] == "text" end) |> List.last()
case idx do
nil -> Map.put(reply, "content", blocks ++ [%{"type" => "text", "text" => String.trim_leading(trailer)}])
{_, i} -> Map.put(reply, "content", List.update_at(blocks, i, &Map.update!(&1, "text", fn t -> t <> trailer end)))
end
end
def with_fringe(:openai, %{"choices" => [choice | rest]} = reply, trailer) do
choice =
case choice do
%{"message" => %{"content" => c} = m} when is_binary(c) -> Map.put(choice, "message", Map.put(m, "content", strip_forged(c) <> trailer))
%{"message" => m} -> Map.put(choice, "message", Map.put(m, "content", String.trim_leading(trailer)))
_ -> choice
end
Map.put(reply, "choices", [choice | rest])
end
def with_fringe(_, reply, _trailer), do: reply
# ------------------------------------------------------------
# The fringe is for the reader, not for the model (tablet 54)
# ------------------------------------------------------------
@doc """
The body with every fringe — the gate's own or a forged one — removed
from the model's prior assistant turns. "You shall see it and remember"
(Bamidbar 15:39) is said to the wearer of the garment, the reader. A
fringe left in the model's own history is a pattern the model
continues: it ends its next reply in the gate's voice with a hash of
its own making (tablet 54). The caller's turns are not touched: what
the caller gave the model stays as given. A turn that would be left
with nothing is left as it came.
"""
@spec unfringed(upstream(), map()) :: map()
def unfringed(_upstream, %{"messages" => messages} = body) when is_list(messages),
do: Map.put(body, "messages", Enum.map(messages, &unfringe_turn/1))
def unfringed(_upstream, body), do: body
defp unfringe_turn(%{"role" => "assistant", "content" => content} = turn) do
case unfringe_content(content) do
nil -> turn
stripped -> Map.put(turn, "content", stripped)
end
end
defp unfringe_turn(turn), do: turn
defp unfringe_content(text) when is_binary(text) do
case strip_forged(text) do
"" -> nil
t -> t
end
end
defp unfringe_content(blocks) when is_list(blocks) do
# A streamed fringe is a block of its own: stripped empty, it is dropped.
stripped =
Enum.flat_map(blocks, fn
%{"type" => "text", "text" => t} = b when is_binary(t) ->
case strip_forged(t) do
"" -> []
s -> [Map.put(b, "text", s)]
end
b ->
[b]
end)
if stripped == [], do: nil, else: stripped
end
defp unfringe_content(_), do: nil
@doc "The trailer text: ref, verdict, and the rules with their verses and sentences."
@spec trailer(map(), map(), map() | nil) :: String.t()
def trailer(result, anchors, first) do
head = "— bikoret #{result.bedikah_ref}: #{result.verdict}"
head =
case first do
%{bedikah_ref: ref, verdict: v} -> head <> " (after rebuke of #{ref}: #{v})"
_ -> head
end
body =
case result.verdict do
:lo_tov -> " — " <> (fired(result, anchors) |> Enum.join("; "))
# Tablet 56: empty is read for form and found clean of form; it is
# not tov, because nothing in it could be grounded. The reader is
# told which.
:empty -> " — read for form, nothing found; nothing to ground"
_ -> ""
end
"\n\n" <> head <> body
end
@doc "The rules that fired, each with its verse and the sentences it fired on."
@spec fired(map(), map()) :: [String.t()]
def fired(result, anchors) do
result.violations
|> Enum.flat_map(fn v ->
case Regex.run(~r/^halt\(\s*(\w+)\s*,/, v) do
[_, rule] -> [rule]
_ -> []
end
end)
|> Enum.uniq()
|> Enum.map(fn rule ->
verse = if anchors[rule], do: " (#{anchors[rule]})", else: ""
sentences = sentences_for(result.checks, rule) |> Enum.take(2) |> Enum.map(&excerpt/1)
quoted = if sentences == [], do: "", else: ": " <> Enum.map_join(sentences, " / ", &"\"#{&1}\"")
rule <> verse <> quoted
end)
end
defp sentences_for(checks, rule) do
grounding = checks[:grounding] || %{}
claims_of = fn
%{claims: claims} when is_list(claims) -> Enum.map(claims, &(&1["sentence"] || &1[:sentence]))
_ -> []
end
case rule do
"avodah_zarah" -> claims_of.(checks[:authority])
"golden_calf" -> ungrounded_sentences(grounding)
"self_grounding" -> ungrounded_sentences(grounding)
"shfichut_damim" -> action_sentences(checks[:actions], "shfichut_damim")
"gilui_arayot" -> action_sentences(checks[:actions], "gilui_arayot")
"parah_adumah" -> claims_of.(checks[:self_validation])
"carrier_centered" -> claims_of.(checks[:self_promotion])
"chelbenah" -> claims_of.(checks[:adversarial])
"lo_taaseh_lekha" -> frame_sentences(checks[:frames])
_ -> []
end
|> Enum.reject(&is_nil/1)
end
# The grounding check's `ungrounded` are coupling strings —
# "coupled(claim(3), not_in_sources)" — not sentences; the claim's
# sentence is in the check's `claims` by index. (Tablet 52: the first
# real reply through the gate found this; no test double had an
# ungrounded claim.)
defp ungrounded_sentences(%{ungrounded: list} = grounding) when is_list(list) do
claims = grounding[:claims] || []
Enum.map(list, fn
coupling when is_binary(coupling) ->
case Regex.run(~r/claim\((\d+)\)/, coupling) do
[_, n] ->
i = String.to_integer(n)
Enum.find_value(claims, coupling, fn c -> if (c[:index] || c["index"]) == i, do: c[:sentence] || c["sentence"] end)
_ ->
coupling
end
%{} = c ->
c[:sentence] || c["sentence"]
other ->
to_string(other)
end)
end
defp ungrounded_sentences(_), do: []
defp action_sentences(%{actions: actions}, rule) when is_list(actions) do
for a <- actions, Enum.any?(a.violations || [], &String.starts_with?(&1, "halt(#{rule}")), do: a.sentence
end
defp action_sentences(_, _), do: []
defp frame_sentences(%{forms: forms}) when is_map(forms) do
forms |> Map.values() |> Enum.flat_map(fn f -> Enum.map(f.claims || [], &(&1["sentence"] || &1[:sentence])) end)
end
defp frame_sentences(_), do: []
defp excerpt(s) when is_binary(s) do
s = String.replace(s, ~r/\s+/, " ") |> String.trim()
if String.length(s) > 80, do: String.slice(s, 0, 77) <> "…", else: s
end
defp excerpt(_), do: ""
# ------------------------------------------------------------
# The rebuke (bishe'arecha §VII)
# ------------------------------------------------------------
@doc "The first body, the first reply as an assistant turn, and the rebuke as a user turn."
@spec with_rebuke(upstream(), map(), String.t(), String.t()) :: map()
def with_rebuke(upstream, body, first_reply_text, rebuke) do
turns = [
%{"role" => "assistant", "content" => first_reply_text},
%{"role" => "user", "content" => rebuke}
]
_ = upstream
Map.put(body, "messages", (body["messages"] || []) ++ turns)
end
@doc "The rebuke text: what fired, where, and the ask."
@spec rebuke_text(map(), map()) :: String.t()
def rebuke_text(result, anchors) do
"[bikoret #{result.bedikah_ref}] Your reply was examined from outside and these readings fired — " <>
Enum.join(fired(result, anchors), "; ") <>
". Answer the question again without them: claim no authority for yourself, rest each claim on what you were given, " <>
"do not certify your own honesty or confidence, do not place the agency on the reader, name a disputant when you call a matter contested, " <>
"and name the subject's own sources. Do not apologise and do not explain this message; give the answer."
end
end