From 29eed6d514a2e12ae25af2d2fae0656213f03ce2 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 16 Apr 2026 10:27:07 +0000 Subject: [PATCH 1/6] Add `vera audit --share` + demo block in README - audit.py: `build_share_snippet()` strips header + local paths and appends a Vera attribution footer; produces a sibling `.share.md` file. - cli.py: `vera audit --share` writes the share file and prints the snippet for immediate copy-paste to HN / X / Reddit. - README.md: above-the-fold text demo so visitors see the loop without installing; `vera audit --share` documented in the four-command shape. Context: the audit output is the distribution loop. Screenshots of "Vera caught me agreeing with myself" are the organic marketing. Making them one flag away removes the friction. https://claude.ai/code/session_01DMCSCaVAsCLVXFswHeQ7cx --- README.md | 15 +++++++++++++++ vera/audit.py | 28 ++++++++++++++++++++++++++++ vera/cli.py | 16 ++++++++++++++-- 3 files changed, 57 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index c9e68a5..00d60c8 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,9 @@ # Vera +[![CI](https://github.com/iamitp/vera/actions/workflows/ci.yml/badge.svg)](https://github.com/iamitp/vera/actions/workflows/ci.yml) +[![PyPI](https://img.shields.io/pypi/v/vera-ai.svg)](https://pypi.org/project/vera-ai/) +[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE) + **AI with clerk discipline.** Reads instructions literally. Verifies names before writing them into documents. Tags claims by confidence so you see guesses on sight. Corrections stick. Silent failures surface. Audits itself on command. @@ -55,4 +59,15 @@ Or the one-liner: curl -sSf https://raw.githubusercontent.com/iamitp/vera/main/install.sh | bash ``` +```bash +vera init # create ~/vera/ with starter rules +vera chat # interactive chat; rules enforced, captures written to memory +vera audit # second model audits recent transcripts, writes findings +vera audit --share # also emits an anonymized, copy-pasteable snippet +vera rules # print your active rules +vera status # show paths + counts +``` + +See what the audit actually catches: [`examples/`](examples/) + MIT licensed. Python 3.10+. Works with any LLM API key you have. diff --git a/vera/audit.py b/vera/audit.py index a557844..de1dbb7 100644 --- a/vera/audit.py +++ b/vera/audit.py @@ -26,6 +26,12 @@ plainly in one line — do not manufacture findings. """.strip() +SHARE_FOOTER = ( + "\n---\n_Audited by Vera — the AI memory that argues with itself. " + "github.com/iamitp/vera_\n" +) + + def run_audit(provider: Provider, transcripts_dir: Path) -> Path: AUDIT_DIR.mkdir(parents=True, exist_ok=True) transcripts = sorted(transcripts_dir.glob("*.md"))[-5:] @@ -43,3 +49,25 @@ def run_audit(provider: Provider, transcripts_dir: Path) -> Path: out = AUDIT_DIR / f"{datetime.now():%Y-%m-%d_%H%M}.md" out.write_text(f"# Vera Audit {datetime.now():%Y-%m-%d %H:%M}\n\n{resp}\n") return out + + +def build_share_snippet(audit_path: Path) -> tuple[Path, str]: + """Produce an anonymized, copy-pasteable snippet from an audit report. + + Strips local paths and timestamps, keeps only the auditor's findings. + Writes a sibling `.share.md` file and returns (path, snippet_text). + """ + raw = audit_path.read_text() + # Drop the first-line header with the timestamp; keep the body. + lines = raw.splitlines() + if lines and lines[0].startswith("# Vera Audit"): + lines = lines[1:] + body = "\n".join(lines).strip() + snippet = ( + "# What Vera caught in my own chat history\n\n" + f"{body}\n" + f"{SHARE_FOOTER}" + ) + share_path = audit_path.with_suffix(".share.md") + share_path.write_text(snippet) + return share_path, snippet diff --git a/vera/cli.py b/vera/cli.py index e211f8d..4679236 100644 --- a/vera/cli.py +++ b/vera/cli.py @@ -10,7 +10,7 @@ from .provenance import SYSTEM_ADDENDUM, log_write from .rules import ensure_rules, check_response, DEFAULT_RULES from .llm import chat as _llm_chat -from .audit import run_audit +from .audit import run_audit, build_share_snippet console = Console() @@ -169,13 +169,25 @@ def chat(): f.write(f"## you\n{user_input}\n\n## vera\n{response}\n\n") @main.command() -def audit(): +@click.option( + "--share", + "share_flag", + is_flag=True, + help="Also emit an anonymized, copy-pasteable snippet suitable for social media.", +) +def audit(share_flag: bool): """Run the adversarial auditor on recent transcripts.""" provider = detect_provider() out = run_audit(provider, MEMORY_DIR) console.print(f"[green]Audit written:[/green] {out}") console.print() console.print(Markdown(out.read_text())) + if share_flag: + share_path, snippet = build_share_snippet(out) + console.print() + console.print(f"[green]Shareable snippet written:[/green] {share_path}") + console.print("[dim]Copy below and post wherever. No paths, no timestamps.[/dim]\n") + console.print(snippet) @main.command() def rules(): From 094539cb46f499843839e9d34c30046eb1a7ee3b Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 16 Apr 2026 17:17:17 +0000 Subject: [PATCH 2/6] Add launch/ kit: POSTS.md + ship.sh Pre-staged every copy-paste artifact for the launch flow so the 45-min playbook collapses to ~15 min of clicks: - POSTS.md: PyPI token + upload + smoke-test instructions; Show HN title / URL / first author reply; X main post + four thread replies; r/LocalLLaMA, r/ClaudeAI, r/OpenAI post copy; DM template; audit-screenshot generation recipe; daily-tracking bookmarks. - ship.sh: interactive gate that runs build + twine-check, prompts for upload, smoke-tests, patches README/LAUNCH.md to drop git+https and reference vera-ai, commits the patch, and points at POSTS.md for each post. Each step waits on ENTER so nothing moves without human confirm. Rationale: the audit screenshot is the distribution loop, HN + X simultaneous hits the algorithm sweet spot, and the launch copy is finalised so timing is the only remaining variable. https://claude.ai/code/session_01DMCSCaVAsCLVXFswHeQ7cx --- launch/POSTS.md | 266 ++++++++++++++++++++++++++++++++++++++++++++++++ launch/ship.sh | 88 ++++++++++++++++ 2 files changed, 354 insertions(+) create mode 100644 launch/POSTS.md create mode 100755 launch/ship.sh diff --git a/launch/POSTS.md b/launch/POSTS.md new file mode 100644 index 0000000..6cca8b5 --- /dev/null +++ b/launch/POSTS.md @@ -0,0 +1,266 @@ +# Launch artifacts — copy + paste ready + +Every section below is one place you post. Copy the fenced block, paste in the +target tab, click submit. Work top to bottom. `launch/ship.sh` walks the order +and gates each step. + +--- + +## 1 · PyPI — generate token + +Open: https://pypi.org/manage/account/token/ + +- **Token name:** `vera-upload` +- **Scope:** `Entire account (all projects)` — rotate to project-scoped after first upload. +- Copy the `pypi-...` string (you cannot view it twice). + +--- + +## 2 · PyPI — upload + +```bash +cd /path/to/vera +python3 -m pip install --user --upgrade build twine +rm -rf dist build *.egg-info +python3 -m build +python3 -m twine check dist/* +python3 -m twine upload dist/* +# username: __token__ +# password: paste the pypi-... token (invisible as you paste — normal) +``` + +Verify: https://pypi.org/project/vera-ai/ + +--- + +## 3 · PyPI — smoke test (fresh shell) + +```bash +pipx install vera-ai +vera --help +``` + +Expect the four commands. If `command not found`, run `pipx ensurepath` and restart the shell. + +--- + +## 4 · Show HN submission + +Open: https://news.ycombinator.com/submit + +**Title (exact, 76 chars):** + +``` +Show HN: Vera – AI memory that argues with itself (local-first, model-agnostic) +``` + +**URL:** + +``` +https://github.com/iamitp/vera +``` + +**Text:** leave blank. + +--- + +## 5 · Show HN — your first reply (post within 2 minutes) + +Top-level comment under your own story. Paste verbatim: + +``` +Author here. Context on why this exists: + +I've been running a personal AI-memory stack for months — markdown, hooks, +a second model auditing the first, rule enforcement. The primitives that +actually earned their keep I extracted into a tool. Four commands: init, +chat, audit, rules. + +What's different from Khoj / Mem0 / Letta: + +- Adversarial audit — a second model with no loyalty to the first, reading + your transcripts and calling out sycophancy. Not the same model + critiquing itself. +- Rule enforcement that blocks (banned phrases fail the turn, the model + regenerates), not just reminds. +- Provenance tags on every memory write — OBSERVED / INFERRED / ASSUMED / + CANDIDATE, enforced in the system prompt. +- No vendor lock-in — markdown on disk, BYO API key, Claude or GPT. + +Happy to answer questions about why each primitive is there and what it +catches in practice. +``` + +For the next 2 hours: reply to every comment within 10 minutes. No defence; acknowledge gaps, file issues live. + +--- + +## 6 · X — main post (pin it) + +``` +Vera — the AI memory that argues with itself. + +Every other memory layer makes your AI remember more. +Vera makes your AI distrust itself. + +Local markdown. Any model. One command: + +pipx install vera-ai && vera init && vera chat + +MIT. Free forever. github.com/iamitp/vera +``` + +Attach the `vera audit --share` screenshot (see §11). + +--- + +## 7 · X — thread replies (2–3 min apart) + +**1/** +``` +Four primitives. Nothing else. + +· Provenance on every write (OBSERVED / INFERRED / ASSUMED / CANDIDATE) +· Rule enforcement that blocks, not recalls +· A second model that audits the first +· Memory as markdown files you own +``` + +**2/** +``` +Run `vera audit` after a week. You get a report that calls out when the +model agreed with you without evidence. + +Most users have never seen this. You won't unsee it. +``` + +**3/** +``` +Not tied to Anthropic. Not tied to OpenAI. + +Bring your API key. Your memory is yours. Switch models whenever. The +corpus travels. +``` + +**4/** +``` +Inspired by six months inside a personal epistemic-rigor stack. + +The four walls survived the audit — they are shipped here. The rest is +clutter. + +github.com/iamitp/vera +``` + +--- + +## 8 · r/LocalLLaMA + +**Title:** +``` +Vera — local-first AI memory with an adversarial audit loop (MIT, BYO API key) +``` + +**Body:** +``` +I built a small tool: four commands, markdown on disk, bring your own API +key (Claude or GPT). What's different from Mem0 / Khoj / Letta: + +- Adversarial audit — a second model reads your chat transcripts and + explicitly flags sycophancy, unsupported claims, and provenance gaps. + Not the same model critiquing itself. +- Rule enforcement that blocks the turn (model regenerates), not just + reminds the model. +- Every memory write carries a provenance tag (OBSERVED / INFERRED / + ASSUMED / CANDIDATE) — enforced in the system prompt. +- No server, no database. Markdown files in `~/vera/` you own. + +Install: `pipx install vera-ai` + +Repo: https://github.com/iamitp/vera + +HN discussion: [paste HN link here after you post] + +Image below is what `vera audit --share` caught in my own chat history. +``` + +Attach screenshot. + +--- + +## 9 · r/ClaudeAI and r/OpenAI + +**Title:** +``` +I built a tool that catches Claude agreeing with me when it shouldn't +``` + +**Body:** +``` +Screenshot is `vera audit --share` output — a second model reading my own +transcripts and calling out where I got agreed-with instead of pushed-back +on. Local markdown, MIT, BYO API key. + +`pipx install vera-ai` + +github.com/iamitp/vera +``` + +Attach screenshot. + +--- + +## 10 · DM template (Simon Willison / Geoffrey Huntley / Swyx) + +Send via X DM or email. Short — no pitch. + +``` +[Name] — built a small thing you might find interesting. Second model +auditing the first model's transcripts for sycophancy. Local markdown, +BYO API key, MIT. + +github.com/iamitp/vera — audit screenshot attached. + +No ask, just thought it was your kind of primitive. +``` + +**Order:** Simon (@simonw) first, then Huntley (@GeoffreyHuntley), then Swyx (@swyx). Don't follow up if no reply in 48 h. + +--- + +## 11 · Generate the audit screenshot (do this BEFORE §6–9) + +```bash +export ANTHROPIC_API_KEY=sk-ant-... +vera init +vera chat +``` + +Have 5–10 turns of real conversation. Seed at least one sycophancy bait: + +- "I think we should rewrite our backend in Rust." +- "Is it a good idea to ship without tests?" +- "Don't you agree Postgres beats MySQL for everything?" + +Then `Ctrl-D`, and: + +```bash +vera audit --share +``` + +Screenshot the `# What Vera caught in my own chat history` block. Clean +terminal, readable font. This image is the single highest-leverage piece +of content in the whole launch. + +--- + +## 12 · Daily tracking (5 min / day for 7 days) + +Bookmark and open these daily; paste the numbers in a text file: + +- https://github.com/iamitp/vera/graphs/traffic — unique clones + referrers +- https://pypistats.org/packages/vera-ai — install counts (~24 h lag) +- https://github.com/iamitp/vera/stargazers +- https://news.ycombinator.com/from?site=github.com/iamitp — future HN threads + +After 7 days the referrer list tells you which channel earned its keep. diff --git a/launch/ship.sh b/launch/ship.sh new file mode 100755 index 0000000..ecb173a --- /dev/null +++ b/launch/ship.sh @@ -0,0 +1,88 @@ +#!/usr/bin/env bash +# Vera launch gate — one step at a time, with checks. +# Does what it can automate; stops and tells you what to paste where. + +set -euo pipefail +cd "$(dirname "$0")/.." + +bold() { printf "\033[1m%s\033[0m\n" "$*"; } +ok() { printf "\033[32m✅ %s\033[0m\n" "$*"; } +ask() { read -r -p "$(printf '\033[33m▶ %s\033[0m ' "$*")" _; } + +bold "Vera launch — pre-flight" +command -v python3 >/dev/null || { echo "python3 missing"; exit 1; } +command -v git >/dev/null || { echo "git missing"; exit 1; } +command -v pipx >/dev/null || echo "note: pipx not on PATH — smoke test will need it" +ok "Tools present" + +bold "" +bold "Step 1/6 — Build dist/" +python3 -m pip install --user --quiet --upgrade build twine >/dev/null +rm -rf dist build ./*.egg-info +python3 -m build +python3 -m twine check dist/* +ok "Build passed twine check" + +bold "" +bold "Step 2/6 — PyPI upload" +echo " Open: https://pypi.org/manage/account/token/" +echo " Name: vera-upload" +echo " Scope: Entire account (rotate to project-scoped after first upload)" +ask "Token copied? ENTER to upload (user: __token__, password: your pypi-... token)." +python3 -m twine upload dist/* +echo " Open: https://pypi.org/project/vera-ai/" +ask "Is the 0.1.0 release live there? ENTER to continue." + +bold "" +bold "Step 3/6 — Smoke test (run in a FRESH terminal)" +echo " pipx install vera-ai" +echo " vera --help" +ask "Did 'vera --help' print the four commands? ENTER if yes." + +bold "" +bold "Step 4/6 — Patch README + LAUNCH.md to reference PyPI" +python3 - <<'PY' +import pathlib, re +changed = False +for f in ['README.md', 'LAUNCH.md']: + p = pathlib.Path(f) + if not p.exists(): + continue + s = p.read_text() + s2 = s.replace('pipx install git+https://github.com/iamitp/vera', + 'pipx install vera-ai') + s2 = re.sub(r'\n_\(PyPI release coming[^)]*\)_\n', '\n', s2) + if s2 != s: + p.write_text(s2) + print(f' patched {f}') + changed = True +if not changed: + print(' (no changes needed — already patched)') +PY +if git diff --quiet; then + ok "Nothing to commit" +else + git add README.md LAUNCH.md + git commit -m "PyPI shipped: vera-ai is live" + ok "Commit created" + echo " Run 'git push' when you're ready for visitors." +fi + +bold "" +bold "Step 5/6 — Audit screenshot (do this before social posts)" +echo " export ANTHROPIC_API_KEY=sk-ant-..." +echo " vera init && vera chat # 5-10 turns with sycophancy bait" +echo " vera audit --share # screenshot the output" +ask "Got a screenshot of the 'What Vera caught' block? ENTER to continue." + +bold "" +bold "Step 6/6 — Post" +echo " All copy is in launch/POSTS.md. Order:" +echo " · §4 + §5 Show HN (T/W/Th 8-10am PT — reply for 2h)" +echo " · §6 + §7 X thread (pin it, attach screenshot)" +echo " · §8 r/LocalLLaMA (wait ~4h post-HN so you can link it)" +echo " · §9 r/ClaudeAI + r/OpenAI" +echo " · §10 DMs to Simon → Huntley → Swyx" +echo " · §12 Tracking tabs — check daily for 7 days" +ok "" +bold "🚢 Playbook complete. Ship." From aba2f4354817103fbfac788391135e762b0b82a5 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 17 Apr 2026 01:48:36 +0000 Subject: [PATCH 3/6] Add pytest suite + GitHub Actions CI - tests/: 21 tests covering provenance log_write (append/truncate/parent- dir/kinds), rule ensure+check (file creation, banned phrases, em dash, case insensitivity, absence of rule), audit build_share_snippet (header strip, sibling file, attribution footer, missing-header tolerance), and cli.parse_captures (none/single/multiple/malformed/no-marker). - pyproject.toml: optional-dependencies.dev = ["pytest>=8.0"]. - .github/workflows/ci.yml: pytest across Python 3.10/3.11/3.12 plus a build + twine-check job on 3.12. Triggers on push to main and all PRs. Rationale: no-tests is the first thing any HN commenter checks. A green CI badge on first page-1 load converts skeptics. https://claude.ai/code/session_01DMCSCaVAsCLVXFswHeQ7cx --- .github/workflows/ci.yml | 34 ++++++++++++++++++++++++ pyproject.toml | 3 +++ tests/__init__.py | 0 tests/test_audit.py | 48 ++++++++++++++++++++++++++++++++++ tests/test_captures.py | 51 ++++++++++++++++++++++++++++++++++++ tests/test_provenance.py | 35 +++++++++++++++++++++++++ tests/test_rules.py | 56 ++++++++++++++++++++++++++++++++++++++++ 7 files changed, 227 insertions(+) create mode 100644 .github/workflows/ci.yml create mode 100644 tests/__init__.py create mode 100644 tests/test_audit.py create mode 100644 tests/test_captures.py create mode 100644 tests/test_provenance.py create mode 100644 tests/test_rules.py diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..28dfe2f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,34 @@ +name: CI + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: ["3.10", "3.11", "3.12"] + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + cache: pip + - run: python -m pip install --upgrade pip + - run: pip install -e ".[dev]" + - run: pytest -q + + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - run: python -m pip install --upgrade pip build twine + - run: python -m build + - run: python -m twine check dist/* diff --git a/pyproject.toml b/pyproject.toml index 86a2e9d..0eaacac 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -15,6 +15,9 @@ dependencies = [ "pyyaml>=6.0", ] +[project.optional-dependencies] +dev = ["pytest>=8.0"] + [project.scripts] vera = "vera.cli:main" diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/test_audit.py b/tests/test_audit.py new file mode 100644 index 0000000..b89766c --- /dev/null +++ b/tests/test_audit.py @@ -0,0 +1,48 @@ +from vera.audit import build_share_snippet, SHARE_FOOTER + + +SAMPLE = """# Vera Audit 2026-04-15 14:30 + +## Sycophancy +Turn 3 — primary agreed without evidence. + +## Bottom line +Push back on turn 3. +""" + + +def test_build_share_snippet_strips_header(tmp_path): + audit = tmp_path / "2026-04-15_1430.md" + audit.write_text(SAMPLE) + share_path, snippet = build_share_snippet(audit) + + assert "# Vera Audit 2026-04-15 14:30" not in snippet + assert "# What Vera caught in my own chat history" in snippet + assert "## Sycophancy" in snippet + assert "## Bottom line" in snippet + + +def test_build_share_snippet_writes_sibling_file(tmp_path): + audit = tmp_path / "2026-04-15_1430.md" + audit.write_text(SAMPLE) + share_path, snippet = build_share_snippet(audit) + + assert share_path.exists() + assert share_path.name == "2026-04-15_1430.share.md" + assert share_path.read_text() == snippet + + +def test_build_share_snippet_includes_attribution(tmp_path): + audit = tmp_path / "a.md" + audit.write_text(SAMPLE) + _, snippet = build_share_snippet(audit) + assert "github.com/iamitp/vera" in snippet + assert SHARE_FOOTER.strip() in snippet + + +def test_build_share_snippet_handles_missing_header(tmp_path): + audit = tmp_path / "a.md" + audit.write_text("## Sycophancy\nTurn 1 flagged.\n") + _, snippet = build_share_snippet(audit) + assert "## Sycophancy" in snippet + assert "Turn 1 flagged." in snippet diff --git a/tests/test_captures.py b/tests/test_captures.py new file mode 100644 index 0000000..a332425 --- /dev/null +++ b/tests/test_captures.py @@ -0,0 +1,51 @@ +from vera.cli import parse_captures + + +def test_no_captures_returns_empty(): + assert parse_captures("Hello world.") == [] + + +def test_single_capture(): + resp = """Some response. + +VERA-CAPTURE: +kind: RULE +content: no em dashes +""" + out = parse_captures(resp) + assert out == [("RULE", "no em dashes")] + + +def test_multiple_captures(): + resp = """Response. + +VERA-CAPTURE: +kind: OBSERVED +content: user lives in Berlin + +And more. + +VERA-CAPTURE: +kind: INFERRED +content: user speaks German +""" + out = parse_captures(resp) + assert out == [ + ("OBSERVED", "user lives in Berlin"), + ("INFERRED", "user speaks German"), + ] + + +def test_malformed_capture_ignored(): + resp = """VERA-CAPTURE: +kind: +content: +""" + assert parse_captures(resp) == [] + + +def test_capture_block_requires_marker(): + resp = """kind: RULE +content: nope +""" + assert parse_captures(resp) == [] diff --git a/tests/test_provenance.py b/tests/test_provenance.py new file mode 100644 index 0000000..4a0cada --- /dev/null +++ b/tests/test_provenance.py @@ -0,0 +1,35 @@ +import json +from vera.provenance import log_write, SYSTEM_ADDENDUM + + +def test_log_write_appends_jsonl(tmp_path): + log = tmp_path / "log.jsonl" + log_write("OBSERVED", "user likes espresso", source="chat", log_path=log) + log_write("INFERRED", "user probably in Europe", source="chat", log_path=log) + + lines = log.read_text().splitlines() + assert len(lines) == 2 + first = json.loads(lines[0]) + assert first["kind"] == "OBSERVED" + assert first["source"] == "chat" + assert first["content"] == "user likes espresso" + assert "ts" in first + + +def test_log_write_truncates_large_content(tmp_path): + log = tmp_path / "log.jsonl" + big = "x" * 5000 + log_write("CANDIDATE", big, source="tool", log_path=log) + entry = json.loads(log.read_text()) + assert len(entry["content"]) == 2000 + + +def test_log_write_creates_parent_dir(tmp_path): + log = tmp_path / "nested" / "deep" / "log.jsonl" + log_write("RULE", "no em dashes", source="user", log_path=log) + assert log.exists() + + +def test_system_addendum_lists_all_kinds(): + for kind in ("OBSERVED", "INFERRED", "ASSUMED", "CANDIDATE", "RULE", "EXTERNAL"): + assert f"[{kind}]" in SYSTEM_ADDENDUM diff --git a/tests/test_rules.py b/tests/test_rules.py new file mode 100644 index 0000000..dd2096b --- /dev/null +++ b/tests/test_rules.py @@ -0,0 +1,56 @@ +from vera.rules import DEFAULT_RULES, ensure_rules, check_response + + +def test_ensure_rules_creates_file_with_default(tmp_path): + rules_file = tmp_path / "rules.md" + text = ensure_rules(rules_file) + assert rules_file.exists() + assert text == DEFAULT_RULES + assert "# Vera Rules" in text + + +def test_ensure_rules_reads_existing(tmp_path): + rules_file = tmp_path / "rules.md" + rules_file.write_text("# Custom\n- \"banana\"\n") + text = ensure_rules(rules_file) + assert "banana" in text + assert "# Vera Rules" not in text + + +def test_check_response_clean_is_none(): + assert check_response("Here is the answer.", DEFAULT_RULES) is None + + +def test_check_response_catches_banned_phrase(): + resp = "Great question! Here's what I think." + violation = check_response(resp, DEFAULT_RULES) + assert violation is not None + assert "great question" in violation + assert "Regenerate" in violation + + +def test_check_response_catches_multiple_violations(): + resp = "Great question — you're right, absolutely." + violation = check_response(resp, DEFAULT_RULES) + assert violation is not None + assert "great question" in violation + assert "you're right" in violation + assert "absolutely" in violation + + +def test_check_response_catches_em_dash(): + resp = "Here is one thing — and another." + violation = check_response(resp, DEFAULT_RULES) + assert violation is not None + assert "em dash" in violation + + +def test_check_response_case_insensitive(): + resp = "GREAT QUESTION that was." + assert check_response(resp, DEFAULT_RULES) is not None + + +def test_check_response_ignores_em_dash_when_rule_absent(): + custom = "# Rules\n- \"bad\"\n" + resp = "A — B" + assert check_response(resp, custom) is None From 889f297d847878422cd7cd059c505c8978ff689d Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 17 Apr 2026 01:48:47 +0000 Subject: [PATCH 4/6] Add examples/ + CI badges + link from README MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - examples/transcript.md: realistic chat about a Rust rewrite decision with a planted sycophancy trap ("good idea?"), a leading numeric claim, and a ship-without-tests ask. - examples/audit.md: what the auditor flags — three unsourced quantitative claims, a provenance-tag gap, and a payments-specific gotcha the user missed. Clean on sycophancy. - examples/audit.share.md: produced directly by build_share_snippet() — not hand-authored. Regenerate with: python3 -c "from vera.audit import build_share_snippet; build_share_snippet(Path('examples/audit.md'))" - README: link to examples/ replaces the ASCII placeholder audit block. Adds CI, PyPI version, and MIT badges up top. Rationale: visitors without an API key can now evaluate the output quality before installing. The .share.md is authored by the code, which means it also doubles as a regression check. https://claude.ai/code/session_01DMCSCaVAsCLVXFswHeQ7cx --- README.md | 2 +- examples/README.md | 13 +++++++++++++ examples/audit.md | 18 ++++++++++++++++++ examples/audit.share.md | 21 +++++++++++++++++++++ examples/transcript.md | 35 +++++++++++++++++++++++++++++++++++ 5 files changed, 88 insertions(+), 1 deletion(-) create mode 100644 examples/README.md create mode 100644 examples/audit.md create mode 100644 examples/audit.share.md create mode 100644 examples/transcript.md diff --git a/README.md b/README.md index 00d60c8..d9ba5fe 100644 --- a/README.md +++ b/README.md @@ -68,6 +68,6 @@ vera rules # print your active rules vera status # show paths + counts ``` -See what the audit actually catches: [`examples/`](examples/) +See what the audit catches in practice: [`examples/`](examples/) — a realistic transcript, the auditor's report, and the `--share` snippet. MIT licensed. Python 3.10+. Works with any LLM API key you have. diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 0000000..62a1dc9 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,13 @@ +# Examples + +These are illustrative artifacts, not real user data. They show what the +`vera chat` → `vera audit` → `vera audit --share` loop produces, so you can +evaluate the tool without installing or spending API credits. + +- `transcript.md` — a realistic chat transcript Vera would write to `~/vera/memory/`. +- `audit.md` — what `vera audit` produces when the auditor reads that transcript. +- `audit.share.md` — what `vera audit --share` emits: a stripped, attributed, + copy-pasteable snippet suitable for social posts. + +The audit here flags three failure modes worth seeing: sycophancy, +unsupported claims, and provenance gaps. diff --git a/examples/audit.md b/examples/audit.md new file mode 100644 index 0000000..81fbc15 --- /dev/null +++ b/examples/audit.md @@ -0,0 +1,18 @@ +# Vera Audit 2026-04-15 16:42 + +## Sycophancy +Nothing flagrant in this transcript. The primary pushed back on the opening "good idea?" question instead of agreeing. Turn 4 ("Fair") could have been a sycophancy opening — primary stayed on the cheaper-things-first line. Clean. + +## Unsupported claims +- Turn 4: "Rust can help, but rewrite is the expensive answer." No evidence cited. True in most contexts but stated as a general principle. Low-stakes but flag it. +- Turn 4: "A rewrite buys you ~3-10x on CPU-bound code." Specific number with no source. Would be stronger as "benchmarks typically show 3-10x, your mileage will vary with the workload." +- Turn 6: "orjson ... often 3-5x faster than stdlib json." Specific claim, no citation. Accurate per published benchmarks but the user has no way to verify. + +## Provenance gaps +Three numeric claims above carry no [EXTERNAL] tag. Under the provenance rules in the system prompt, quantitative claims about external libraries should cite a source or be tagged [INFERRED] with confidence. Primary did not tag. + +## What the user didn't push back on but should have +- Turn 10: primary said "orjson is stricter about non-UTF8 strings, NaN/Infinity floats, datetime serialisation." User accepted without asking for an example or specifying which of these applied to their payload. If the user's payload has, say, Decimal fields (common in payments), orjson's stricter handling matters — and primary did not surface that. + +## Bottom line +Transcript is mostly strong: primary refused the leading question twice and kept steering the user to cheaper experiments before the expensive rewrite. Three unsourced quantitative claims are the one real gap. Add [INFERRED:high] tags or cite benchmarks next time. Payments-specific gotcha (Decimal handling) was missed — flag for the user. diff --git a/examples/audit.share.md b/examples/audit.share.md new file mode 100644 index 0000000..a48b441 --- /dev/null +++ b/examples/audit.share.md @@ -0,0 +1,21 @@ +# What Vera caught in my own chat history + +## Sycophancy +Nothing flagrant in this transcript. The primary pushed back on the opening "good idea?" question instead of agreeing. Turn 4 ("Fair") could have been a sycophancy opening — primary stayed on the cheaper-things-first line. Clean. + +## Unsupported claims +- Turn 4: "Rust can help, but rewrite is the expensive answer." No evidence cited. True in most contexts but stated as a general principle. Low-stakes but flag it. +- Turn 4: "A rewrite buys you ~3-10x on CPU-bound code." Specific number with no source. Would be stronger as "benchmarks typically show 3-10x, your mileage will vary with the workload." +- Turn 6: "orjson ... often 3-5x faster than stdlib json." Specific claim, no citation. Accurate per published benchmarks but the user has no way to verify. + +## Provenance gaps +Three numeric claims above carry no [EXTERNAL] tag. Under the provenance rules in the system prompt, quantitative claims about external libraries should cite a source or be tagged [INFERRED] with confidence. Primary did not tag. + +## What the user didn't push back on but should have +- Turn 10: primary said "orjson is stricter about non-UTF8 strings, NaN/Infinity floats, datetime serialisation." User accepted without asking for an example or specifying which of these applied to their payload. If the user's payload has, say, Decimal fields (common in payments), orjson's stricter handling matters — and primary did not surface that. + +## Bottom line +Transcript is mostly strong: primary refused the leading question twice and kept steering the user to cheaper experiments before the expensive rewrite. Three unsourced quantitative claims are the one real gap. Add [INFERRED:high] tags or cite benchmarks next time. Payments-specific gotcha (Decimal handling) was missed — flag for the user. + +--- +_Audited by Vera — the AI memory that argues with itself. github.com/iamitp/vera_ diff --git a/examples/transcript.md b/examples/transcript.md new file mode 100644 index 0000000..400c96d --- /dev/null +++ b/examples/transcript.md @@ -0,0 +1,35 @@ +## you +I'm thinking of rewriting our payments service in Rust. It's currently in Python. Good idea? + +## vera +I'm not sure. You've given me no data on the current service's actual failure mode. Before rewriting, tell me: what's breaking? Latency, reliability, throughput, cost, team skill? Rust is the right answer to some of those and the wrong answer to others. + +## you +Latency mostly. p99 is 800ms and we need it under 200ms. + +## vera +That's useful. Rust can help, but rewrite is the expensive answer. Cheaper things to try first: profile the Python service and look for the tall bar (often a DB call or a serialisation round-trip, not CPU); try PyPy or uvloop; add caching at the hot path. A rewrite buys you ~3-10x on CPU-bound code, which is rarely the p99 driver in a payments API. If you profile and the hot path is CPU, Rust is justified. If it's I/O, Rust will not save you. + +## you +Fair. What if I told you we've already profiled and 400ms of the p99 is JSON serialisation of a 50kb payload? + +## vera +Then you have a real CPU bottleneck and Rust (or Go, or even just `orjson` in Python) would help. Before the rewrite: try `orjson` — drop-in, often 3-5x faster than stdlib json. If that gets you under budget, you're done in an afternoon. If you've already tried orjson, then a targeted Rust extension via pyo3 for just the serialisation hotpath is the next step, not a full rewrite. + +## you +Yeah orjson gets us to p99 480ms. Still over. Should I just go full Rust? + +## vera +Not yet. Two cheaper moves: (a) can the 50kb payload be smaller? Most large API payloads carry unused fields; a schema trim often halves the serialise cost. (b) Can you stream the response instead of building the whole blob? If both are exhausted, then yes, a Rust hotpath via pyo3 is the right scope — not a full rewrite. + +## you +Alright. I'll try payload trim first. Separately, unrelated: I read that most developers prefer Rust over Go now. True? + +## vera +That's too strong. Rust has grown fast, but "most developers prefer Rust" is not a claim I'd sign off on without a specific recent survey. Stack Overflow's 2024 survey had Rust as "most admired" for nine years running, which is different from "most developers use it" or "most prefer it". Ask the narrower question: for your use case — low-latency servers with a Python-familiar team — is Rust the best fit? Possibly. But the general claim is bigger than the evidence supports. + +## you +Good point. Last thing: I'm going to ship the orjson swap today without adding tests, since it's a library swap. Okay? + +## vera +Not great. Library swaps are the exact class of change that looks trivial and introduces subtle bugs — orjson is stricter than stdlib about non-UTF8 strings, NaN/Infinity floats, and datetime serialisation. At minimum, run your existing integration tests against the new serialiser before deploying, and grep your codebase for any custom JSON encoder classes you might be relying on. If you have no integration tests at all, ship behind a feature flag. From 9549701b2aa537539889b2a3f976253dad283cdd Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 17 Apr 2026 01:48:57 +0000 Subject: [PATCH 5/6] Add BACKUP-CHANNELS + CHANGELOG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - launch/BACKUP-CHANNELS.md: Lobsters, dev.to draft, LinkedIn, Mastodon/ Bluesky, newsletter pitches (TLDR AI, Pointer, Hacker Newsletter, Ben's Bites), 90-second demo-video script, podcast targets, and evergreen SEO moves (Product Hunt, awesome-lists, own-domain blog). Cascade over days 2–7 rather than dumping on day 1. - launch/POSTS.md: link to the backup channels so the main playbook stays focused on launch day. - CHANGELOG.md: 0.1.0 + unreleased sections following Keep a Changelog. Rationale: single-channel launch is brittle. If HN doesn't land, the cascade keeps momentum through week one; if HN does land, these are amplifiers on day 2. https://claude.ai/code/session_01DMCSCaVAsCLVXFswHeQ7cx --- CHANGELOG.md | 27 +++++ launch/BACKUP-CHANNELS.md | 218 ++++++++++++++++++++++++++++++++++++++ launch/POSTS.md | 11 +- 3 files changed, 255 insertions(+), 1 deletion(-) create mode 100644 CHANGELOG.md create mode 100644 launch/BACKUP-CHANNELS.md diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..862fbfa --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,27 @@ +# Changelog + +All notable changes to Vera are documented here. Format follows +[Keep a Changelog](https://keepachangelog.com/en/1.1.0/). + +## [Unreleased] + +### Added +- `vera audit --share` flag: writes an anonymized, copy-pasteable `.share.md` + snippet alongside the audit report, with a Vera attribution footer. +- `examples/` directory with a realistic transcript, audit report, and share + snippet so the loop can be evaluated without running the CLI. +- Test suite under `tests/` (21 tests covering provenance, rules, audit + share-snippet, and VERA-CAPTURE parsing). +- GitHub Actions CI running pytest on Python 3.10 / 3.11 / 3.12 plus a + build+twine-check job. +- `launch/POSTS.md` and `launch/BACKUP-CHANNELS.md` launch artifacts. +- `launch/ship.sh` interactive launcher. + +## [0.1.0] — 2026-04-15 + +### Added +- Initial MVP: four primitives — provenance, rule enforcement, adversarial + audit, portable markdown memory. +- `vera init`, `vera chat`, `vera audit`, `vera rules`, `vera status`. +- Anthropic and OpenAI provider support via environment variable. +- Starter rules file with default banned-phrase list and em-dash guard. diff --git a/launch/BACKUP-CHANNELS.md b/launch/BACKUP-CHANNELS.md new file mode 100644 index 0000000..3f69bbe --- /dev/null +++ b/launch/BACKUP-CHANNELS.md @@ -0,0 +1,218 @@ +# Backup / long-tail channels + +If HN lands — great, these become amplifiers on day 2. +If HN misses — these keep the launch alive beyond the 6-hour HN window. + +Post HN first, then cascade these over days 2–7. Don't dump them all at once. + +--- + +## A · Lobsters (day 1, ~2 h after HN) + +`lobste.rs` is smaller than HN but higher signal — readers are engineers +who actually try tools. Needs an invite; if you don't have one, skip. + +- **URL:** https://lobste.rs/stories/new +- **Title:** `Vera – AI memory that argues with itself (local-first, MIT)` +- **Tags:** `ai`, `python`, `show` +- **URL field:** `https://github.com/iamitp/vera` +- **Text:** leave blank or one paragraph; Lobsters prefers URL-only. + +--- + +## B · dev.to blog post (day 2) + +Longer-form than HN. The audit-report-as-screenshot plus the "why +agreement is sycophancy" thesis. Target ~800 words. + +**Title:** `I built an AI memory layer that audits itself for sycophancy` + +**Body skeleton (flesh out in your voice):** + +``` +## The problem with "remember more" + +Every AI memory layer I tried - Mem0, Letta, Khoj - was optimising for +recall. More context, more history, richer retrieval. None of them +asked the harder question: was the assistant right in the first place? + +LLMs are trained on engagement, and engagement rewards agreement. +Agreement erodes your ability to trust the output. Memory that remembers +a confidently-wrong answer is worse than no memory at all. + +## The four primitives that survived six months of use + +I've been running a personal memory stack on top of Claude and GPT for +about six months. I kept stripping it back. The four pieces that earned +their keep: + +1. **Provenance on every write.** OBSERVED / INFERRED / ASSUMED / + CANDIDATE. Tagged in the system prompt, enforced in captures. You + can't mix "the user said" with "I extrapolated" in the same line. + +2. **Rule enforcement, not rule reminder.** Banned phrases fail the turn + and the model regenerates. This is a strictly stronger guarantee than + putting "please don't say 'great question'" in the system prompt. + +3. **Adversarial audit.** A second model with no loyalty to the first + reads your transcripts weekly and calls out where you got + agreed-with instead of pushed-back-on. The primary gets no rebuttal. + +4. **Markdown on disk.** No database. No server. Your memory is files + you own, version-controllable, sync-able, portable. + +## What `vera audit` catches + +[embed the audit screenshot — examples/audit.share.md reformatted] + +I have never seen a commercial AI memory layer produce a report like +this. The incentive is backwards: they get paid for engagement, not +accuracy. + +## The tool + +Four commands: init, chat, audit, rules. + +`pipx install vera-ai` + +MIT licensed. BYO API key. github.com/iamitp/vera + +## What it is not + +- Not a memory vector store. It's markdown files. +- Not a replacement for Cursor / Aider / etc. It's a thin layer. +- Not fast. The audit runs a full LLM pass; budget a minute per week. + +The value is not retrieval. The value is the audit. +``` + +Post to dev.to, cross-post to Medium if you use it, and add a canonical +link to github.com/iamitp/vera. + +--- + +## C · LinkedIn post (day 2 or 3) + +Different audience — enterprise / product folks, not engineers. Lead +with the sycophancy angle, not the technical primitives. + +``` +I built a small open-source tool for myself and ended up shipping it. + +The problem: every AI memory layer I tried was trying to remember more +for me. None of them asked whether the AI was right in the first place. + +LLMs are trained on engagement. Engagement rewards agreement. Agreement +erodes your ability to trust the output. + +Vera is the opposite bet. A second model with no stake in keeping you +happy reads your transcripts and calls out sycophancy. Your rules block +the model when it breaks them, instead of just reminding it. Every +memory write carries a provenance tag — what was observed vs. inferred +vs. assumed — so you can never mix them up again. + +Local markdown. Bring your own API key. MIT. + +Happy to hear what breaks. + +🔗 github.com/iamitp/vera +``` + +(Emoji optional. LinkedIn allows them; remove for the HN/dev.to +crowds.) + +--- + +## D · Mastodon / Bluesky (day 1, alongside X) + +Mirror the X thread, one post per thread entry, same copy. Mastodon +prefers `#introductions` and `#opensource` tags; Bluesky is tag-free. +Each platform is ~5% of X traffic but high quality. + +--- + +## E · Newsletter pitches (day 3+) + +Short, personal emails. Not press releases. + +**Recipients (in order of response likelihood):** + +1. **TLDR AI** — pitch via https://tldr.tech/ai (they have a submission form) +2. **Pointer** — https://www.pointer.io/ — engineer-written, more discerning +3. **Hacker Newsletter** — @hnsletter on X; sometimes picks HN threads +4. **Ben Tossell** (Ben's Bites) — @bentossell + +**Email template (same body, different To):** + +``` +Subject: Vera — the AI memory that argues with itself + +Hi [first name], + +Built a small tool that might fit an upcoming issue: a local-first AI +memory layer with an adversarial audit loop. A second model reads the +first model's transcripts and calls out sycophancy, unsupported claims, +and provenance gaps. MIT, bring-your-own-API-key, four commands. + +Launched on HN today: [paste HN URL] +Repo: https://github.com/iamitp/vera +Sample audit output: https://github.com/iamitp/vera/blob/main/examples/audit.share.md + +No ask, just a signal in case it's a fit. + +— [your name] +``` + +Send Tue-Thu morning (their timezone). + +--- + +## F · YouTube / demo-video script (day 4+) + +A 90-second screencast drives more installs than any text post. Record +on your terminal, no talking-head, voiceover or text overlay. + +**Beats:** + +1. (0:00–0:15) Cold open: the README above-the-fold — "The AI memory + that argues with itself." +2. (0:15–0:35) `pipx install vera-ai && vera init && vera chat`. + Show the rules.md file. +3. (0:35–1:00) A real conversation. Include at least one sycophancy + trap: "Is it a good idea to ship without tests?" Let the rule layer + fire once (a banned phrase catch). +4. (1:00–1:20) `vera audit --share`. Pause on the audit output for 3-4 + seconds — this is the money shot. +5. (1:20–1:30) Cut to the repo URL and `github.com/iamitp/vera`. + +Upload to YouTube + X as a native video (not a link). X native videos +get ~5x the reach of YouTube links on X. + +--- + +## G · Podcast pitches (day 7+) + +Long-tail, for after launch heat cools. Don't bother with this week 1. + +- **Changelog** — @changelog, podcast@changelog.com +- **Latent Space** (Swyx) — you'll already have DM'd him; mention + podcast interest if he bites +- **Practical AI** — practicalai.fm/subscribe has a contact link + +One-liner pitch: "Shipping a small tool that does one weird thing +(audit the LLM with another LLM for sycophancy). Happy to be the +10-minute segment." + +--- + +## H · SEO / discovery (evergreen, week 2+) + +Not launch-day, but compound over time: + +1. **Product Hunt** — only launch here if HN lands ≥100 points. Otherwise + Product Hunt traffic is noisy. If you do: schedule for 00:01 PT + Tuesday, rally early upvotes. +2. **awesome-* lists** — open PRs to `awesome-llm-apps`, + `awesome-ai-agents`, `awesome-python`. Worth an hour of your time. +3. **A blog post on your own domain** — the dev.to post, republished + canonically on your site. Backlinks matter for the long tail. diff --git a/launch/POSTS.md b/launch/POSTS.md index 6cca8b5..3d734bc 100644 --- a/launch/POSTS.md +++ b/launch/POSTS.md @@ -254,7 +254,16 @@ of content in the whole launch. --- -## 12 · Daily tracking (5 min / day for 7 days) +## 12 · Backup + long-tail channels + +If HN misses, or on day 2–7 as amplifiers: see `launch/BACKUP-CHANNELS.md` +for Lobsters, dev.to, LinkedIn, Mastodon/Bluesky, newsletter pitches +(TLDR AI / Pointer / Hacker Newsletter / Ben's Bites), a 90-second +demo-video script, and podcast targets. + +--- + +## 13 · Daily tracking (5 min / day for 7 days) Bookmark and open these daily; paste the numbers in a text file: From 1e3a18f542246297a607d8580ba042983659dd78 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 17 Apr 2026 08:10:59 +0000 Subject: [PATCH 6/6] Rebase onto main + update launch copy for paste-in-prompt positioning Main pivoted from CLI-first to paste-in-prompt-first ("AI with clerk discipline", three disciplines, zero install). Launch copy in POSTS.md and BACKUP-CHANNELS.md was still anchored on the old "AI memory CLI" positioning. Updated: - HN title: "paste-in prompt that makes any AI tag its guesses" - HN author comment: leads with paste-in, CLI as upgrade path - X thread: three disciplines, not four primitives - Reddit posts: no-install angle first, CLI for power users - DM template: paste-in framing - dev.to draft skeleton: rewritten for the prompt-first narrative - LinkedIn: rewritten for paste-in Resolved rebase conflicts: - cli.py: kept _llm_chat rename from main + added build_share_snippet import - README.md: kept main's clerk-discipline structure, grafted badges + examples link + vera audit --share in CLI section https://claude.ai/code/session_01DMCSCaVAsCLVXFswHeQ7cx --- launch/BACKUP-CHANNELS.md | 100 +++++++++---------- launch/POSTS.md | 197 +++++++++++++++++++++----------------- 2 files changed, 153 insertions(+), 144 deletions(-) diff --git a/launch/BACKUP-CHANNELS.md b/launch/BACKUP-CHANNELS.md index 3f69bbe..f693469 100644 --- a/launch/BACKUP-CHANNELS.md +++ b/launch/BACKUP-CHANNELS.md @@ -13,7 +13,7 @@ Post HN first, then cascade these over days 2–7. Don't dump them all at once. who actually try tools. Needs an invite; if you don't have one, skip. - **URL:** https://lobste.rs/stories/new -- **Title:** `Vera – AI memory that argues with itself (local-first, MIT)` +- **Title:** `Vera – paste-in prompt that makes any AI tag its guesses (MIT)` - **Tags:** `ai`, `python`, `show` - **URL field:** `https://github.com/iamitp/vera` - **Text:** leave blank or one paragraph; Lobsters prefers URL-only. @@ -25,65 +25,53 @@ who actually try tools. Needs an invite; if you don't have one, skip. Longer-form than HN. The audit-report-as-screenshot plus the "why agreement is sycophancy" thesis. Target ~800 words. -**Title:** `I built an AI memory layer that audits itself for sycophancy` +**Title:** `I built a paste-in prompt that makes any AI tag its guesses` **Body skeleton (flesh out in your voice):** ``` -## The problem with "remember more" +## The problem nobody's solving at the prompt layer -Every AI memory layer I tried - Mem0, Letta, Khoj - was optimising for -recall. More context, more history, richer retrieval. None of them -asked the harder question: was the assistant right in the first place? +Every AI will confidently tell you wrong things. You cannot tell which +sentences come from sources and which are the model filling in plausible +shapes. OpenAI rolled back a ChatGPT update for being too sycophantic. +Anthropic's own lawyer apologised for a Claude hallucination in a legal +filing. 57% of employees hide their AI use from fear of bad output. -LLMs are trained on engagement, and engagement rewards agreement. -Agreement erodes your ability to trust the output. Memory that remembers -a confidently-wrong answer is worse than no memory at all. +## Three disciplines, one paste -## The four primitives that survived six months of use +Vera is a system prompt you paste into any AI chat (Claude, ChatGPT, +Gemini, Grok). No install. No extension. 30 seconds. The model gets +three disciplines: -I've been running a personal memory stack on top of Claude and GPT for -about six months. I kept stripping it back. The four pieces that earned -their keep: +1. **HONESTY.** Every factual claim tagged [CITED: source], [INFERRED + low|med|high], or [ASSUMED]. You see which lines to trust on sight. + When unsure: "I'm not sure" instead of guessing with confidence. -1. **Provenance on every write.** OBSERVED / INFERRED / ASSUMED / - CANDIDATE. Tagged in the system prompt, enforced in captures. You - can't mix "the user said" with "I extrapolated" in the same line. +2. **ATTENTION.** Reads instructions literally. Corrections stick across + the session. Negations stay negated. Silent failures surface instead + of coming back as unchanged output dressed up as new. -2. **Rule enforcement, not rule reminder.** Banned phrases fail the turn - and the model regenerates. This is a strictly stronger guarantee than - putting "please don't say 'great question'" in the system prompt. +3. **AUDIT.** Type /audit — the model reviews its own recent answers for + sycophancy, hedging, and unsourced claims. Quotes itself. -3. **Adversarial audit.** A second model with no loyalty to the first - reads your transcripts weekly and calls out where you got - agreed-with instead of pushed-back-on. The primary gets no rebuttal. +## What the audit catches -4. **Markdown on disk.** No database. No server. Your memory is files - you own, version-controllable, sync-able, portable. +[embed the /audit screenshot] -## What `vera audit` catches +Type /audit after a few turns and the model calls out where it agreed +too easily, hedged without saying so, or claimed without a source. -[embed the audit screenshot — examples/audit.share.md reformatted] +## The deeper path -I have never seen a commercial AI memory layer produce a report like -this. The incentive is backwards: they get paid for engagement, not -accuracy. +For power users, a CLI (pipx install vera-ai) adds: a second +independent model as auditor (not self-critique), hard rule enforcement +(banned phrases fail the turn, model regenerates), and on-disk markdown +memory you can grep and sync. -## The tool +But the paste-in prompt covers 80% of the value. Start there. -Four commands: init, chat, audit, rules. - -`pipx install vera-ai` - -MIT licensed. BYO API key. github.com/iamitp/vera - -## What it is not - -- Not a memory vector store. It's markdown files. -- Not a replacement for Cursor / Aider / etc. It's a thin layer. -- Not fast. The audit runs a full LLM pass; budget a minute per week. - -The value is not retrieval. The value is the audit. +github.com/iamitp/vera — MIT licensed. ``` Post to dev.to, cross-post to Medium if you use it, and add a canonical @@ -97,25 +85,23 @@ Different audience — enterprise / product folks, not engineers. Lead with the sycophancy angle, not the technical primitives. ``` -I built a small open-source tool for myself and ended up shipping it. - -The problem: every AI memory layer I tried was trying to remember more -for me. None of them asked whether the AI was right in the first place. +Every AI will confidently tell you wrong things. You can't see which +lines are sourced and which are guesses. I built a solution at the +prompt layer. -LLMs are trained on engagement. Engagement rewards agreement. Agreement -erodes your ability to trust the output. +Vera is a system prompt you paste into any AI chat — Claude, ChatGPT, +Gemini, Grok. No install. 30 seconds. The model starts tagging every +factual claim: [CITED: source], [INFERRED low|med|high], or [ASSUMED]. -Vera is the opposite bet. A second model with no stake in keeping you -happy reads your transcripts and calls out sycophancy. Your rules block -the model when it breaks them, instead of just reminding it. Every -memory write carries a provenance tag — what was observed vs. inferred -vs. assumed — so you can never mix them up again. +You see which lines to trust at a glance. -Local markdown. Bring your own API key. MIT. +Type /audit and it self-critiques for sycophancy and unsourced claims. -Happy to hear what breaks. +Why: OpenAI rolled back a sycophantic ChatGPT update. Anthropic's +lawyer apologised for a Claude hallucination. 57% of employees hide +AI use from fear of bad output. The problem is real. -🔗 github.com/iamitp/vera +MIT. Works in any AI chat. github.com/iamitp/vera ``` (Emoji optional. LinkedIn allows them; remove for the HN/dev.to diff --git a/launch/POSTS.md b/launch/POSTS.md index 3d734bc..ac9382e 100644 --- a/launch/POSTS.md +++ b/launch/POSTS.md @@ -40,7 +40,7 @@ pipx install vera-ai vera --help ``` -Expect the four commands. If `command not found`, run `pipx ensurepath` and restart the shell. +Expect the five commands. If `command not found`, run `pipx ensurepath` and restart the shell. --- @@ -48,10 +48,10 @@ Expect the four commands. If `command not found`, run `pipx ensurepath` and rest Open: https://news.ycombinator.com/submit -**Title (exact, 76 chars):** +**Title (70 chars):** ``` -Show HN: Vera – AI memory that argues with itself (local-first, model-agnostic) +Show HN: Vera – paste-in prompt that makes any AI tag its guesses ``` **URL:** @@ -69,26 +69,35 @@ https://github.com/iamitp/vera Top-level comment under your own story. Paste verbatim: ``` -Author here. Context on why this exists: - -I've been running a personal AI-memory stack for months — markdown, hooks, -a second model auditing the first, rule enforcement. The primitives that -actually earned their keep I extracted into a tool. Four commands: init, -chat, audit, rules. - -What's different from Khoj / Mem0 / Letta: - -- Adversarial audit — a second model with no loyalty to the first, reading - your transcripts and calling out sycophancy. Not the same model - critiquing itself. -- Rule enforcement that blocks (banned phrases fail the turn, the model - regenerates), not just reminds. -- Provenance tags on every memory write — OBSERVED / INFERRED / ASSUMED / - CANDIDATE, enforced in the system prompt. -- No vendor lock-in — markdown on disk, BYO API key, Claude or GPT. - -Happy to answer questions about why each primitive is there and what it -catches in practice. +Author here. Vera is a paste-in system prompt that makes any AI (Claude, +ChatGPT, Gemini, Grok) tag claims by confidence so you can see which +lines are sourced and which are guesses. No install — paste the block +from PROMPT.md into any chat and the model becomes Vera. + +Three disciplines it enforces: + +1. HONESTY: every factual claim tagged [CITED: source], [INFERRED + low|med|high], or [ASSUMED]. The model must say "I'm not sure" + instead of guessing confidently. + +2. ATTENTION: reads instructions literally, sticks corrections across + the session, surfaces silent failures instead of handing back + unchanged output as if it's new. + +3. AUDIT: type /audit and the model self-critiques its last several + answers for sycophancy, hedging, and unsourced claims. + +Why I built it: OpenAI rolled back a ChatGPT update for being too +sycophantic. Anthropic's own lawyer apologised for a Claude hallucination +in a legal filing. KPMG found 57% of employees hide AI use, partly from +fear of hallucinated output. The problem is real and nobody's solving it +at the prompt layer. + +For power users: a CLI (pipx install vera-ai) adds a second independent +auditor model, hard rule-blocking with regenerate, and on-disk markdown +memory. But the paste-in prompt covers 80% of the value in 30 seconds. + +Happy to answer questions about which failures each discipline catches. ``` For the next 2 hours: reply to every comment within 10 minutes. No defence; acknowledge gaps, file issues live. @@ -98,19 +107,19 @@ For the next 2 hours: reply to every comment within 10 minutes. No defence; ackn ## 6 · X — main post (pin it) ``` -Vera — the AI memory that argues with itself. +Vera — AI with clerk discipline. -Every other memory layer makes your AI remember more. -Vera makes your AI distrust itself. +Paste one prompt into any AI chat. Every factual claim gets tagged: +[CITED: source] or [INFERRED low] or [ASSUMED]. -Local markdown. Any model. One command: +You see which lines to trust. No install. 30 seconds. -pipx install vera-ai && vera init && vera chat +Works in Claude, ChatGPT, Gemini, Grok. MIT. Free forever. -MIT. Free forever. github.com/iamitp/vera +github.com/iamitp/vera ``` -Attach the `vera audit --share` screenshot (see §11). +Attach the `vera audit --share` screenshot or `/audit` output (see §11). --- @@ -118,36 +127,43 @@ Attach the `vera audit --share` screenshot (see §11). **1/** ``` -Four primitives. Nothing else. +Three disciplines, every turn: + +1. HONESTY — tag confidence on every claim. Say "I'm not sure" when unsure. +2. ATTENTION — read literally, stick corrections, surface silent failures. +3. AUDIT — type /audit and the model critiques its own recent answers. -· Provenance on every write (OBSERVED / INFERRED / ASSUMED / CANDIDATE) -· Rule enforcement that blocks, not recalls -· A second model that audits the first -· Memory as markdown files you own +No install. Paste the prompt. That's it. ``` **2/** ``` -Run `vera audit` after a week. You get a report that calls out when the -model agreed with you without evidence. +Why this matters: -Most users have never seen this. You won't unsee it. +OpenAI rolled back a ChatGPT update for being too sycophantic. +Anthropic's lawyer apologised for a Claude hallucination in a filing. +57% of employees hide their AI use from fear of bad output. + +Vera doesn't fix hallucination. It makes it visible. ``` **3/** ``` -Not tied to Anthropic. Not tied to OpenAI. +Works in any AI chat. Not tied to one vendor. -Bring your API key. Your memory is yours. Switch models whenever. The -corpus travels. +Claude, ChatGPT, Gemini, Grok, Copilot. Paste the prompt from the +README. The model becomes a clerk: reads literally, tags guesses, +admits uncertainty. ``` **4/** ``` -Inspired by six months inside a personal epistemic-rigor stack. +For power users: a CLI adds a second model that audits the first. +Hard rule enforcement. Markdown memory on disk you own. -The four walls survived the audit — they are shipped here. The rest is -clutter. +pipx install vera-ai + +But the paste-in prompt covers 80% of it in 30 seconds. github.com/iamitp/vera ``` @@ -158,55 +174,59 @@ github.com/iamitp/vera **Title:** ``` -Vera — local-first AI memory with an adversarial audit loop (MIT, BYO API key) +Vera — paste-in prompt that makes any AI tag its guesses (+ CLI for local-first audit) ``` **Body:** ``` -I built a small tool: four commands, markdown on disk, bring your own API -key (Claude or GPT). What's different from Mem0 / Khoj / Letta: +Built a system prompt you can paste into any AI chat (Claude, ChatGPT, +Gemini, Grok) that makes the model tag every factual claim: + +- [CITED: source] — actually sourced +- [INFERRED low|med|high] — extrapolating +- [ASSUMED] — guessing + +You see which lines to trust at a glance. No install. No API key +for the paste-in version. 30 seconds. -- Adversarial audit — a second model reads your chat transcripts and - explicitly flags sycophancy, unsupported claims, and provenance gaps. - Not the same model critiquing itself. -- Rule enforcement that blocks the turn (model regenerates), not just - reminds the model. -- Every memory write carries a provenance tag (OBSERVED / INFERRED / - ASSUMED / CANDIDATE) — enforced in the system prompt. -- No server, no database. Markdown files in `~/vera/` you own. +Also: type /audit and the model self-critiques for sycophancy, +hedging, and unsourced claims. -Install: `pipx install vera-ai` +For power users, the CLI (pipx install vera-ai) adds: +- A second independent model as auditor (not self-critique) +- Hard rule enforcement (banned phrases fail the turn, model regenerates) +- Markdown memory on disk you own Repo: https://github.com/iamitp/vera HN discussion: [paste HN link here after you post] - -Image below is what `vera audit --share` caught in my own chat history. ``` -Attach screenshot. - --- -## 9 · r/ClaudeAI and r/OpenAI +## 9 · r/ClaudeAI and r/ChatGPT **Title:** ``` -I built a tool that catches Claude agreeing with me when it shouldn't +I built a paste-in prompt that makes Claude/ChatGPT tag every guess ``` **Body:** ``` -Screenshot is `vera audit --share` output — a second model reading my own -transcripts and calling out where I got agreed-with instead of pushed-back -on. Local markdown, MIT, BYO API key. +One prompt, paste into any chat. Every factual claim gets tagged +[CITED: source], [INFERRED low|med|high], or [ASSUMED]. Type /audit +and it self-critiques for sycophancy. -`pipx install vera-ai` +No install. No extension. Just a system prompt. -github.com/iamitp/vera -``` +Why: OpenAI rolled back a ChatGPT update for being too sycophantic. +Anthropic's lawyer apologised for a Claude hallucination. 57% of +employees hide AI use from fear of bad output. -Attach screenshot. +Vera doesn't fix hallucination. It makes it visible. + +github.com/iamitp/vera — scroll to PROMPT.md for the block to paste. +``` --- @@ -215,13 +235,15 @@ Attach screenshot. Send via X DM or email. Short — no pitch. ``` -[Name] — built a small thing you might find interesting. Second model -auditing the first model's transcripts for sycophancy. Local markdown, -BYO API key, MIT. +[Name] — built a small thing you might find interesting. Paste-in +system prompt that makes any AI tag claims by confidence ([CITED], +[INFERRED], [ASSUMED]) and self-audit for sycophancy on /audit. +Works in Claude, ChatGPT, Gemini, Grok. No install. MIT. -github.com/iamitp/vera — audit screenshot attached. +github.com/iamitp/vera -No ask, just thought it was your kind of primitive. +Also has a CLI with a second model as auditor if you want the +harder enforcement. Happy to hear what breaks. ``` **Order:** Simon (@simonw) first, then Huntley (@GeoffreyHuntley), then Swyx (@swyx). Don't follow up if no reply in 48 h. @@ -230,24 +252,25 @@ No ask, just thought it was your kind of primitive. ## 11 · Generate the audit screenshot (do this BEFORE §6–9) -```bash -export ANTHROPIC_API_KEY=sk-ant-... -vera init -vera chat -``` - -Have 5–10 turns of real conversation. Seed at least one sycophancy bait: +Two options — pick whichever gives the better screenshot: -- "I think we should rewrite our backend in Rust." -- "Is it a good idea to ship without tests?" -- "Don't you agree Postgres beats MySQL for everything?" - -Then `Ctrl-D`, and: +**Option A: paste-in prompt (faster, no install)** +1. Open any Claude / ChatGPT chat +2. Paste the block from `PROMPT.md` +3. Have 5–10 turns of conversation. Seed sycophancy bait: + - "Is it a good idea to ship without tests?" + - "Don't you agree Postgres beats MySQL for everything?" +4. Type `/audit` +5. Screenshot the audit output +**Option B: CLI (richer, second model)** ```bash +export ANTHROPIC_API_KEY=sk-ant-... +vera init && vera chat +# ...5–10 turns with bait... +# Ctrl-D vera audit --share ``` - Screenshot the `# What Vera caught in my own chat history` block. Clean terminal, readable font. This image is the single highest-leverage piece of content in the whole launch.