TigerHarness
A vendor-agnostic agent harness for Claude Code and OpenAI Codex: iterative task execution, Slack integration, and persistent memory management.
Docs:
docs/INDEX.mdis the home — it routes you to the right doc in one hop (and separates current reference from design history).
Sub-packages
| Package | Description |
|---|---|
tigerharness.agent_sdk |
Backend-agnostic agent SDK. Same caller code, swappable runtimes: the claude -p subprocess backend, the codex exec subprocess backend (OpenAI's Codex CLI), and Anthropic's claude-agent-sdk backend, with a shared typed event/retry/error model. Which one a persona runs on is declared per persona in configs/personas.yaml (vendor: claude | chatgpt, with a team default) — see ADR 0011. |
tigerharness.journal |
File-based subscription backend. Routes agent work through the interactive agent app (Claude Code or Codex, per persona) so it counts against a monthly subscription instead of token-billed API. Single-persona tasks (kind=task) and multi-persona workflows (kind=workflow) compiled in-session by a drafter/two-critic loop over mechanical validators, then walked through gates that enforce step order and write persona-stamped worklog notes (the per-persona memory rail). Crash-safe by lease: tasks classify idle/busy/crashed and a fresh session resumes a crashed walk at the same step. Team-pinned scheduling: every task records the journal root it was scheduled into (provenance), scheduling verbs refuse to fall back silently to the per-user journal, and the sweep flags misplaced tasks. A deferred/ inbox makes Slack-side scheduling cheap: journal defer parks the conversation verbatim; journal materialize (inside a drive) turns it into a real task. Drive lanes: every task, workflow step and inbox entry runs on its owner persona's vendor/model — claim refuses other-lane work (exit 3) and step-done prints a handoff: cue (ADR 0012); whether a Slack session may drive at all is a team setting, TIGERHARNESS_JOURNAL_SLACK_DRIVES (ADR 0013). 20 CLI verbs under journal. See docs/journal.md, docs/journal-workflow-mode.md, docs/journal-instant-resume.md. |
tigerharness.autodrive |
Periodic journal driver (the Operator-authorized exception to the human-only drive rule). A detached daemon fires "drive the journal" on a fixed cadence via the backend-agnostic agent_sdk — one drive per vendor/model lane that has work, on that lane's backend (ADR 0012) — fire every N seconds without waiting, so drives may overlap; each fire is a fresh, context-clean session; not built on /loop. Overlap is safe and self-limiting because the journal's busy lease makes a redundant fire a cheap no-op. Before each fire it runs the journal's plain-Python sweep itself, so a busy tick is skipped and an idle tick costs a file walk instead of a model session. Self-driving when opted in (TIGERHARNESS_AUTODRIVE_AUTOSTART in the team's configs/.env): scheduling work starts the daemon, and a drained queue — after the idle memory sweep + context compaction — stops it, so steady state is no process running. Only safe while each vendor CLI (claude -p, codex exec) bills its subscription, so it ships with a --max-budget cap on Claude lanes (Codex has no per-run budget flag; mind the N×-concurrent multiplier), a 60s interval floor, an atomically-locked single-instance-per-team daemon, and an autodrive stop off-switch. See docs/autodrive.md and ADR 0010. |
tigerharness.slack_bridge |
Slack Socket Mode bridge. Forwards DMs to each persona's vendor backend (claude -p or codex exec, resolved from configs/personas.yaml) and posts replies back to the thread. See docs/slack-bridge.md. |
tigerharness.tiger_memory |
Persistent bounded agent memory: three self-pruning stores (skills/must_remember/topics) projected into an index-only session-start briefing, with forgetting via staged compaction (ADR 0007). Includes the team-wide sweep protocol (sweep-plan/sweep-done/sweep-complete/sweep-release under a lease, watermark, and per-wake cap), a lazy team event log (ADR 0008), and subscription-rail staging (plan, ingest-extraction/ingest-staged) so memory extraction bills to the subscription. Sources: Claude Code transcripts (claude_code), Codex session rollouts (codex), Slack threads and journal worklogs; a team sweep can be restricted to one vendor/model lane (sweep-plan --lane-of). See docs/tiger-memory.md, docs/tiger-memory-sweep-protocol.md. |
Bundled agent skills (Claude Code + Codex)
tigerharness init installs seven skills into a new team's
.claude/skills/ — one vendor-neutral copy that Claude Code reads there
and Codex reads through the .agents/skills symlink: drive-journal
(the subscription drive loop), journal-new (task/workflow
scaffolding), journal-autodrive (start/stop the periodic journal
driver), sweep-memory (the team-wide memory sweep), slack-notify
(proactive Slack messages), workflow-append-steps (runtime
graph extension), and tigerharness-basics (how to operate the team
itself: the CLI, the file layout, recruiting personas, creating
workflows). Refreshes are hash-aware: a skill that still
matches a previously shipped version is updated in place, while a
hand-edited skill is left alone (src/tigerharness/init.py).
Installation
Pick the strategy that matches how you want to use tigerharness:
Option A — Scope it to one folder (recommended for teams)
Best when you want a self-contained teams/ directory that owns its
own tigerharness install — nothing global, nothing borrowed:
mkdir -p ~/projects/teams && cd ~/projects/teams
uv init --bare # creates a minimal pyproject.toml
uv add 'tigerharness[all]' # adds dep, creates .venv + uv.lock
uv run tigerharness init # interactive team scaffolder
tigerharness only exists inside this folder's .venv; from then on
you invoke it with uv run tigerharness ....
Option B — Install as a global CLI
Best when you want tigerharness available everywhere, like git or gh:
uv tool install 'tigerharness[all]' # one-time; puts `tigerharness` on PATH
tigerharness init # works from any directory
Equivalent with pipx: pipx install 'tigerharness[all]'.
Option C — Traditional pip into an active venv
python -m venv .venv && source .venv/bin/activate
pip install 'tigerharness[all]'
tigerharness init
Choosing extras
tigerharness has zero hard dependencies by design. Optional features
are gated behind extras so you don't pay disk/install cost for things
you don't use:
| Extra | Pulls in | Enables / required for |
|---|---|---|
| (none) | — | init scaffolder, dismiss teardown, the journal subscription backend, autodrive (its Slack notices need [slack]), and the claude_p / codex_exec subprocess backends |
[anthropic] |
claude-agent-sdk |
The official Claude Agent SDK backend (anthropic_sdk) |
[slack] |
slack-bolt, aiohttp, python-dotenv |
slack-bridge (Slack Socket Mode DM bridge) |
[memory] |
pyyaml |
tiger-memory (per-persona persistent memory) and reading configs/personas.yaml for per-persona vendor/model resolution (without it every persona falls back to claude) |
[all] |
union of everything above | Everything works out of the box |
Pick the union that matches what you'll use, e.g.
'tigerharness[slack,memory,anthropic]' for a Slack-fronted agent with
persistent memory and the official SDK backend.
Heads up: the install commands quote the extras (
'tigerharness[all]') because zsh treats[as a glob character. In bash you can drop the quotes, but quoting always works.
Quick start
Scaffold a team and its first persona
tigerharness init is interactive — it walks you through:
- Persona name + team (pick an existing team or create one).
- Optional toggles: the Slack
.envtemplate, multi-team Slack mode (recommended for new setups), and the per-persona memory config. - For a new team: which model vendor it defaults to — Claude
(
claude -p) or ChatGPT (codex exec) — and an optional default model. Written toconfigs/personas.yaml;--vendor/--modelskip the prompt and--yestakes Claude. Any persona can override with its ownvendor:/model:in that file. - The new team's goal (one sentence, seeds the charter's Mission) and
the persona's initial traits (recorded in its prompt) —
--goal/--traitsskip the prompts. - Slack user-ID allowlist for the team's bridge bot (optional).
The memory store is auto-initialized for each new persona with both transcript sources wired in (the Claude Code project path, auto-detected from the team root, and the Codex sessions tree), so the user never has to come back and edit placeholders. Every persona always belongs to a team, and a team is a self-contained directory.
Every team gets two governance folders alongside the runtime config:
charter/ holds the team's operating manual (mission, scope, allowed
write zones, working conventions, first-read checklist for new
personas) and knowledge/ holds the team's curated, lazy-loaded
reference base. The generated persona prompt wires both into each
persona's first-read flow, so they're load-bearing from day one
rather than decorative.
tigers/
├── AGENTS.md # vendor-neutral session bootstrap (which persona you are,
│ # where the manual lives, the runtime glossary)
├── CLAUDE.md # imports AGENTS.md so Claude Code loads it
├── .gitignore # secrets + runtime state (topped up by `init --refresh`)
├── .claude/
│ ├── settings.json # wires TIGERHARNESS_PERSONAS_CONFIG
│ └── skills/ # bundled skills (drive-journal, journal-new,
│ # journal-autodrive, slack-notify, sweep-memory,
│ # workflow-append-steps, tigerharness-basics)
├── .agents/
│ └── skills -> ../.claude/skills # symlink: Codex discovers the same skills
├── configs/
│ ├── personas.yaml # team registry + default model vendor (auto-updated)
│ ├── repos.yaml # path map: team_root + the project the team works on
│ ├── tiger-memory.defaults.yaml # team-wide memory defaults (per-persona configs inherit)
│ ├── slack-bridge.yaml # this team's bridge lane fragment (multi-team mode)
│ └── .env # Slack tokens + team knobs (gitignored)
├── charter/
│ └── README.md # team's mission, scope, conventions
│ # (single entry point for personas)
├── knowledge/
│ └── README.md # curated, lazy-loaded reference base
├── skills/
│ └── README.md # drop team-shared skills here
├── personas/
│ ├── chief/
│ │ └── prompt.md # the persona's system prompt
│ └── scout/
│ └── prompt.md
└── memories/
├── chief/
│ └── tiger-memory.config.yaml # per-persona memory config
│ # (claude_code + codex sources)
└── scout/
└── tiger-memory.config.yaml
Persona and team names are space-separated words: letters, digits,
-, and _, each word starting with a letter or digit, single
spaces between words (so chief, scout-7, and Chuan Ying are all
valid; leading, trailing, or consecutive spaces are not). Paths
printed by init are shell-quoted when a name contains a space.
# Fully interactive — prompts for persona name, team, slack/memory opts
tigerharness init
# Non-interactive — creates team 'tigers' with persona 'chief'
tigerharness init --persona chief --team tigers --yes
# Names may contain single internal spaces
tigerharness init --persona 'Chuan Ying' --team tigers --yes
# Add a second persona to the same team
tigerharness init --persona scout --team tigers --yes
# Skip Slack or memory generation
tigerharness init --persona chief --team tigers --no-memory --no-slack --yes
Tear down a team or persona
tigerharness dismiss is the symmetric counterpart of init — it
removes a team (or a single persona inside a team) and all of the
associated state: configs, prompts, per-persona memory data, the
multi-team index entry, and — for the last team in a multi-team
setup — the root's per-root slack-bridge-<root>-<hash> systemd user
unit too (discovered by content, so older slack-bridge-multi-* names
and the legacy global slack-bridge-multi.service are found as well).
The command is always interactive and gated behind two confirmations (a backup acknowledgement and a type-the-name check), so it's hard to fire by accident:
# Walks you through: pick team-or-persona → preview → backup confirm
# → type the name → execute. Out of scope: deleting the Slack app on
# api.slack.com (the command prints a manual reminder).
tigerharness dismiss
# Same flow but exits after the preview — useful for checking what
# would happen without touching anything.
tigerharness dismiss --dry-run
Refusals are deliberate, not bugs:
- Dismissing the last persona of a team is refused — use team-level dismissal instead, or add another persona first.
- Dismissing a persona that's the team's
default_personain the Slack-bridge fragment is refused — pick a new default in the fragment first, then re-run.
Journal (subscription backend)
# 1. Point tigerharness at your team's persona registry
export TIGERHARNESS_PERSONAS_CONFIG=./tigers/configs/personas.yaml
# 2. Scaffold a task for a persona (the drive-journal skill works it)
tigerharness journal new --kind task --persona chief --prd brief.md
# 3. Check the queue
tigerharness journal list
# 4. Sweep state (archives done, classifies in-progress)
tigerharness journal sweep
Slack bridge
One bridge process serves 1..N teams (lanes) — one Slack app per team,
separate bot identities, separate threads.json. A single team is just a
one-lane index.
# 1. Fill in each team's Slack tokens in <team>/configs/.env (from api.slack.com)
# SLACK_APP_TOKEN=xapp-... SLACK_BOT_TOKEN=xoxb-... SLACK_ALLOWED_USER_IDS=U0123ABC
# 2. Create a lanes index listing your team(s):
printf 'lanes:\n - tigers\n' > slack-bridge.yaml
# 3. Point the bridge at it and run:
export TIGERHARNESS_BRIDGES_CONFIG=$PWD/slack-bridge.yaml
python -m tigerharness.slack_bridge
tigerharness init auto-registers each new team's lane.
TIGERHARNESS_BRIDGES_CONFIG is required: the former single-tenant
fallback was removed on 2026-08-11 (ADR 0009), and the bridge now fails
fast at startup with a migration pointer when it is unset. See
docs/slack-bridge.md
for the full setup and
migrating off single-tenant.
Tiger memory
Each persona has its own memory config under
tigers/memories/<persona>/tiger-memory.config.yaml. init fills in
both transcript sources (the Claude Code project path detected from the
team root, and the Codex sessions tree), so it works as-is; to also
ingest the persona's journal work, add a journal_worklog source (see
docs/per-persona-journal-memory.md).
Then:
--config is a top-level option for the tiger-memory sub-command, so
it must appear before the verb:
# Save typing — the same path is reused everywhere
CFG=tigers/memories/chief/tiger-memory.config.yaml
# 1. Initialize the memory store (per persona)
tigerharness tiger-memory --config $CFG init
# 2. Rebuild the session-start briefing (no model call; the in-session
# `sweep-memory` skill does the actual extraction)
tigerharness tiger-memory --config $CFG rebuild
# 3. Search memory
tigerharness tiger-memory --config $CFG search "solar energy"
# 4. Pin a must-memorize fact
tigerharness tiger-memory --config $CFG pin "Prefers solar over wind"
Configuration
All paths are resolved from environment variables -- no hardcoded paths.
"Where" says which file a variable belongs in: the team configs/.env
(per-team, read by the bridge lane, the journal and autodrive; never
pollutes the process env), the bridge process env (multi-bridge.env
via systemd EnvironmentFile=), or the session / process env of
whatever CLI or agent session is running.
| Variable | Where | Default | Description |
|---|---|---|---|
TIGERHARNESS_BRIDGES_CONFIG |
bridge process env | (none; required for the bridge) | Path to the slack-bridge.yaml lanes index (see docs/slack-bridge.md) |
TIGERHARNESS_PERSONAS_CONFIG |
.claude/settings.json (written by init) or session env |
(none) | Path to the team's configs/personas.yaml for journal verbs run inside a session |
SLACK_ALLOWED_USER_IDS |
team configs/.env |
(none) | Comma-separated Slack user IDs the bridge answers; the notify CLI's DM target |
SLACK_NOTIFY_CHANNEL |
team configs/.env |
(none → operator DM) | Ops-log channel for proactive notifies, turn-progress heartbeats and autodrive notices (invite the bot to it) |
TIGERHARNESS_JOURNAL_SLACK_DRIVES |
team configs/.env |
unset (off) | 1 lets a Slack-triggered session journal claim (drive) without --allow-api-drive; off means Slack schedules, never drives (ADR 0013) |
TIGERHARNESS_AUTODRIVE_AUTOSTART |
team configs/.env |
unset (off) | 1 makes scheduling start the autodrive daemon and a drained queue stop it (ADR 0010) |
TIGERHARNESS_AUTODRIVE_INTERVAL |
team configs/.env |
600 |
Seconds between daemon fires (floor 60) |
TIGERHARNESS_AUTODRIVE_MAX_BUDGET |
team configs/.env |
(none = uncapped) | USD cap handed to each drive's backend |
TIGERHARNESS_AUTODRIVE_DRIVER |
team configs/.env |
the team's default persona | --driver persona for daemon drives; its vendor/model is the daemon's home lane |
TIGERHARNESS_AUTODRIVE_NOTIFY, TIGERHARNESS_AUTODRIVE_NOTIFY_CHANNEL |
team configs/.env |
slack; channel falls back to SLACK_NOTIFY_CHANNEL, then the operator DM |
slack or none: daemon heartbeats + threaded drive summaries; the channel value dm pins them to the operator DM (see docs/autodrive-notifications.md) |
TIGERHARNESS_SLACK_ENV |
notify CLI process env | (none) | Explicit .env path for the notify CLI's credential lookup and the turn-progress heartbeats' env fallback |
TIGERHARNESS_ATTACHMENT_DIR |
bridge process env | /tmp/slack-attachments |
Where to stage downloaded files |
TIGER_MEMORY_CONFIG |
session / process env | (none) | Path to tiger-memory YAML config |
TIGER_MEMORY_CLI |
team configs/.env |
(none → tiger-memory on PATH) |
tiger-memory CLI binary for the bridge's post-thread rebuild |
TIGERHARNESS_JOURNAL_DIR |
session / process env | the team root's journal/ when the cwd is a team dir, else $XDG_STATE_HOME/tigerharness-journal (~/.local/state/…) |
Overrides the journal root (autodrive sets it for the drives it launches) |
TIGERHARNESS_JOURNAL_STUCK_TIMEOUT |
session / process env | 1800 (30 min) |
Heartbeat age (seconds) past which the journal sweep treats an attached in_progress task as crashed (below it, busy) |
TIGERHARNESS_LOG_LEVEL |
session / process env | WARNING (notify: INFO) |
Log level for every tigerharness CLI and daemon |
Examples
See examples/ for a fully-populated sample team folder
(examples/tigers/) and standalone reference configs:
examples/tigers/-- sample team scaffolded bytigerharness init --yes(regenerated, never hand-edited; see CONTRIBUTING)tiger-memory.config.yaml-- annotated memory config (standalone)env.example-- teamconfigs/.envtemplate: Slack tokens + team knobs (standalone)slack-bridge-multi.service-- reference systemd unit for the multi-team bridge
Requirements
- Python 3.11+
- For the default
claude_pbackend: the Claude Code CLI (claude) onPATH. - For the
codex_execbackend (personas withvendor: chatgpt): the Codex CLI (codex) onPATH, signed in withcodex login. - For the
anthropic_sdkbackend: install with[anthropic]extra; pulls inclaude-agent-sdk.
Known limitations & roadmap
Gaps we've hit in real use, tracked here so they can be picked up later. None of these block normal use once the listed workaround is applied.
Bridge boot environment
claudenot found on PATH when the bridge auto-starts at boot. On distros where the Claude Code CLI is installed outside/usr/bin(e.g. NixOS at/run/current-system/sw/bin/, npm-global at~/.npm-global/bin/, pipx at~/.local/bin/), the systemd unit emitted bytigerharness slack-bridge gen-servicehas noEnvironment=PATH=...line. Restarting from an interactive shell works (the rich PATH is inherited from the live user session), but a cold boot auto-start sees only the minimal systemd PATH (/usr/bin:/bin), soshutil.which("claude")in the SDK fails withbackend error: \claude` not found on PATH`.- Workaround: a systemd drop-in at
~/.config/systemd/user/<your-bridge-unit>.service.d/path.conf(e.g.slack-bridge-teams-4a8c8b.service.d/) containing[Service]\nEnvironment="PATH=/run/current-system/sw/bin:/usr/bin:/bin"(adapted to the local install location). Drop-ins survivegen-serviceregeneration. - Fix candidates: (a)
gen-serviceemits a sensible defaultEnvironment=PATH=covering common install locations; (b) add aCLAUDE_CLIenv var the bridge reads and forwards ascli=toClaudePBackend(), mirroring the existingTIGER_MEMORY_CLIknob. - Same shape for
codex(personas onvendor: chatgpt): the Codex CLI's standalone install lands in~/.local/bin, which the drop-in must also list.
- Workaround: a systemd drop-in at
- Same shape applies to the
tiger-memorybinary used by the bridge's post-thread rebuild trigger. The existingTIGER_MEMORY_CLIenv var already provides the per-team-.envworkaround, but a PATH default ingen-servicewould fix both at once.
Bridge setup ergonomics
-
gen-servicereferencesmulti-bridge.envbut doesn't create it. The generated systemd unit hasEnvironmentFile=<teams-root>/multi-bridge.env, and the bridge won't start without that file existing (systemd skips a missing optional EnvironmentFile silently, but the bridge then fails because$TIGERHARNESS_BRIDGES_CONFIGis unset). The user has to manuallyecho "TIGERHARNESS_BRIDGES_CONFIG=/path/to/slack-bridge.yaml" > <teams-root>/multi-bridge.envas a separate step aftergen-service. Fix candidate: havegen-serviceeither emit the env file alongside the unit (skipping if it already exists), or at least print a clear "you must now create this file with the following contents" hint. -
Two
.envfiles with non-obvious, very different semantics. In multi-team mode there are two distinct env files:multi-bridge.env(referenced by systemdEnvironmentFile=): loaded into the bridge process'sos.environ. Used only for the bootstrap pointerTIGERHARNESS_BRIDGES_CONFIG. Adding other env vars here does not reach per-lane behavior, because the per-team loader reads from disk into a separate dict.- per-team
configs/.env(referenced by the YAML index's per-laneenv:key): loaded via_load_env_fileinto a per-lane dict that deliberately does not polluteos.environ. This is where Slack tokens,TIGER_MEMORY_CLI,SLACK_NOTIFY_CHANNEL,TIGERHARNESS_PERSONAS_CONFIG, and any agent-facing env vars go.
The Configuration table above carries a "where it goes" column for exactly this reason.
Bridge shutdown
- The drain budget has only ever been exercised with one turn in flight. On shutdown the bridge waits up to
_DRAIN_TIMEOUT_Sfor its in-flight turns to finish, and that wait is released by a singleself._drained.set()that fires when the in-flight counter reaches zero. The counter can only exceed 1 with concurrent dispatches, which no test creates — so the "still in flight, do not release yet" side of that check has never run, and it sits behind a# pragma: no branchthat keeps the 100% coverage gate green over it. The margins in the budget chain (STOP_DRAIN_S + FINISH_POST_S <= _DRAIN_TIMEOUT_S <= TimeoutStopSec) exist precisely for the multi-turn case, so the case they were sized for is the one with no coverage. See "The drain budget is a chain, not a number" for the full ordering and the reasoning behind each margin.- Fix candidate: a test with two concurrent dispatches in which the first to finish must not set
_drained. That reaches the untested branch and lets the pragma come off. - Why it isn't already written — so this doesn't read as neglect. The gap was found and documented by a documentation step, and the pragma predates that branch, so writing a new test there would have shipped unreviewed new behaviour from a docs seat. The reason it is recorded rather than closed is a scope boundary, not an oversight or a difficulty. The test is small; whoever picks it up should expect it to be.
- The branch is unexercised, not unreachable — this matters if you are cleaning up pragmas. The standing rule at a 100% branch floor is to delete a branch the design made unreachable rather than pragma over it. That rule does not apply here: concurrent dispatches genuinely reach the "still in flight" side, no test creates them. Delete the pragma once the test above exists; do not delete the branch.
- Not known to misbehave — this is an untested path, not a reported bug.
- Fix candidate: a test with two concurrent dispatches in which the first to finish must not set
Agent Slack notifications
- Notifications require the bot to be in
SLACK_NOTIFY_CHANNEL. Each team has its own Slack app (own bot user). After creating a new team's Slack app and settingSLACK_NOTIFY_CHANNELinconfigs/.env, the bot must be invited to that channel (/invite @BotNamein Slack). Without this,chat.postMessagereturnschannel_not_foundand notifications are silently skipped. The notify CLI logs the error to stderr but doesn't surface it to the user.- Fix candidate:
tigerharness initcould print a reminder ("Don't forget to invite your bot to the ops-log channel"), or the notifier could log a more prominent first-time warning. - This now also covers autodrive, which inherits
SLACK_NOTIFY_CHANNELas the last layer of its notify-channel chain. A team that set the key but never invited the bot can decline the inheritance withTIGERHARNESS_AUTODRIVE_NOTIFY_CHANNEL=dmand keep its daemon heartbeats in the operator DM. See autodrive.md.
- Fix candidate:
- Agents need
.claude/settings.json+ skills to send proactive DMs. Agents use theslack-notifyskill to send per-iteration updates viapython -m tigerharness.slack_bridge.notify. Without.claude/settings.json(which wiresTIGERHARNESS_PERSONAS_CONFIG) and.claude/skills/slack-notify/SKILL.md(which teaches the agent the notify CLI exists), the agent doesn't know how to send Slack messages. As of v0.2.1+,tigerharness initscaffolds these automatically for new teams. Existing teams adopt them withtigerharness init --refresh, which installs any missing skills, refreshes un-customized ones to the latest, recreates the.agents/skillssymlink if missing, and tops up.gitignorewith any newly-shipped rule — without clobbering hand edits. --threadmust be passed when notifying from a Slack thread. When an agent sends a proactive message from a Slack DM thread, it must pass--thread <slack_thread_ts>(from the[bridge-context]block) so notifications land in the right thread. Without it, notifications go to a new top-level message instead of the conversation thread. Theslack-notifyskill documents this prominently, but it's easy to forget.
tigerharness init
- Auto-init of the tiger-memory store fails intermittently with
CalledProcessErrorduringtigerharness init's last step. Runningtigerharness tiger-memory --config <path> initby hand afterward always succeeds, suggesting environment propagation in thesys.executable -m tigerharness ...subprocess is the moving part. A direct in-process call would remove it.
License
MIT
Release files for TigerHarness 0.6.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| tigerharness-0.6.1.tar.gz | 1.2 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| tigerharness-0.6.1-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 1.8 MB
Release files / tigerharness-0.6.1.tar.gz
| Download URL | tigerharness-0.6.1.tar.gz |
|---|---|
| Size | 1.2 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
343e945c2633f2012b3c0dbea078c10b4c75d64ca0e42520e474e503528a8f27
|
|
BLAKE2b-256 checksum How to use checksums |
6f742c9ac77bdac320fdbef361474b48a2dd31bfad4d50fc0275e57b0e8a4c48
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 14, 2026.
Transparency logRelease files / tigerharness-0.6.1-py3-none-any.whl
| Download URL | tigerharness-0.6.1-py3-none-any.whl |
|---|---|
| Size | 601.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
fdcc8d5d08074259d6a64144a895faef876167b0c131c77a5186a4e48e020a69
|
|
BLAKE2b-256 checksum How to use checksums |
a0f0242609bd47c63cbfa951e63da20122d0e5c7cccc091f5af591a4c6e6f39c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 14, 2026.
Transparency log