Skip to content

Commit ef39514

Browse files
Shahinyanmclaude
andcommitted
feat(v0.8.0): hybrid classifier (heuristic + API), drop cli backend
BREAKING: --backend=cli is deprecated. Default is now --backend=hybrid. Background: Anthropic changed `claude -p` to bill against a separate token budget instead of riding the Pro/Max subscription, so v0.7.x's `cli` backend silently charged users who had explicitly opted into a "free fallback". This release replaces it with a heuristic-first hybrid that runs zero-network pattern matching on common phrasing cues, and falls back to the Anthropic API only when the heuristic is uncertain. New backends: - hybrid (default): heuristic → API fallback if ANTHROPIC_API_KEY set - heuristic: pattern-only, no LLM, zero cost / offline - api: always call the Anthropic API (best quality, paid) The cli backend value remains accepted as a legacy alias; it routes to hybrid and prints a one-line stderr deprecation warning. Will be removed in v0.9.0. Plugin hooks.json and install-hooks settings.json template both drop the explicit --backend=cli flag; the binary's `hybrid` default wins. Pattern coverage (EN+RU): Decision, Rejection, Evidence, Finding, Constraint, Hypothesis, Correction. Heuristic catches the obvious cases — file:line references, "we decided to", "won't work", "test passed", "rate limit", etc. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 34715cc commit ef39514

12 files changed

Lines changed: 518 additions & 21 deletions

File tree

