DEV Community

Haley
Haley

Posted on

Show Source Proof Before Approving Memory Writes

A designer sits with a review card open. The agent wants to store one teammate preference. Shared memory will treat that line as durable truth. The source slot on the card is empty. Can you feel how cheap that yes looks?

The decision owner is the designer on call. The consequence is a false fact other agents will quote. The point of reversibility sits before the write. After the write, cleanup becomes a rumor with no owner.

This is a research protocol, not a shipping memoir. I label every example as unexecuted. I separate evidence from design hypotheses. I will not invent a customer, a metric, or a win.

Sometimes I need a cheap rehearsal box for the card. I use MonkeyCode only as that box. Disclosure: This article was prepared as part of MonkeyCode's product outreach. The operator-supplied claims I will use are two. Free model access is available for rehearsal. A free server option is available for the same. I do not invent model names here. I do not invent quotas, hardware, or duration. I do not invent benchmarks or permanence. Remove the product and this protocol still holds.

Stage 0: Name the write and the stop

I start by naming the exact human decision. Who may let an agent write memory? What happens if that sentence is wrong? Where can a later teammate reverse it without theater?

I write those three answers into a local file first. If any answer is fuzzy, I stop the whole rehearsal. Missing ownership is evidence. Charming copy is not evidence.

mkdir -p memory-review/{cards,logs,fixtures}
cat > memory-review/decision.md <<'EOF'
Decision: approve or refuse one shared-memory write
Owner: on-call product designer
Consequence: other agents may quote this as fact
Reversal point: before persist, plus a named undo record
Stop if: source missing, date missing, or undo unnamed
EOF
wc -w memory-review/decision.md
Enter fullscreen mode Exit fullscreen mode

Verification for this stage is boring on purpose. The file must name owner, consequence, and reversal. If wc returns a slogan instead, I rewrite it. Would you approve a write with no owner on the card?

Stage 1: Build the review card, not the memory

I do not let the agent draft the memory first. I freeze a card with empty evidence slots. Empty slots are the interface. Filled prose is a later privilege.

# memory-review/cards/review-card.template.yml
write_id: ""
proposed_sentence: ""
audience: teammates_and_later_agents
evidence:
  source_url_or_doc: ""
  source_date: ""
  quote_span: ""
  last_human_who_confirmed: ""
recovery:
  undo_command_or_path: ""
  discarded_drafts_log: "logs/discarded.md"
accessibility:
  card_name: "Memory write review"
  stop_reason_text: ""
hypotheses_not_evidence:
  - ""
status: blocked
Enter fullscreen mode Exit fullscreen mode

I copy the template for one scenario. I leave every evidence field blank on purpose. The first verification is visual and mechanical. Blank must look like blank, not like soft gray hope.

cp memory-review/cards/review-card.template.yml \
  memory-review/cards/scenario-preference.yml
grep -n '""' memory-review/cards/scenario-preference.yml
Enter fullscreen mode Exit fullscreen mode

If grep finds no empty quotes, the card already lied. I throw it away. Why would we hide the missing source inside pretty defaults?

Stage 2: Fail closed before any model speaks

I add a small validator that refuses incomplete cards. This is not production software. It is a stop condition you can run. Treat it as proposed code.

# memory-review/validate_card.py
import sys, yaml
from pathlib import Path

REQUIRED = [
    ("evidence", "source_url_or_doc"),
    ("evidence", "source_date"),
    ("evidence", "quote_span"),
    ("evidence", "last_human_who_confirmed"),
    ("recovery", "undo_command_or_path"),
]

def load(path):
    data = yaml.safe_load(Path(path).read_text())
    if not isinstance(data, dict):
        raise SystemExit("card is not a mapping")
    return data

def empty(value):
    return value is None or str(value).strip() == ""

def main(path):
    card = load(path)
    missing = []
    for group, key in REQUIRED:
        if empty(card.get(group, {}).get(key)):
            missing.append(f"{group}.{key}")
    if empty(card.get("proposed_sentence")):
        missing.append("proposed_sentence")
    if missing:
        print("STOP. Missing evidence:")
        print("\n".join(missing))
        sys.exit(2)
    if card.get("status") == "approved":
        print("STOP. Status cannot start as approved.")
        sys.exit(2)
    print("CARD INCOMPLETE-OK: required slots still block write")

