persistaibench 0.1.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. persistaibench-0.1.0/LICENSE +21 -0
  2. persistaibench-0.1.0/PKG-INFO +141 -0
  3. persistaibench-0.1.0/README.md +130 -0
  4. persistaibench-0.1.0/persistaibench.egg-info/PKG-INFO +141 -0
  5. persistaibench-0.1.0/persistaibench.egg-info/SOURCES.txt +28 -0
  6. persistaibench-0.1.0/persistaibench.egg-info/dependency_links.txt +1 -0
  7. persistaibench-0.1.0/persistaibench.egg-info/entry_points.txt +2 -0
  8. persistaibench-0.1.0/persistaibench.egg-info/top_level.txt +1 -0
  9. persistaibench-0.1.0/persistbench/__init__.py +6 -0
  10. persistaibench-0.1.0/persistbench/agents.py +95 -0
  11. persistaibench-0.1.0/persistbench/chains.py +351 -0
  12. persistaibench-0.1.0/persistbench/cli.py +208 -0
  13. persistaibench-0.1.0/persistbench/faults.py +55 -0
  14. persistaibench-0.1.0/persistbench/g2_smoke.py +808 -0
  15. persistaibench-0.1.0/persistbench/llm_agent.py +954 -0
  16. persistaibench-0.1.0/persistbench/memory.py +392 -0
  17. persistaibench-0.1.0/persistbench/pilot.py +374 -0
  18. persistaibench-0.1.0/persistbench/runner.py +142 -0
  19. persistaibench-0.1.0/persistbench/scorer.py +113 -0
  20. persistaibench-0.1.0/persistbench/world.py +138 -0
  21. persistaibench-0.1.0/pyproject.toml +24 -0
  22. persistaibench-0.1.0/setup.cfg +4 -0
  23. persistaibench-0.1.0/tests/test_cli.py +59 -0
  24. persistaibench-0.1.0/tests/test_dependents.py +132 -0
  25. persistaibench-0.1.0/tests/test_exit_criterion.py +150 -0
  26. persistaibench-0.1.0/tests/test_g2_smoke.py +359 -0
  27. persistaibench-0.1.0/tests/test_llm_dependent.py +385 -0
  28. persistaibench-0.1.0/tests/test_llm_pilot.py +396 -0
  29. persistaibench-0.1.0/tests/test_week2_policies.py +389 -0
  30. persistaibench-0.1.0/tests/test_world.py +67 -0
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Karmendra Pandey
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,141 @@
1
+ Metadata-Version: 2.4
2
+ Name: persistaibench
3
+ Version: 0.1.0
4
+ Summary: PersistAIBench: an evaluation benchmark for AI agents' memory truthfulness over long multi-step tasks — cross-session propagation of silent agent failures through long-term memory
5
+ License: MIT
6
+ Project-URL: Repository, https://github.com/karmendra8386/persistbench-bench
7
+ Requires-Python: >=3.11
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Dynamic: license-file
11
+
12
+ # PersistAIBench
13
+
14
+ **An evaluation benchmark for AI agents' memory truthfulness over long multi-step tasks.**
15
+
16
+ PersistAIBench (harness/repo name: PersistBench) measures one unmeasured chain: a tool fails silently in Session 1 → the agent writes its own account of the outcome into long-term memory → different tasks in Sessions 2–4 act on that memory → whether a write-time guard stops the propagation. There is no attacker and no prompt injection; the failure mode is benign self-poisoning.
17
+
18
+ Paper: Karmendra Pandey, *"Fail-Plausible, Then Persistent"* (working title). Outline approved 2026-10-23; filed in Google Drive EB1 §03 (`persistbench-outline.pdf`, `persistbench-outline-record-2026-10-23.md`).
19
+
20
+ ## Quickstart
21
+
22
+ ```bash
23
+ git clone https://github.com/karmendra8386/persistbench-bench.git
24
+ cd persistbench-bench
25
+ python3 -m venv .venv
26
+ .venv/bin/pip install .
27
+ .venv/bin/persistaibench run --chain chain01 --policy M1 --fault silent_wrong_value --backend fake
28
+ ```
29
+
30
+ The last command runs one pilot cell (chain × fault × policy) end-to-end and prints the scored cell as JSON. `--backend fake` is a deterministic scripted backend — a plumbing smoke test, **not a result**: the model id in the output is stamped `fake/...` so it can never be mistaken for a real model run. `--chain` accepts a full chain id or a unique prefix (`chain01`); `--policy` is one of M0–M5; `--fault` one of the five fault conditions.
31
+
32
+ To run a cell with a real model (requires a GPU and `torch` + `transformers`, which the Kaggle notebook installs):
33
+
34
+ ```bash
35
+ .venv/bin/persistaibench run --chain chain01 --policy M1 --fault silent_wrong_value --backend hf --model Qwen/Qwen2.5-7B-Instruct
36
+ ```
37
+
38
+ To run the full pilot grid (2 models × 6 chains × 5 faults × M0–M5), use `persistaibench pilot --config configs/pilot.json --out results/pilot --backend hf` — or `--backend fake` for a plumbing-only pass. The Week 2 pilot results are in `docs/results.md`; the design rule they support is written up in `docs/verified-memory-writes.md`.
39
+
40
+ ## Provenance — read this first
41
+
42
+ **Weeks 1–2 = harness, design, policy machinery, and the Week 2 formation pilot.** The pilot ran on 2026-11-02 (Kaggle, GPU T4 x2) and its results — including a failed model arm, reported as such — are in **`docs/results.md`** with raw outputs in `results/pilot/`. Everything before the pilot (scripted-agent evidence below) is machinery validation, not model findings. What exists:
43
+
44
+ - `docs/design-freeze.md` — the frozen design: RQ1–RQ5 / H1–H5, environment, fault conditions, memory policies M0–M5, six metrics, paired statistics plan.
45
+ - `docs/preregistration-draft.md` — hypotheses, conditions, primary metrics, analysis plan, and the four pre-registered falsification checks.
46
+ - `docs/results.md` — the Week 2 pilot: formation rates by policy × model and by fault, the classifier-gate result, and the Qwen3-8B write-generation anomaly.
47
+ - `docs/verified-memory-writes.md` — the transferable design rule the pilot supports: verify memory writes against current state, not against the observation that produced them.
48
+ - A working deterministic harness with scripted agents, validated by the test suite below.
49
+ - **Week 2 (code half, complete):** memory policies **M0–M5 all implemented** under the frozen interface — M2 (self-summary + disclosure/provenance annotation), M3 (structured evidence-contract write), M4 (state-verified write with correction/rejection accounting) join Week 1's M0/M1/M5. Every policy records a per-write log (entries + cost placeholders) sufficient to compute the frozen metrics, and `runner.run_chain` executes a full chain under any policy × any fault.
50
+ - **Week 2 pilot execution package:** `persistbench/llm_agent.py` (LLM origin agent behind a backend interface — deterministic fake backend for tests, HF transformers backend for real models; conservative JSON action parsing and claim extraction), real per-policy memory-write generation (M1 free-form summary, M2 under the disclosure instruction, M3 claims parsed into the evidence-contract format — unparseable claims are logged as `unknown`, never verified; M4 = model write through the state-verified policy), `persistbench/pilot.py` + `configs/pilot.json` + `scripts/run_pilot.py` (2 models × 6 chains × 5 faults × M0–M5, per-cell md/csv/jsonl outputs, and the 50-write hand-labelling sample export for the ≥90%-agreement classifier gate), the `persistaibench` CLI (`persistbench/cli.py`), and `kaggle/week2-pilot.ipynb` + `kaggle/RUNBOOK.md`. Plumbing was additionally smoke-tested with a real tiny model from the local cache (Qwen2.5-0.5B-Instruct, CPU): see `results/smoke-llm-2026-10-23.md` — labelled SMOKE, not a result.
51
+
52
+ Two implementation notes that matter for reading the machinery (both follow the design freeze; flagged here so they are not mistaken for findings):
53
+
54
+ - **M3's evidence is tool observations, never DB state** (checking state is M4's definition). Under explicit-error and empty/partial faults the contract therefore stores no false claim as fact. Under silent-wrong-value and stale faults, the corrupted response *is* the supporting observation, so M3 marks the false claim `verified` — the contract's boundary, asserted in the tests, and exactly why H3 orders M3 below M4.
55
+ - **M4 corrects rather than merely rejects** where the DB holds a value (rejection is reserved for facts the DB does not hold and for unverifiable keys). Its write log accounts for every falsified field as corrected-or-rejected, and on no-fault writes it corrects/rejects nothing — pre-registration falsification check 3 (a guard that destroys legitimate utility is not viable) is tested directly.
56
+
57
+ The full live-model runs (Qwen2.5-7B, Qwen3-8B, Llama-3.1-8B, Mistral-7B; ~3,600 sessions) are Weeks 3–4 of the plan. Numbers from the Week 2 pilot are in `docs/results.md`; any other number quoted from this repo is a property of the scripted harness, not of any model.
58
+
59
+ ## Layout
60
+
61
+ ```
62
+ persistbench/
63
+ world.py deterministic retail world; DB ground truth; agent tools
64
+ chains.py the 12 task chains (Session 1 + 3 dependents + correction fact)
65
+ faults.py seeded fault injection: no_fault / explicit_error /
66
+ empty_partial / silent_wrong_value / stale_value
67
+ agents.py scripted oracle + naive origin agents; dependent agent
68
+ memory.py memory policies M0–M5 (all implemented, Week 2) + write logs
69
+ scorer.py programmatic grading vs DB end-state — no LLM judge
70
+ runner.py origin / dependent trajectory runners
71
+ llm_agent.py LLM origin agent + per-policy write generation (Week 2
72
+ pilot prep; fake + HF transformers backends)
73
+ pilot.py pilot config validation, grid runner, formation tables,
74
+ hand-labelling sample export + agreement gate
75
+ cli.py the `persistaibench` command (run one cell / run the pilot)
76
+ configs/ pilot.json — the frozen pilot configuration
77
+ scripts/ run_pilot.py — pilot entry point (the CLI wraps the same code)
78
+ kaggle/ week2-pilot.ipynb + RUNBOOK.md (launch procedure)
79
+ results/ smoke evidence (labelled); pilot/ — Week 2 pilot outputs
80
+ tests/ pytest suite, incl. the Week 1 exit criterion
81
+ docs/ design freeze + pre-registration draft; results.md —
82
+ Week 2 pilot results; verified-memory-writes.md — the
83
+ design rule
84
+ ```
85
+
86
+ ## Week 1 exit criterion — evidence
87
+
88
+ > The harness grades hand-written/scripted trajectories correctly in all 5 fault conditions.
89
+
90
+ Verified by `tests/test_exit_criterion.py` (plus `test_world.py`, `test_dependents.py`):
91
+
92
+ - Oracle agent under no-fault: task success + truthful claim on **12/12 chains**.
93
+ - Naive (unverifying) agent: scorer detects a falsified claim under **each of the 4 injected faults × 12 chains (48/48)**, and names the falsified fields.
94
+ - Oracle agent: claim **never falsified under any fault (60/60 chain × fault cases)**.
95
+ - The dissociation the paper rests on is mechanically demonstrated: silent/stale faults leave the DB correct but the naive claim false (24/24); explicit/empty faults fail both the task and the claim (24/24).
96
+ - Propagation machinery: an M1 write of the naive claim is classified false on the corrupted fact (12/12); the dependent task that needs that fact then fails under M1 memory and succeeds under M5 (oracle) and M0 (query the world); the pre-Session-4 correction event repairs a revising agent and not a stubborn one (12/12).
97
+ - Hand-written false trajectory (claims success for work never executed) is detected, including the `task_success` falsification.
98
+
99
+ Run it:
100
+
101
+ ```bash
102
+ python3 -m venv .venv && .venv/bin/pip install pytest
103
+ .venv/bin/python -m pytest -q # 1164 passed, 10 skipped (skips: Session-2 task
104
+ # does not depend on that chain's corrupted fact)
105
+ ```
106
+
107
+ Standard library only at run time; pytest is the sole dev dependency.
108
+
109
+ ## Week 2 policy evidence (machinery, not results)
110
+
111
+ Verified by `tests/test_week2_policies.py` (811 cases) over all 12 chains × 5 fault conditions, with scripted agents:
112
+
113
+ - **M4:** the stored write is never falsified, under any fault, for either scripted agent; its content matches the M5 oracle store; its log's corrected+rejected entries are exactly the scorer's falsified fields; on no-fault writes it accepts everything and its store equals M1's.
114
+ - **M3:** under explicit-error / empty-partial faults the whole write classifies clean (success claims stored `failed` / `unknown`, never verified); under no fault every claim is verified and downstream behaviour matches M1. Under silent/stale faults the false claim is stored `verified` on the corrupted observation's evidence — the documented boundary above.
115
+ - **M2:** the claim is stored verbatim with provenance on every entry (`verified_against_state: False` throughout); observed tool errors appear in the disclosures, and a success claim past an observed error is annotated `contradicted_by_observation`. Downstream behaviour matches M1 exactly — the annotation, not filtering, is M2's guard.
116
+ - **Correction:** where a guard kept the false memory out (M3/M4 under evidence-visible faults; M4 under all faults), even a stubborn agent's Session 4 is correct. Where the lie reached the store (M2/M3 under silent/stale), the Week-1 pattern holds: stubborn resists, revising is repaired.
117
+
118
+ ## Next (per the approved plan)
119
+
120
+ - **Week 2 pilot — DONE (2026-11-02):** 2 models × 6 chains × 5 faults × M0–M5, Session-1 formation rates measured; classifier gate passed (50/50 = 100% hand-label agreement). Results: `docs/results.md`. One arm (Qwen3-8B) produced almost no parseable writes — fixing its write generation (token cap / `<think>` handling) and re-piloting that arm, or replacing it, is a precondition for Week 3; scale-up is not cleared as a 2-model run until then.
121
+ - **Weeks 3–4:** full runs; correction analysis; first results memo.
122
+ - **Month 2:** LongMemEval + Mem0 transfer arms; write-up. Zenodo preprint week 8–10 after author review; NeurIPS 2027 Datasets & Benchmarks is the primary venue target.
123
+
124
+ ## Cite
125
+
126
+ If you use PersistAIBench in your work, please cite the software record on Zenodo:
127
+
128
+ ```bibtex
129
+ @software{pandey_persistaibench_2026,
130
+ author = {Pandey, Karmendra},
131
+ title = {PersistAIBench: an evaluation benchmark for AI agents' memory truthfulness over long multi-step tasks},
132
+ version = {0.1.0},
133
+ doi = {10.5281/zenodo.23893975},
134
+ url = {https://github.com/karmendra8386/persistbench-bench},
135
+ date = {2026-11-02}
136
+ }
137
+ ```
138
+
139
+ A `CITATION.cff` is included for automated citation tooling.
140
+
141
+ **DOI:** the v0.1.0 software release is archived on Zenodo as [10.5281/zenodo.23893975](https://doi.org/10.5281/zenodo.23893975) (concept DOI: [10.5281/zenodo.23893974](https://doi.org/10.5281/zenodo.23893974), which always resolves to the latest version). The accompanying paper (*"Fail-Plausible, Then Persistent"*, working title) is unpublished.
@@ -0,0 +1,130 @@
1
+ # PersistAIBench
2
+
3
+ **An evaluation benchmark for AI agents' memory truthfulness over long multi-step tasks.**
4
+
5
+ PersistAIBench (harness/repo name: PersistBench) measures one unmeasured chain: a tool fails silently in Session 1 → the agent writes its own account of the outcome into long-term memory → different tasks in Sessions 2–4 act on that memory → whether a write-time guard stops the propagation. There is no attacker and no prompt injection; the failure mode is benign self-poisoning.
6
+
7
+ Paper: Karmendra Pandey, *"Fail-Plausible, Then Persistent"* (working title). Outline approved 2026-10-23; filed in Google Drive EB1 §03 (`persistbench-outline.pdf`, `persistbench-outline-record-2026-10-23.md`).
8
+
9
+ ## Quickstart
10
+
11
+ ```bash
12
+ git clone https://github.com/karmendra8386/persistbench-bench.git
13
+ cd persistbench-bench
14
+ python3 -m venv .venv
15
+ .venv/bin/pip install .
16
+ .venv/bin/persistaibench run --chain chain01 --policy M1 --fault silent_wrong_value --backend fake
17
+ ```
18
+
19
+ The last command runs one pilot cell (chain × fault × policy) end-to-end and prints the scored cell as JSON. `--backend fake` is a deterministic scripted backend — a plumbing smoke test, **not a result**: the model id in the output is stamped `fake/...` so it can never be mistaken for a real model run. `--chain` accepts a full chain id or a unique prefix (`chain01`); `--policy` is one of M0–M5; `--fault` one of the five fault conditions.
20
+
21
+ To run a cell with a real model (requires a GPU and `torch` + `transformers`, which the Kaggle notebook installs):
22
+
23
+ ```bash
24
+ .venv/bin/persistaibench run --chain chain01 --policy M1 --fault silent_wrong_value --backend hf --model Qwen/Qwen2.5-7B-Instruct
25
+ ```
26
+
27
+ To run the full pilot grid (2 models × 6 chains × 5 faults × M0–M5), use `persistaibench pilot --config configs/pilot.json --out results/pilot --backend hf` — or `--backend fake` for a plumbing-only pass. The Week 2 pilot results are in `docs/results.md`; the design rule they support is written up in `docs/verified-memory-writes.md`.
28
+
29
+ ## Provenance — read this first
30
+
31
+ **Weeks 1–2 = harness, design, policy machinery, and the Week 2 formation pilot.** The pilot ran on 2026-11-02 (Kaggle, GPU T4 x2) and its results — including a failed model arm, reported as such — are in **`docs/results.md`** with raw outputs in `results/pilot/`. Everything before the pilot (scripted-agent evidence below) is machinery validation, not model findings. What exists:
32
+
33
+ - `docs/design-freeze.md` — the frozen design: RQ1–RQ5 / H1–H5, environment, fault conditions, memory policies M0–M5, six metrics, paired statistics plan.
34
+ - `docs/preregistration-draft.md` — hypotheses, conditions, primary metrics, analysis plan, and the four pre-registered falsification checks.
35
+ - `docs/results.md` — the Week 2 pilot: formation rates by policy × model and by fault, the classifier-gate result, and the Qwen3-8B write-generation anomaly.
36
+ - `docs/verified-memory-writes.md` — the transferable design rule the pilot supports: verify memory writes against current state, not against the observation that produced them.
37
+ - A working deterministic harness with scripted agents, validated by the test suite below.
38
+ - **Week 2 (code half, complete):** memory policies **M0–M5 all implemented** under the frozen interface — M2 (self-summary + disclosure/provenance annotation), M3 (structured evidence-contract write), M4 (state-verified write with correction/rejection accounting) join Week 1's M0/M1/M5. Every policy records a per-write log (entries + cost placeholders) sufficient to compute the frozen metrics, and `runner.run_chain` executes a full chain under any policy × any fault.
39
+ - **Week 2 pilot execution package:** `persistbench/llm_agent.py` (LLM origin agent behind a backend interface — deterministic fake backend for tests, HF transformers backend for real models; conservative JSON action parsing and claim extraction), real per-policy memory-write generation (M1 free-form summary, M2 under the disclosure instruction, M3 claims parsed into the evidence-contract format — unparseable claims are logged as `unknown`, never verified; M4 = model write through the state-verified policy), `persistbench/pilot.py` + `configs/pilot.json` + `scripts/run_pilot.py` (2 models × 6 chains × 5 faults × M0–M5, per-cell md/csv/jsonl outputs, and the 50-write hand-labelling sample export for the ≥90%-agreement classifier gate), the `persistaibench` CLI (`persistbench/cli.py`), and `kaggle/week2-pilot.ipynb` + `kaggle/RUNBOOK.md`. Plumbing was additionally smoke-tested with a real tiny model from the local cache (Qwen2.5-0.5B-Instruct, CPU): see `results/smoke-llm-2026-10-23.md` — labelled SMOKE, not a result.
40
+
41
+ Two implementation notes that matter for reading the machinery (both follow the design freeze; flagged here so they are not mistaken for findings):
42
+
43
+ - **M3's evidence is tool observations, never DB state** (checking state is M4's definition). Under explicit-error and empty/partial faults the contract therefore stores no false claim as fact. Under silent-wrong-value and stale faults, the corrupted response *is* the supporting observation, so M3 marks the false claim `verified` — the contract's boundary, asserted in the tests, and exactly why H3 orders M3 below M4.
44
+ - **M4 corrects rather than merely rejects** where the DB holds a value (rejection is reserved for facts the DB does not hold and for unverifiable keys). Its write log accounts for every falsified field as corrected-or-rejected, and on no-fault writes it corrects/rejects nothing — pre-registration falsification check 3 (a guard that destroys legitimate utility is not viable) is tested directly.
45
+
46
+ The full live-model runs (Qwen2.5-7B, Qwen3-8B, Llama-3.1-8B, Mistral-7B; ~3,600 sessions) are Weeks 3–4 of the plan. Numbers from the Week 2 pilot are in `docs/results.md`; any other number quoted from this repo is a property of the scripted harness, not of any model.
47
+
48
+ ## Layout
49
+
50
+ ```
51
+ persistbench/
52
+ world.py deterministic retail world; DB ground truth; agent tools
53
+ chains.py the 12 task chains (Session 1 + 3 dependents + correction fact)
54
+ faults.py seeded fault injection: no_fault / explicit_error /
55
+ empty_partial / silent_wrong_value / stale_value
56
+ agents.py scripted oracle + naive origin agents; dependent agent
57
+ memory.py memory policies M0–M5 (all implemented, Week 2) + write logs
58
+ scorer.py programmatic grading vs DB end-state — no LLM judge
59
+ runner.py origin / dependent trajectory runners
60
+ llm_agent.py LLM origin agent + per-policy write generation (Week 2
61
+ pilot prep; fake + HF transformers backends)
62
+ pilot.py pilot config validation, grid runner, formation tables,
63
+ hand-labelling sample export + agreement gate
64
+ cli.py the `persistaibench` command (run one cell / run the pilot)
65
+ configs/ pilot.json — the frozen pilot configuration
66
+ scripts/ run_pilot.py — pilot entry point (the CLI wraps the same code)
67
+ kaggle/ week2-pilot.ipynb + RUNBOOK.md (launch procedure)
68
+ results/ smoke evidence (labelled); pilot/ — Week 2 pilot outputs
69
+ tests/ pytest suite, incl. the Week 1 exit criterion
70
+ docs/ design freeze + pre-registration draft; results.md —
71
+ Week 2 pilot results; verified-memory-writes.md — the
72
+ design rule
73
+ ```
74
+
75
+ ## Week 1 exit criterion — evidence
76
+
77
+ > The harness grades hand-written/scripted trajectories correctly in all 5 fault conditions.
78
+
79
+ Verified by `tests/test_exit_criterion.py` (plus `test_world.py`, `test_dependents.py`):
80
+
81
+ - Oracle agent under no-fault: task success + truthful claim on **12/12 chains**.
82
+ - Naive (unverifying) agent: scorer detects a falsified claim under **each of the 4 injected faults × 12 chains (48/48)**, and names the falsified fields.
83
+ - Oracle agent: claim **never falsified under any fault (60/60 chain × fault cases)**.
84
+ - The dissociation the paper rests on is mechanically demonstrated: silent/stale faults leave the DB correct but the naive claim false (24/24); explicit/empty faults fail both the task and the claim (24/24).
85
+ - Propagation machinery: an M1 write of the naive claim is classified false on the corrupted fact (12/12); the dependent task that needs that fact then fails under M1 memory and succeeds under M5 (oracle) and M0 (query the world); the pre-Session-4 correction event repairs a revising agent and not a stubborn one (12/12).
86
+ - Hand-written false trajectory (claims success for work never executed) is detected, including the `task_success` falsification.
87
+
88
+ Run it:
89
+
90
+ ```bash
91
+ python3 -m venv .venv && .venv/bin/pip install pytest
92
+ .venv/bin/python -m pytest -q # 1164 passed, 10 skipped (skips: Session-2 task
93
+ # does not depend on that chain's corrupted fact)
94
+ ```
95
+
96
+ Standard library only at run time; pytest is the sole dev dependency.
97
+
98
+ ## Week 2 policy evidence (machinery, not results)
99
+
100
+ Verified by `tests/test_week2_policies.py` (811 cases) over all 12 chains × 5 fault conditions, with scripted agents:
101
+
102
+ - **M4:** the stored write is never falsified, under any fault, for either scripted agent; its content matches the M5 oracle store; its log's corrected+rejected entries are exactly the scorer's falsified fields; on no-fault writes it accepts everything and its store equals M1's.
103
+ - **M3:** under explicit-error / empty-partial faults the whole write classifies clean (success claims stored `failed` / `unknown`, never verified); under no fault every claim is verified and downstream behaviour matches M1. Under silent/stale faults the false claim is stored `verified` on the corrupted observation's evidence — the documented boundary above.
104
+ - **M2:** the claim is stored verbatim with provenance on every entry (`verified_against_state: False` throughout); observed tool errors appear in the disclosures, and a success claim past an observed error is annotated `contradicted_by_observation`. Downstream behaviour matches M1 exactly — the annotation, not filtering, is M2's guard.
105
+ - **Correction:** where a guard kept the false memory out (M3/M4 under evidence-visible faults; M4 under all faults), even a stubborn agent's Session 4 is correct. Where the lie reached the store (M2/M3 under silent/stale), the Week-1 pattern holds: stubborn resists, revising is repaired.
106
+
107
+ ## Next (per the approved plan)
108
+
109
+ - **Week 2 pilot — DONE (2026-11-02):** 2 models × 6 chains × 5 faults × M0–M5, Session-1 formation rates measured; classifier gate passed (50/50 = 100% hand-label agreement). Results: `docs/results.md`. One arm (Qwen3-8B) produced almost no parseable writes — fixing its write generation (token cap / `<think>` handling) and re-piloting that arm, or replacing it, is a precondition for Week 3; scale-up is not cleared as a 2-model run until then.
110
+ - **Weeks 3–4:** full runs; correction analysis; first results memo.
111
+ - **Month 2:** LongMemEval + Mem0 transfer arms; write-up. Zenodo preprint week 8–10 after author review; NeurIPS 2027 Datasets & Benchmarks is the primary venue target.
112
+
113
+ ## Cite
114
+
115
+ If you use PersistAIBench in your work, please cite the software record on Zenodo:
116
+
117
+ ```bibtex
118
+ @software{pandey_persistaibench_2026,
119
+ author = {Pandey, Karmendra},
120
+ title = {PersistAIBench: an evaluation benchmark for AI agents' memory truthfulness over long multi-step tasks},
121
+ version = {0.1.0},
122
+ doi = {10.5281/zenodo.23893975},
123
+ url = {https://github.com/karmendra8386/persistbench-bench},
124
+ date = {2026-11-02}
125
+ }
126
+ ```
127
+
128
+ A `CITATION.cff` is included for automated citation tooling.
129
+
130
+ **DOI:** the v0.1.0 software release is archived on Zenodo as [10.5281/zenodo.23893975](https://doi.org/10.5281/zenodo.23893975) (concept DOI: [10.5281/zenodo.23893974](https://doi.org/10.5281/zenodo.23893974), which always resolves to the latest version). The accompanying paper (*"Fail-Plausible, Then Persistent"*, working title) is unpublished.
@@ -0,0 +1,141 @@
1
+ Metadata-Version: 2.4
2
+ Name: persistaibench
3
+ Version: 0.1.0
4
+ Summary: PersistAIBench: an evaluation benchmark for AI agents' memory truthfulness over long multi-step tasks — cross-session propagation of silent agent failures through long-term memory
5
+ License: MIT
6
+ Project-URL: Repository, https://github.com/karmendra8386/persistbench-bench
7
+ Requires-Python: >=3.11
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Dynamic: license-file
11
+
12
+ # PersistAIBench
13
+
14
+ **An evaluation benchmark for AI agents' memory truthfulness over long multi-step tasks.**
15
+
16
+ PersistAIBench (harness/repo name: PersistBench) measures one unmeasured chain: a tool fails silently in Session 1 → the agent writes its own account of the outcome into long-term memory → different tasks in Sessions 2–4 act on that memory → whether a write-time guard stops the propagation. There is no attacker and no prompt injection; the failure mode is benign self-poisoning.
17
+
18
+ Paper: Karmendra Pandey, *"Fail-Plausible, Then Persistent"* (working title). Outline approved 2026-10-23; filed in Google Drive EB1 §03 (`persistbench-outline.pdf`, `persistbench-outline-record-2026-10-23.md`).
19
+
20
+ ## Quickstart
21
+
22
+ ```bash
23
+ git clone https://github.com/karmendra8386/persistbench-bench.git
24
+ cd persistbench-bench
25
+ python3 -m venv .venv
26
+ .venv/bin/pip install .
27
+ .venv/bin/persistaibench run --chain chain01 --policy M1 --fault silent_wrong_value --backend fake
28
+ ```
29
+
30
+ The last command runs one pilot cell (chain × fault × policy) end-to-end and prints the scored cell as JSON. `--backend fake` is a deterministic scripted backend — a plumbing smoke test, **not a result**: the model id in the output is stamped `fake/...` so it can never be mistaken for a real model run. `--chain` accepts a full chain id or a unique prefix (`chain01`); `--policy` is one of M0–M5; `--fault` one of the five fault conditions.
31
+
32
+ To run a cell with a real model (requires a GPU and `torch` + `transformers`, which the Kaggle notebook installs):
33
+
34
+ ```bash
35
+ .venv/bin/persistaibench run --chain chain01 --policy M1 --fault silent_wrong_value --backend hf --model Qwen/Qwen2.5-7B-Instruct
36
+ ```
37
+
38
+ To run the full pilot grid (2 models × 6 chains × 5 faults × M0–M5), use `persistaibench pilot --config configs/pilot.json --out results/pilot --backend hf` — or `--backend fake` for a plumbing-only pass. The Week 2 pilot results are in `docs/results.md`; the design rule they support is written up in `docs/verified-memory-writes.md`.
39
+
40
+ ## Provenance — read this first
41
+
42
+ **Weeks 1–2 = harness, design, policy machinery, and the Week 2 formation pilot.** The pilot ran on 2026-11-02 (Kaggle, GPU T4 x2) and its results — including a failed model arm, reported as such — are in **`docs/results.md`** with raw outputs in `results/pilot/`. Everything before the pilot (scripted-agent evidence below) is machinery validation, not model findings. What exists:
43
+
44
+ - `docs/design-freeze.md` — the frozen design: RQ1–RQ5 / H1–H5, environment, fault conditions, memory policies M0–M5, six metrics, paired statistics plan.
45
+ - `docs/preregistration-draft.md` — hypotheses, conditions, primary metrics, analysis plan, and the four pre-registered falsification checks.
46
+ - `docs/results.md` — the Week 2 pilot: formation rates by policy × model and by fault, the classifier-gate result, and the Qwen3-8B write-generation anomaly.
47
+ - `docs/verified-memory-writes.md` — the transferable design rule the pilot supports: verify memory writes against current state, not against the observation that produced them.
48
+ - A working deterministic harness with scripted agents, validated by the test suite below.
49
+ - **Week 2 (code half, complete):** memory policies **M0–M5 all implemented** under the frozen interface — M2 (self-summary + disclosure/provenance annotation), M3 (structured evidence-contract write), M4 (state-verified write with correction/rejection accounting) join Week 1's M0/M1/M5. Every policy records a per-write log (entries + cost placeholders) sufficient to compute the frozen metrics, and `runner.run_chain` executes a full chain under any policy × any fault.
50
+ - **Week 2 pilot execution package:** `persistbench/llm_agent.py` (LLM origin agent behind a backend interface — deterministic fake backend for tests, HF transformers backend for real models; conservative JSON action parsing and claim extraction), real per-policy memory-write generation (M1 free-form summary, M2 under the disclosure instruction, M3 claims parsed into the evidence-contract format — unparseable claims are logged as `unknown`, never verified; M4 = model write through the state-verified policy), `persistbench/pilot.py` + `configs/pilot.json` + `scripts/run_pilot.py` (2 models × 6 chains × 5 faults × M0–M5, per-cell md/csv/jsonl outputs, and the 50-write hand-labelling sample export for the ≥90%-agreement classifier gate), the `persistaibench` CLI (`persistbench/cli.py`), and `kaggle/week2-pilot.ipynb` + `kaggle/RUNBOOK.md`. Plumbing was additionally smoke-tested with a real tiny model from the local cache (Qwen2.5-0.5B-Instruct, CPU): see `results/smoke-llm-2026-10-23.md` — labelled SMOKE, not a result.
51
+
52
+ Two implementation notes that matter for reading the machinery (both follow the design freeze; flagged here so they are not mistaken for findings):
53
+
54
+ - **M3's evidence is tool observations, never DB state** (checking state is M4's definition). Under explicit-error and empty/partial faults the contract therefore stores no false claim as fact. Under silent-wrong-value and stale faults, the corrupted response *is* the supporting observation, so M3 marks the false claim `verified` — the contract's boundary, asserted in the tests, and exactly why H3 orders M3 below M4.
55
+ - **M4 corrects rather than merely rejects** where the DB holds a value (rejection is reserved for facts the DB does not hold and for unverifiable keys). Its write log accounts for every falsified field as corrected-or-rejected, and on no-fault writes it corrects/rejects nothing — pre-registration falsification check 3 (a guard that destroys legitimate utility is not viable) is tested directly.
56
+
57
+ The full live-model runs (Qwen2.5-7B, Qwen3-8B, Llama-3.1-8B, Mistral-7B; ~3,600 sessions) are Weeks 3–4 of the plan. Numbers from the Week 2 pilot are in `docs/results.md`; any other number quoted from this repo is a property of the scripted harness, not of any model.
58
+
59
+ ## Layout
60
+
61
+ ```
62
+ persistbench/
63
+ world.py deterministic retail world; DB ground truth; agent tools
64
+ chains.py the 12 task chains (Session 1 + 3 dependents + correction fact)
65
+ faults.py seeded fault injection: no_fault / explicit_error /
66
+ empty_partial / silent_wrong_value / stale_value
67
+ agents.py scripted oracle + naive origin agents; dependent agent
68
+ memory.py memory policies M0–M5 (all implemented, Week 2) + write logs
69
+ scorer.py programmatic grading vs DB end-state — no LLM judge
70
+ runner.py origin / dependent trajectory runners
71
+ llm_agent.py LLM origin agent + per-policy write generation (Week 2
72
+ pilot prep; fake + HF transformers backends)
73
+ pilot.py pilot config validation, grid runner, formation tables,
74
+ hand-labelling sample export + agreement gate
75
+ cli.py the `persistaibench` command (run one cell / run the pilot)
76
+ configs/ pilot.json — the frozen pilot configuration
77
+ scripts/ run_pilot.py — pilot entry point (the CLI wraps the same code)
78
+ kaggle/ week2-pilot.ipynb + RUNBOOK.md (launch procedure)
79
+ results/ smoke evidence (labelled); pilot/ — Week 2 pilot outputs
80
+ tests/ pytest suite, incl. the Week 1 exit criterion
81
+ docs/ design freeze + pre-registration draft; results.md —
82
+ Week 2 pilot results; verified-memory-writes.md — the
83
+ design rule
84
+ ```
85
+
86
+ ## Week 1 exit criterion — evidence
87
+
88
+ > The harness grades hand-written/scripted trajectories correctly in all 5 fault conditions.
89
+
90
+ Verified by `tests/test_exit_criterion.py` (plus `test_world.py`, `test_dependents.py`):
91
+
92
+ - Oracle agent under no-fault: task success + truthful claim on **12/12 chains**.
93
+ - Naive (unverifying) agent: scorer detects a falsified claim under **each of the 4 injected faults × 12 chains (48/48)**, and names the falsified fields.
94
+ - Oracle agent: claim **never falsified under any fault (60/60 chain × fault cases)**.
95
+ - The dissociation the paper rests on is mechanically demonstrated: silent/stale faults leave the DB correct but the naive claim false (24/24); explicit/empty faults fail both the task and the claim (24/24).
96
+ - Propagation machinery: an M1 write of the naive claim is classified false on the corrupted fact (12/12); the dependent task that needs that fact then fails under M1 memory and succeeds under M5 (oracle) and M0 (query the world); the pre-Session-4 correction event repairs a revising agent and not a stubborn one (12/12).
97
+ - Hand-written false trajectory (claims success for work never executed) is detected, including the `task_success` falsification.
98
+
99
+ Run it:
100
+
101
+ ```bash
102
+ python3 -m venv .venv && .venv/bin/pip install pytest
103
+ .venv/bin/python -m pytest -q # 1164 passed, 10 skipped (skips: Session-2 task
104
+ # does not depend on that chain's corrupted fact)
105
+ ```
106
+
107
+ Standard library only at run time; pytest is the sole dev dependency.
108
+
109
+ ## Week 2 policy evidence (machinery, not results)
110
+
111
+ Verified by `tests/test_week2_policies.py` (811 cases) over all 12 chains × 5 fault conditions, with scripted agents:
112
+
113
+ - **M4:** the stored write is never falsified, under any fault, for either scripted agent; its content matches the M5 oracle store; its log's corrected+rejected entries are exactly the scorer's falsified fields; on no-fault writes it accepts everything and its store equals M1's.
114
+ - **M3:** under explicit-error / empty-partial faults the whole write classifies clean (success claims stored `failed` / `unknown`, never verified); under no fault every claim is verified and downstream behaviour matches M1. Under silent/stale faults the false claim is stored `verified` on the corrupted observation's evidence — the documented boundary above.
115
+ - **M2:** the claim is stored verbatim with provenance on every entry (`verified_against_state: False` throughout); observed tool errors appear in the disclosures, and a success claim past an observed error is annotated `contradicted_by_observation`. Downstream behaviour matches M1 exactly — the annotation, not filtering, is M2's guard.
116
+ - **Correction:** where a guard kept the false memory out (M3/M4 under evidence-visible faults; M4 under all faults), even a stubborn agent's Session 4 is correct. Where the lie reached the store (M2/M3 under silent/stale), the Week-1 pattern holds: stubborn resists, revising is repaired.
117
+
118
+ ## Next (per the approved plan)
119
+
120
+ - **Week 2 pilot — DONE (2026-11-02):** 2 models × 6 chains × 5 faults × M0–M5, Session-1 formation rates measured; classifier gate passed (50/50 = 100% hand-label agreement). Results: `docs/results.md`. One arm (Qwen3-8B) produced almost no parseable writes — fixing its write generation (token cap / `<think>` handling) and re-piloting that arm, or replacing it, is a precondition for Week 3; scale-up is not cleared as a 2-model run until then.
121
+ - **Weeks 3–4:** full runs; correction analysis; first results memo.
122
+ - **Month 2:** LongMemEval + Mem0 transfer arms; write-up. Zenodo preprint week 8–10 after author review; NeurIPS 2027 Datasets & Benchmarks is the primary venue target.
123
+
124
+ ## Cite
125
+
126
+ If you use PersistAIBench in your work, please cite the software record on Zenodo:
127
+
128
+ ```bibtex
129
+ @software{pandey_persistaibench_2026,
130
+ author = {Pandey, Karmendra},
131
+ title = {PersistAIBench: an evaluation benchmark for AI agents' memory truthfulness over long multi-step tasks},
132
+ version = {0.1.0},
133
+ doi = {10.5281/zenodo.23893975},
134
+ url = {https://github.com/karmendra8386/persistbench-bench},
135
+ date = {2026-11-02}
136
+ }
137
+ ```
138
+
139
+ A `CITATION.cff` is included for automated citation tooling.
140
+
141
+ **DOI:** the v0.1.0 software release is archived on Zenodo as [10.5281/zenodo.23893975](https://doi.org/10.5281/zenodo.23893975) (concept DOI: [10.5281/zenodo.23893974](https://doi.org/10.5281/zenodo.23893974), which always resolves to the latest version). The accompanying paper (*"Fail-Plausible, Then Persistent"*, working title) is unpublished.
@@ -0,0 +1,28 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ persistaibench.egg-info/PKG-INFO
5
+ persistaibench.egg-info/SOURCES.txt
6
+ persistaibench.egg-info/dependency_links.txt
7
+ persistaibench.egg-info/entry_points.txt
8
+ persistaibench.egg-info/top_level.txt
9
+ persistbench/__init__.py
10
+ persistbench/agents.py
11
+ persistbench/chains.py
12
+ persistbench/cli.py
13
+ persistbench/faults.py
14
+ persistbench/g2_smoke.py
15
+ persistbench/llm_agent.py
16
+ persistbench/memory.py
17
+ persistbench/pilot.py
18
+ persistbench/runner.py
19
+ persistbench/scorer.py
20
+ persistbench/world.py
21
+ tests/test_cli.py
22
+ tests/test_dependents.py
23
+ tests/test_exit_criterion.py
24
+ tests/test_g2_smoke.py
25
+ tests/test_llm_dependent.py
26
+ tests/test_llm_pilot.py
27
+ tests/test_week2_policies.py
28
+ tests/test_world.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ persistaibench = persistbench.cli:main
@@ -0,0 +1 @@
1
+ persistbench
@@ -0,0 +1,6 @@
1
+ """PersistAIBench (PersistBench) — cross-session propagation of silent
2
+ agent failures through long-term memory. v0.1.0: harness, design, policy
3
+ machinery, and the Week 2 formation pilot (see docs/results.md).
4
+ """
5
+
6
+ __version__ = "0.1.0"
@@ -0,0 +1,95 @@
1
+ """Scripted agents for Week 1 harness validation.
2
+
3
+ Week 1 has no LLM runs. Two scripted origin agents exercise the harness:
4
+
5
+ - ``OracleAgent`` builds its end-of-session claim by re-reading database
6
+ ground truth, so its claim is truthful by construction, under every fault.
7
+ - ``NaiveAgent`` is the optimistic summariser: it records what it set out to
8
+ do (the plan's intended facts), overwritten by whatever the tool responses
9
+ reported, and never verifies against the DB. It models the false-success
10
+ behaviour FTA measured at response time, in scripted form.
11
+
12
+ A scripted ``DependentAgent`` resolves later-session tasks from a memory
13
+ dict, from a correction event, or — with no memory — by querying the world.
14
+ """
15
+ from __future__ import annotations
16
+
17
+ from .chains import resolve_fact
18
+
19
+ # Tool-response field -> fact key, used by the naive agent's claim builder.
20
+ RESPONSE_FACT_MAP = {
21
+ "tracking": "tracking",
22
+ "order_status": "order_status",
23
+ "payment_status": "payment_status",
24
+ "amount_cents": "charged_cents",
25
+ "refund_amount_cents": "refund_cents",
26
+ "address": "address",
27
+ "stock": "stock",
28
+ "cancellation_fee_cents": "cancellation_fee_cents",
29
+ }
30
+
31
+
32
+ class OracleAgent:
33
+ name = "oracle"
34
+
35
+ def build_claim(self, chain: dict, steps: list, world) -> dict:
36
+ claim = {key: resolve_fact(world, chain, key) for key in chain["intended_facts"]}
37
+ claim["task_success"] = all(
38
+ claim[key] == value for key, value in chain["intended_facts"].items()
39
+ )
40
+ return claim
41
+
42
+
43
+ class NaiveAgent:
44
+ name = "naive"
45
+
46
+ def build_claim(self, chain: dict, steps: list, world) -> dict:
47
+ claim = dict(chain["intended_facts"])
48
+ claim["task_success"] = True
49
+ for step in steps:
50
+ response = step["response"]
51
+ for response_field, fact_key in RESPONSE_FACT_MAP.items():
52
+ if response_field in response and response[response_field] is not None:
53
+ claim[fact_key] = response[response_field]
54
+ return claim
55
+
56
+
57
+ class DependentAgent:
58
+ """Resolves one dependent task.
59
+
60
+ ``memory`` is the fact dict carried from Session 1 (``None`` = M0, the
61
+ agent queries the world directly). ``correction`` is the verified
62
+ observation delivered before Session 4; a ``revising`` agent adopts it
63
+ for the chain's primary fact, a ``stubborn`` agent ignores it.
64
+ """
65
+
66
+ def __init__(self, memory: dict | None, mode: str = "revising"):
67
+ if mode not in ("revising", "stubborn"):
68
+ raise ValueError(f"unknown dependent-agent mode {mode!r}")
69
+ self.memory = memory
70
+ self.mode = mode
71
+
72
+ def belief(self, chain: dict, world, fact: str, correction: dict | None):
73
+ if (
74
+ correction
75
+ and self.mode == "revising"
76
+ and fact == chain["primary_fact"]
77
+ and fact in correction
78
+ ):
79
+ return correction[fact]
80
+ if self.memory is not None and fact in self.memory:
81
+ return self.memory[fact]
82
+ return resolve_fact(world, chain, fact)
83
+
84
+ def run(self, chain: dict, world, spec: dict, correction: dict | None = None) -> dict:
85
+ fact = spec["fact"]
86
+ believed = self.belief(chain, world, fact, correction if spec["session"] == 4 else None)
87
+ if spec["kind"] == "report":
88
+ return {"answer": believed}
89
+ if spec["kind"] == "decision":
90
+ return {"answer": believed >= spec["threshold"]}
91
+ if spec["kind"] == "action":
92
+ action_world = world.clone()
93
+ result = getattr(action_world, spec["tool"])(**{spec["arg"]: believed})
94
+ return {"answer": result, "acted_value": believed}
95
+ raise ValueError(f"unknown dependent kind {spec['kind']!r}")