CHANGELOG.md

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,57 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [0.8.0] - 2026-05-17
11+
12+
**Breaking — classifier backend reshaped.** Anthropic changed `claude -p`
13+
to bill against a separate token budget instead of riding the Pro/Max
14+
subscription, so the v0.7.x `--backend=cli` path silently charged users
15+
who had explicitly opted into "free background". v0.8.0 removes it from
16+
the default path and replaces it with a heuristic-first hybrid.
17+
18+
### Added
19+
- New `--backend=hybrid` (now the default). Keyword heuristic runs
20+
first — handles obvious decisions, rejections, evidence, findings,
21+
constraints, hypotheses, corrections in EN+RU at zero cost. If the
22+
heuristic is uncertain (or no rule fires), falls back to the
23+
Anthropic API backend when `ANTHROPIC_API_KEY` is set; otherwise
24+
the chunk stays in `pending/` for later retry. No `claude -p`
25+
subprocess.
26+
- New `--backend=heuristic` — heuristic only, no LLM. For users who
27+
want strict zero-cost / offline operation and accept lower coverage.
28+
- `tj_core::classifier::heuristic::try_heuristic` — public helper for
29+
pattern-matched classification, returning `Option<ClassifyOutput>`.
30+
- `tj_core::classifier::hybrid::HybridClassifier` — production
31+
default. Exposes `from_env()` (picks up `ANTHROPIC_API_KEY`) and
32+
`has_llm_fallback()` for callers that want to surface state to the
33+
user.
34+
35+
### Changed
36+
- Default backend in `task-journal ingest-hook` and
37+
`task-journal classify-worker` changed from `cli` to `hybrid`.
38+
- `install-hooks` settings.json template no longer pins
39+
`--backend=cli`; the binary default wins.
40+
- Plugin `hooks.json` (PostToolUse / PreCompact / Stop) no longer
41+
passes `--backend=cli`. Same default-wins reasoning.
42+
43+
### Deprecated
44+
- `--backend=cli` now routes to `hybrid` and prints a one-line
45+
deprecation warning on stderr. The `ClaudeCliClassifier` struct
46+
stays in `tj_core::classifier::cli` for the v0.7.x eval harness
47+
but is no longer reachable from production code. Will be removed
48+
in v0.9.0.
49+
50+
### Migration
51+
- **Users with Pro/Max only and no API key** — keep working: the
52+
heuristic catches the most common cases; chunks it can't classify
53+
land in `pending/` and you can drain them later when you set an
54+
API key (or just leave them — they don't block anything).
55+
- **Users with `ANTHROPIC_API_KEY` set** — best experience. Heuristic
56+
saves API calls on obvious cases; the API handles the rest.
57+
- **No action required.** Old `~/.claude/settings.json` with
58+
`--backend=cli` still works (alias). Re-run `install-hooks` to
59+
remove the deprecation warning.
60+
1061
## [0.7.1] - 2026-05-17
1162

1263
PreCompact closes the gap before compaction — the synchronous hook only

Cargo.lock

Lines changed: 3 additions & 3 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ members = [
77
]
88

99
[workspace.package]
10-
version = "0.7.1"
10+
version = "0.8.0"
1111
edition = "2021"
1212
rust-version = "1.88"
1313
license = "MIT"

crates/tj-cli/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ name = "task-journal"
1616
path = "src/main.rs"
1717

1818
[dependencies]
19-
tj-core = { package = "task-journal-core", version = "0.7.1", path = "../tj-core" }
19+
tj-core = { package = "task-journal-core", version = "0.8.0", path = "../tj-core" }
2020
anyhow = { workspace = true }
2121
clap = { workspace = true }
2222
tracing = { workspace = true }

crates/tj-cli/src/main.rs

Lines changed: 58 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -813,10 +813,15 @@ enum Commands {
813813
/// tool_name+input+response for PostToolUse, etc.).
814814
#[arg(long)]
815815
text: Option<String>,
816-
/// Classifier backend: "cli" uses `claude -p` (free with your Pro/Max
817-
/// subscription) or "api" uses Anthropic API (requires `ANTHROPIC_API_KEY`).
818-
/// Default: cli.
819-
#[arg(long, default_value = "cli")]
816+
/// Classifier backend:
817+
/// - "hybrid" (default) — keyword heuristic first (free, offline);
818+
/// Anthropic API fallback when uncertain (needs ANTHROPIC_API_KEY).
819+
/// - "api" — always call the Anthropic API. Best quality, paid.
820+
/// - "heuristic" — heuristic only, no LLM. Fastest, lowest coverage.
821+
/// - "cli" — deprecated alias for hybrid. The old `claude -p` path
822+
/// was removed in v0.8.0 because Anthropic now bills it
823+
/// separately from Pro/Max.
824+
#[arg(long, default_value = "hybrid")]
820825
backend: String,
821826
/// Test/dev override: bypass classifier and force this event type. Hidden from --help.
822827
#[arg(long, hide = true)]
@@ -835,8 +840,8 @@ enum Commands {
835840
/// at a time. Hidden from --help; not a public API.
836841
#[command(hide = true)]
837842
ClassifyWorker {
838-
/// Classifier backend: "cli" or "api". Defaults to cli.
839-
#[arg(long, default_value = "cli")]
843+
/// Classifier backend: "hybrid", "api", or "heuristic". Defaults to hybrid.
844+
#[arg(long, default_value = "hybrid")]
840845
backend: String,
841846
},
842847
/// One-line status snapshot for the Claude Code statusline. Prints
@@ -1363,7 +1368,11 @@ fn main() -> Result<()> {
13631368
// at env vars Claude Code never sets and therefore always
13641369
// fed the classifier empty text. Stdin-only is the correct
13651370
// wiring (see claude-memory-rsw).
1366-
let cmd = "task-journal ingest-hook --backend=cli || true";
1371+
// No --backend flag: the binary's default (hybrid) wins.
1372+
// Hybrid = free heuristic first, Anthropic API fallback when
1373+
// uncertain. Users wanting always-api can edit settings.json
1374+
// and add `--backend=api`.
1375+
let cmd = "task-journal ingest-hook || true";
13671376
let entries = serde_json::json!({
13681377
"UserPromptSubmit": [{ "matcher": "", "hooks": [{ "type": "command", "command": cmd }] }],
13691378
"PostToolUse": [{ "matcher": "", "hooks": [{ "type": "command", "command": cmd }] }],
@@ -1789,13 +1798,52 @@ fn main() -> Result<()> {
17891798

17901799
use tj_core::classifier::Classifier;
17911800
let classifier: Box<dyn Classifier> = match backend.as_str() {
1792-
"cli" => Box::new(tj_core::classifier::cli::ClaudeCliClassifier::default()),
1801+
// v0.8.0: hybrid is the new default. Heuristic
1802+
// pattern-matching first (free), Anthropic API
1803+
// fallback when uncertain (requires ANTHROPIC_API_KEY).
1804+
// No background spawn of `claude -p` — that subprocess
1805+
// now bills tokens separately from Pro/Max.
1806+
"hybrid" | "" => Box::new(
1807+
tj_core::classifier::hybrid::HybridClassifier::from_env(),
1808+
),
1809+
// Legacy alias — keeps existing settings.json + plugin
1810+
// hooks installed by 0.7.x working without manual edits.
1811+
// Same dispatch as hybrid; the warning lands on stderr
1812+
// which Claude Code captures into hook logs.
1813+
"cli" => {
1814+
eprintln!(
1815+
"warn: --backend=cli is deprecated (claude -p no longer rides Pro/Max). Routing to --backend=hybrid."
1816+
);
1817+
Box::new(
1818+
tj_core::classifier::hybrid::HybridClassifier::from_env(),
1819+
)
1820+
}
17931821
"api" => {
17941822
Box::new(tj_core::classifier::http::AnthropicClassifier::from_env()?)
17951823
}
1796-
other => {
1797-
anyhow::bail!("unknown backend: {other} (expected `cli` or `api`)")
1824+
"heuristic" => {
1825+
// Heuristic-only: no LLM at all. Trades coverage
1826+
// for absolute zero-cost / offline operation.
1827+
use tj_core::classifier::heuristic::try_heuristic;
1828+
use tj_core::classifier::{ClassifyInput, ClassifyOutput};
1829+
struct HeuristicOnly;
1830+
impl Classifier for HeuristicOnly {
1831+
fn classify(
1832+
&self,
1833+
input: &ClassifyInput,
1834+
) -> anyhow::Result<ClassifyOutput> {
1835+
try_heuristic(input).ok_or_else(|| {
1836+
anyhow::anyhow!(
1837+
"heuristic uncertain (heuristic-only mode has no LLM fallback)"
1838+
)
1839+
})
1840+
}
1841+
}
1842+
Box::new(HeuristicOnly)
17981843
}
1844+
other => anyhow::bail!(
1845+
"unknown backend: {other} (expected `hybrid`, `api`, or `heuristic`)"
1846+
),
17991847
};
18001848
let input = tj_core::classifier::ClassifyInput {
18011849
text: text.clone(),

0 commit comments

Comments
 (0)