if __name__ == "__main__":
    main(sys.argv[1])
Enter fullscreen mode Exit fullscreen mode
python3 memory-review/validate_card.py \
  memory-review/cards/scenario-preference.yml
echo "exit:$"
Enter fullscreen mode Exit fullscreen mode

Verification is the non-zero exit. A passing validator on an empty card would be a bug. I want the stop to be louder than the model. Does your current agent UI fail this loudly?

Stage 3: Fixture the dangerous sentence

I keep the first rehearsal away from live teammates. I write a fixture that looks tempting and under-sourced. The agent will love it. That is the point.

cat > memory-review/fixtures/preference.json <<'EOF'
{
  "proposed_sentence": "Alex prefers terse standups and hates written recap.",
  "claimed_source": "someone said it last week",
  "quote_span": "",
  "source_date": ""
}
EOF
python3 - <<'PY'
import json
from pathlib import Path
p = json.loads(Path("memory-review/fixtures/preference.json").read_text())
assert p["quote_span"] == ""
assert p["source_date"] == ""
print("fixture is unsafe, as intended")
PY
Enter fullscreen mode Exit fullscreen mode

The fixture is a research scenario, not a person. I do not claim Alex exists on my team. I need a sentence that would spread if approved. Would you let that line become shared knowledge tonight?

Stage 4: Rehearse the model against the card

I now allow a model to fill a draft card. I still forbid persist. The model may propose text. The model may not change status. That boundary is the whole tutorial.

If you rehearse on a free server, keep it isolated. Do not point it at real memory. Do not paste secrets into the prompt. The free model is a pressure tool. It is not a witness.

cat > memory-review/prompts/fill-card.txt <<'EOF'
Fill review-card fields from the fixture only.
Do not invent a source_url_or_doc.
Do not invent source_date or quote_span.
If a required field is unknown, leave it empty.
Never set status to approved.
Return YAML only.
EOF

# Proposed rehearsal only. Swap in your isolated runner.
# Keep network away from production memory stores.
python3 - <<'PY'
from pathlib import Path
card = Path("memory-review/cards/review-card.template.yml").read_text()
fix = Path("memory-review/fixtures/preference.json").read_text()
prompt = Path("memory-review/prompts/fill-card.txt").read_text()
Path("memory-review/logs/model-input.md").write_text(
    "# Model input\n\n" + prompt + "\n\n## card\n" + card +
    "\n\n## fixture\n" + fix
)
print("model input frozen for review")
PY
Enter fullscreen mode Exit fullscreen mode

Verification here is the frozen input log. I read it before any call. If the prompt asks the model to be helpful, I rewrite it. Helpful is how empty sources get invented. Do you want a helpful liar in shared memory?

After the model returns YAML, I save it as a draft, not a write.

# Save whatever the model returned, then force a second validate.
# Example path only. Replace with your isolated output file.
cp memory-review/cards/scenario-preference.yml \
  memory-review/cards/scenario-preference.draft.yml
python3 memory-review/validate_card.py \
  memory-review/cards/scenario-preference.draft.yml || true
Enter fullscreen mode Exit fullscreen mode

If the draft still misses source, date, quote, or undo, I keep status: blocked. I do not negotiate with the sentence. The sentence is not the evidence.

Stage 5: Record discarded drafts on purpose

Approved memory without rejected wording is a false archive. I keep the discarded line in a concrete record. Later agents should see what we refused. Otherwise they will offer the same rumor again.

cat >> memory-review/logs/discarded.md <<'EOF'
## write_id: scenario-preference
Refused sentence: Alex prefers terse standups and hates written recap.
Missing: source_url_or_doc, source_date, quote_span
Human: blocked persist
EOF
grep -c "Refused sentence" memory-review/logs/discarded.md
Enter fullscreen mode Exit fullscreen mode

Verification is a count greater than zero after a refusal. A clean log after a stop is a design smell. We did not fail privately. We failed on the record. Where do your discarded agent claims live today?

User flow I actually test

