Graduated heads now apply their tag without a human — on by default (opt-out), gated by ≥30 support + each head's own 97%-precision cutoff; every auto-tag is source='head_auto' and reversible.
Observability: misfire (you removed an auto-tag) + under-fire (you hand-added one a head missed) counters captured at correction time, a daily per-concept time-series, and GET /api/heads/metrics. Migration 0060.
UI on the Concept-heads card: enable switch, precision-target + min-examples inputs, Preview / Apply-now, and a per-concept "how it's landing" table (applied / misfires / rate / missed).
Retrain cadences
Nightly scheduled_train_heads beat (passive) + a "Retrain heads" button in the Explore trail bar (active), so your tagging feedback folds into the heads without a trip to Settings.
Explore ← / → navigation
Arrow keys walk the breadcrumb: ← back, → forward to a visited item or a random neighbour. Browser-style cursor (forward history preserved on back-step).
Deploy
Migrations 0059 + 0060 run automatically. Auto-apply is ON by default — after deploy + a training pass, the daily sweep applies tags from graduated heads. Disable on the card if you'd rather ease in; the metrics table is the dial to tune the precision target.
Builds on the heads-as-suggestion-source milestone (PR #142). Adds the rest of the tagging-v2 learn-from-tags loop.
## Earned auto-apply (#114)
Graduated heads now apply their tag without a human — **on by default (opt-out)**, gated by ≥30 support + each head's own 97%-precision cutoff; every auto-tag is `source='head_auto'` and reversible.
- Sweep + provenance + dry-run preview; daily scheduled sweep (no-op unless enabled) + manual trigger; recovery/retention. Migrations 0059.
- **Observability**: misfire (you removed an auto-tag) + under-fire (you hand-added one a head missed) counters captured at correction time, a daily per-concept time-series, and `GET /api/heads/metrics`. Migration 0060.
- **UI** on the Concept-heads card: enable switch, precision-target + min-examples inputs, Preview / Apply-now, and a per-concept "how it's landing" table (applied / misfires / rate / missed).
## Retrain cadences
Nightly `scheduled_train_heads` beat (passive) + a "Retrain heads" button in the Explore trail bar (active), so your tagging feedback folds into the heads without a trip to Settings.
## Explore ← / → navigation
Arrow keys walk the breadcrumb: ← back, → forward to a visited item or a random neighbour. Browser-style cursor (forward history preserved on back-step).
## Deploy
Migrations 0059 + 0060 run automatically. Auto-apply is ON by default — after deploy + a training pass, the daily sweep applies tags from graduated heads. Disable on the card if you'd rather ease in; the metrics table is the dial to tune the precision target.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Arrow keys walk the Explore breadcrumb trail: ← steps back, → goes forward to
an already-visited item or — with no forward history — jumps to a RANDOM
neighbour to keep the rabbit-hole going (operator-asked).
The trail gains a cursor (browser back/forward semantics): stepping back no
longer trims the forward branch, so → can return to it; a genuinely new walk
off a back-step truncates the stale branch then appends. The crumb-bar "current"
highlight follows the cursor, not the tip.
Arrows are ignored while typing a tag, but still navigate when the tag input is
focused-but-empty (it auto-focuses after every walk, so otherwise arrow-nav
would dead-end after one step). Modifier-key combos pass through untouched.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ttrj5P7upUTueSfoJcxEqa
Two cadences for keeping heads in sync with your tagging:
- PASSIVE: a nightly `scheduled_train_heads` beat (skips if a run is already
in flight; creates+commits the run row before dispatching train_heads so the
ml worker always finds it). Folds the day's accepts/rejects + newly-eligible
concepts into the heads without anyone clicking.
- ACTIVE: a "Retrain heads" button in the Explore trail bar — bank the +/-
feedback you just gave while walking content, without a trip to Settings.
Shared logic in a new useHeadTraining composable (trigger + poll + start/finish
toasts), used by the Explore button; reflects an already-running run (incl. the
nightly one) on mount.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ttrj5P7upUTueSfoJcxEqa
Graduated heads can now apply their tag without a human — gated so it's safe:
- FIRING GATE: a head fires only when the master switch (head_auto_apply_enabled,
default OFF) is on AND it has >= head_auto_apply_min_positives (default 30)
clean labels. A precise-looking but under-supported low-N head can't spray tags.
- auto_apply_sweep (heads.py): streams every embedded image in chunks, scores
against the eligible heads (numpy, no sklearn), applies each head's tag where
score >= its auto_apply_threshold and the tag isn't already applied/rejected,
with source='head_auto' (distinguishable + reversible). dry_run counts only.
- HeadAutoApplyRun (migration 0059) tracks each sweep / preview; apply_head_tags
task (ml queue) + scheduled_apply_head_tags daily beat (no-op unless enabled)
+ recovery sweep + retention(20).
- API: POST /api/heads/auto-apply {dry_run} (202 / 409 running / 400 disabled),
GET /api/heads/auto-apply (recent runs + per-concept report). Settings
head_auto_apply_enabled + min_positives via /api/ml/settings.
Tests: sweep applies above threshold, dry-run writes nothing, skips under-
supported + ungraduated heads; API disabled/dry-run/conflict guards.
NEXT (slice 2): the observability the operator asked for — per-concept misfire
(auto-applied-then-removed) + under-fire tracking, time-series snapshots, and a
reporting API to tune. Slice 3: the UI (enable, preview, trends).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ttrj5P7upUTueSfoJcxEqa
Auto-apply is now ON by default (operator-asked: opt-OUT, not opt-in) — migration
0059 + model default flipped. The support (>=30) + measured-precision gates keep
it safe and every auto-tag is reversible.
Observability so the operator can tune from real data:
- MISFIRE = an auto-applied (source='head_auto') tag the operator later removes.
UNDER-FIRE = a tag with a head the operator adds by hand (the head missed it).
Both captured at correction time in TagService.add_to_image/remove_from_image
(source is lost on delete) into durable per-tag counters (head_metric), keyed
by tag so they survive head retrain/prune.
- Daily snapshot_head_metrics writes a per-concept time-series point
(head_metrics_snapshot): auto-applied volume + cumulative misfires/under-fires
+ head quality; 180-day retention; daily beat.
- GET /api/heads/metrics: per-concept current counts + realized misfire rate +
head quality, plus the snapshot time-series — the report to tune the precision
target + support floor.
Migration 0060. Tests: misfire/under-fire counting (and the negatives — manual
removal isn't a misfire, headless manual add isn't an under-fire), snapshot
time-series, metrics API.
What's the autofire threshold? There's no single number — each graduated head
derives its OWN probability cutoff from its PR curve: the operating point that
holds precision >= head_auto_apply_precision (0.97) at max recall. The global
knobs are that target + the >=30 support floor.
NEXT (slice 3): UI — enable toggle, dry-run preview, per-concept trends.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ttrj5P7upUTueSfoJcxEqa
dict(session.execute(...)) on a bare Result invokes the mapping protocol (a
Result has .keys() = column names) and subscripts it → "CursorResult is not
subscriptable". Materialize with .all() so dict() consumes rows as key-value
pairs. The API path already did this; the snapshot task missed it. Caught by
test_snapshot_records_timeseries_point (run 1628).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ttrj5P7upUTueSfoJcxEqa
test_auto_apply_disabled_blocks_real_run assumed head_auto_apply_enabled
defaulted False; it now defaults True (opt-out), so a real sweep is accepted
(202). Set the switch off in the test to exercise the disabled→400 path.
(run 1629)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ttrj5P7upUTueSfoJcxEqa
Surfaces earned auto-apply + its observability in Settings → Tagging → Concept
heads:
- Auto-apply section: an on/off switch (writes head_auto_apply_enabled), the
precision-target + min-examples-to-fire tuning inputs, a Preview (dry-run →
"would apply N", per-concept chips) and Apply-now button, with live run state.
- "How auto-apply is landing": per-concept table from /api/heads/metrics —
applied volume, misfires, realized misfire rate (green/amber/red), and missed
(under-fires) — the signal to tune the precision target from.
store: autoApply(dryRun) / autoApplyStatus() / metrics(). Card polls the sweep
to completion, then refreshes counts + metrics. Completes the auto-apply task.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ttrj5P7upUTueSfoJcxEqa
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Builds on the heads-as-suggestion-source milestone (PR #142). Adds the rest of the tagging-v2 learn-from-tags loop.
Earned auto-apply (#114)
Graduated heads now apply their tag without a human — on by default (opt-out), gated by ≥30 support + each head's own 97%-precision cutoff; every auto-tag is
source='head_auto'and reversible.GET /api/heads/metrics. Migration 0060.Retrain cadences
Nightly
scheduled_train_headsbeat (passive) + a "Retrain heads" button in the Explore trail bar (active), so your tagging feedback folds into the heads without a trip to Settings.Explore ← / → navigation
Arrow keys walk the breadcrumb: ← back, → forward to a visited item or a random neighbour. Browser-style cursor (forward history preserved on back-step).
Deploy
Migrations 0059 + 0060 run automatically. Auto-apply is ON by default — after deploy + a training pass, the daily sweep applies tags from graduated heads. Disable on the card if you'd rather ease in; the metrics table is the dial to tune the precision target.
🤖 Generated with Claude Code
Graduated heads can now apply their tag without a human — gated so it's safe: - FIRING GATE: a head fires only when the master switch (head_auto_apply_enabled, default OFF) is on AND it has >= head_auto_apply_min_positives (default 30) clean labels. A precise-looking but under-supported low-N head can't spray tags. - auto_apply_sweep (heads.py): streams every embedded image in chunks, scores against the eligible heads (numpy, no sklearn), applies each head's tag where score >= its auto_apply_threshold and the tag isn't already applied/rejected, with source='head_auto' (distinguishable + reversible). dry_run counts only. - HeadAutoApplyRun (migration 0059) tracks each sweep / preview; apply_head_tags task (ml queue) + scheduled_apply_head_tags daily beat (no-op unless enabled) + recovery sweep + retention(20). - API: POST /api/heads/auto-apply {dry_run} (202 / 409 running / 400 disabled), GET /api/heads/auto-apply (recent runs + per-concept report). Settings head_auto_apply_enabled + min_positives via /api/ml/settings. Tests: sweep applies above threshold, dry-run writes nothing, skips under- supported + ungraduated heads; API disabled/dry-run/conflict guards. NEXT (slice 2): the observability the operator asked for — per-concept misfire (auto-applied-then-removed) + under-fire tracking, time-series snapshots, and a reporting API to tune. Slice 3: the UI (enable, preview, trends). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Ttrj5P7upUTueSfoJcxEqa