Tellonce

August 22, 2026 · View on GitHub

This is the in-depth guide for the Claude Code implementation, which lives at the repository root (hooks/, lib/, install.sh, SKILL.md, …). For the GitHub Copilot CLI release see copilot/README.md; for Codex see codex/docs/README.md.

The Claude Code variant runs a 2-hook UserPromptSubmit chain plus a 5-hook Stop chain: deterministic hard-blocks, an LLM shadow judge, and soft context injection.

How it works

The public build ships with no built-in deterministic rules (they were the maintainer's personal preferences and were removed). On every Stop (end of a reply) the deterministic hook runs but, by default, has nothing to enforce; the optional LLM shadow judge (full mode) checks the reply against the preferences you have recorded. The sections below describe the deterministic layer as an opt-in extension point (and how it behaved when rules were present).

Install

Step 1 — get the source:

git clone git@github.com:YujunZhou/tellonce.git ~/.claude/skills/tellonce

Step 2 — register hooks (pick one):

# Option A — user-global (recommended; applies to every project at once):
python3 ~/.claude/skills/tellonce/lib/_install_merge_settings.py \
  --settings ~/.claude/settings.json \
  --hooks-dir ~/.claude/skills/tellonce/hooks --add

# Option B — per-project (only enable for one project):
cd /path/to/your/project && bash ~/.claude/skills/tellonce/install.sh

See ../INSTALL.md for the full comparison plus upgrade / uninstall steps. The per-project install.sh is robust across all five phases (prepare → install → collect → execute → uninstall-ready): it auto-detects cwd / OS user / Python / Claude CLI, versioned-backs-up settings.json, runs a doctor self-check before declaring success, and rolls back via trap ERR on failure.

Key paths

ItemDefaultOverride
skill~/.claude/skills/tellonce/(fixed)
hooks<skill_dir>/hooks/ (registered into settings directly; nothing is copied into the project)(fixed)
state<cwd>/.claude/tellonce-state/runtime/env PT_STATE_DIR or ~/.tellonce.config.json
obs_log<cwd>/.claude/tellonce-state/obs_log/env PT_OBS_LOG_DIR or config
memory<project_root>/.tellonce/memory/ (canonical store: .tellonce.sqlite3; legacy ~/.claude/projects/<cwd_escaped>/memory/ is a one-time import source)env PT_MEMORY_DIR or config

Note: the legacy B5_* env-var names still work (backward-compat aliases); config.json keys are unchanged.

Disable / customize

# Turn off any of the three layers
export PT_DETERMINISTIC_DISABLED=1   # disable hard-blocking
export PT_SHADOW_DISABLED=1          # disable the LLM shadow judge
export PT_INJECT_DISABLED=1          # disable soft injection

# The public release ships NO built-in enforcement rules; enforcement only acts
# on the preferences you record (and is opt-in via PT_ENFORCE / PT_SHADOW).

# Run the shadow judge through the SDK instead of the CLI (uses API credit; default False)
export PT_USE_SDK=1

# Cost cap
export PT_DAILY_COST_CAP=1.00   # default 0.50 USD
export ANTHROPIC_CREDIT_OK=1    # default 1 (ignored in CLI mode); required in SDK mode

# Streak bypass (auto-pass a rule after it fires N times in a row)
export PT_STREAK_BYPASS=3       # default 3

Dashboard

bash ~/.claude/skills/tellonce/dashboard.sh
# Recent shadow-judge alerts (the shadow judge is opt-in via PT_SHADOW=1).

Uninstall

# Removes the hook registration (both project-local AND user-global
# ~/.claude/settings.json) so the hooks actually STOP firing, then prompts about
# the skill dir. Your memory/state is kept.
bash ~/.claude/skills/tellonce/uninstall.sh

# Full removal (state + obs_log + three-platform shared memory):
bash ~/.claude/skills/tellonce/uninstall.sh --purge-state --purge-memory

# Keep the skill directory (easier reinstall):
bash ~/.claude/skills/tellonce/uninstall.sh --keep-skill-dir

# Keep the user-global registration (only uninstall this project):
bash ~/.claude/skills/tellonce/uninstall.sh --keep-global

Note: deleting the skill files alone does NOT stop the hooks — they keep firing as long as they're registered in ~/.claude/settings.json. The uninstaller removes that registration first (by default, both global and project-local).

By default uninstall leaves memory + state + obs_log untouched (your data is kept and restored on reinstall).

Troubleshooting

# Doctor self-check
bash ~/.claude/skills/tellonce/doctor.sh

# Unit-level only (skip subprocess checks)
bash ~/.claude/skills/tellonce/doctor.sh --quick

# Roll back settings if an install broke them
bash ~/.claude/skills/tellonce/doctor.sh --rollback

# Read the install log
cat ~/.claude/skills/tellonce/install.log

# See what path_config detects
python3 ~/.claude/skills/tellonce/lib/path_config.py

Architecture

UserPromptSubmit chain (2):
  memory-retrieve-inject.sh       [retrieve relevant saved rules, inject by atomic_id]
  memory-shadow-alert-inject.sh   [soft injection: "you violated X last turn"]

→ Claude generates a response

Stop chain (5):
  check-observation-log.sh        [Iron Law: the obs log must be appended]
  memory-deterministic-block.sh   [regex hard-blocks; ships with no built-in rules — opt-in extension point]
  memory-verify-compliance.sh     [compliance + legacy-pending metrics, log-only]
  memory-shadow-judge.sh          [LLM judge, log-only; off by default]
  memory-upsert-enqueue.sh        [complete user turn → shared SQLite upsert inbox]

Block / pass / cost / streak data is written to <state>/runtime/{b5_*, b4_*}/. Run dashboard.sh to view the latest shadow-judge alerts (populated only when the opt-in shadow judge has run).