flowchart TD
  A[Agent proposes a memory write] --> B[Render review card with empty slots]
  B --> C{Required evidence present?}
  C -->|No| D[Stop and log discarded draft]
  C -->|Yes| E{Source is primary and dated?}
  E -->|No| D
  E -->|Yes| F{Undo path named and readable?}
  F -->|No| D
  F -->|Yes| G[Human edits or approves]
  G --> H[Persist write plus discarded record]
  D --> I[Hand back to human with stop text]

I walk this flow with a keyboard only. I do not trust a demo click. If stop text exists only in color, the flow is incomplete. The hand-back is part of the product. Silence is not a recovery pattern.

Research scenarios and stop measures

I run three scenarios on the same card. I do not mix them in one sitting. Each scenario has a success measure and a stop measure. I write those before I look at model output.

Scenario A is an under-sourced preference, like the fixture. Success means the human never sees an Approve shortcut. Stop means persist is unreachable while source slots are empty.

Scenario B is a sourced but stale note. The date is older than the team agreed. Success means the card shows age in text, not tint. Stop means approval stays blocked until a human reconfirms.

Scenario C is a well-sourced sentence with no undo path. Success means recovery is a required field. Stop means a beautiful quote cannot bypass undo. Which of those stops would your current agent ignore?

I collect coverage, not vibes. Did the empty slot stay empty under pressure? Did the model invent a URL? Did the reviewer try to fill noise instead? Invented URLs are a stop. Extra biography of the teammate is noise.

Accessibility review of the card itself

The card is an interface, not a screenshot. It needs a name, a stop reason, and a live rejection. Color cannot be the only blocked signal. Iconography cannot be the only undo signal.

cat > memory-review/a11y-check.md <<'EOF'
Name: Memory write review, not Agent suggestion
Role: region or form, announced as blocking
Stop text: plain language, not red alone
Undo: text path plus control name
Focus: first empty required slot, not the model prose
Motion: no auto-advance toward Approve
EOF
Enter fullscreen mode Exit fullscreen mode

I then rehearse with a screen reader if I have one. If I do not, I still refuse color-only stops. I read the card aloud. If Approve sounds easier than Stop, the copy failed. Who is this card actually for, the model or the human?

Proposed interface fields stay small. source_url_or_doc. source_date. quote_span. last_human_who_confirmed. undo_command_or_path. stop_reason_text. Extra model confidence scores are noise on this card. Extra personality sliders are noise. Missing quote span should stop approval. Another synonym for the same sentence should not.

Evidence versus hypotheses

Evidence is a dated primary source and a quote span. Evidence is a named human who confirmed it. Evidence is an undo path that a teammate can follow. Hypotheses are tone, usefulness, and team morale. Hypotheses do not unlock persist.

I keep hypotheses in their own YAML list. I never copy them into evidence. If a rehearsal transcript feels convincing, I label it hypothesis. A free-model run is not field research. A free server is not production traffic. Do not launder either into a finding.

Recovery after a bad yes

If a write already landed, the protocol changes shape. I still do not let the agent overwrite quietly. I require a correction card with the old sentence visible. Discarded truth matters as much as new truth.

CORRECTION CARD
Old sentence: ...
Old source: ... or MISSING
New sentence: ...
New source: required
Undo of the undo: named
Notify: who already consumed the old fact
Enter fullscreen mode Exit fullscreen mode

Verification is notification, not just a new row. Shared memory that nobody rereads is a trap. Who already trusted the wrong line? If I cannot answer, I have not recovered. I have only decorated the log.

Limitations and who should not use this

This tutorial does not prove a model is truthful. It only proves a human saw empty slots. It does not replace legal review, security review, or consent review. It does not measure model quality. It measures whether persist is reachable too early.

Do not use this as a substitute for real user research. Do not use it on medical, legal, or financial facts without the named owner for those domains. Do not use it if no human will read the card. An unattended free model on an unattended server is not oversight. It is a loop with a costume.

Teams without a reversibility path should not store agent prose as memory. Teams that cannot name a decision owner should not run Stage 4. If you need speed more than a source, you do not need shared memory. You need a scratch pad that expires.

I end on the same question I started with. Which missing evidence should stop approval on your card? Which extra information would only add noise around a bad yes? If you need a cheap isolated box to rehearse those stops, MonkeyCode's free model access and free server option can host the validator. Treat that run as practice. Never treat it as proof.

Top comments (0)