Earned auto-apply (fire + observability + UI), retrain cadences, Explore arrow-nav #143

Merged
bvandeusen merged 8 commits from dev into main 2026-06-29 07:30:41 -04:00
Owner

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

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)
bvandeusen added 8 commits 2026-06-29 07:30:33 -04:00
feat(explore): ← / → keyboard navigation through the walk
CI / lint (push) Successful in 3s
CI / frontend-build (push) Successful in 19s
CI / backend-lint-and-test (push) Successful in 27s
CI / integration (push) Successful in 3m25s
353b5d8087
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
feat(heads): nightly auto-retrain + inline Retrain button in Explore
CI / lint (push) Successful in 3s
CI / frontend-build (push) Successful in 19s
CI / backend-lint-and-test (push) Successful in 26s
CI / integration (push) Successful in 3m26s
77baee49fd
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
feat(heads): earned auto-apply — sweep mechanism, off by default (#114 auto-apply A)
CI / lint (push) Failing after 3s
CI / frontend-build (push) Successful in 19s
CI / backend-lint-and-test (push) Successful in 26s
CI / integration (push) Successful in 3m21s
74fef908d2
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
style(test): drop unused img in ungraduated-head sweep test (ruff F841)
CI / lint (push) Successful in 3s
CI / frontend-build (push) Successful in 19s
CI / backend-lint-and-test (push) Successful in 26s
CI / integration (push) Successful in 3m23s
01933c5b26
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Ttrj5P7upUTueSfoJcxEqa
feat(heads): auto-apply observability + on by default (#114 auto-apply B)
CI / lint (push) Successful in 3s
CI / frontend-build (push) Successful in 19s
CI / backend-lint-and-test (push) Successful in 26s
CI / integration (push) Failing after 3m25s
48c8811d69
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
fix(heads): .all() before dict() in snapshot_head_metrics
CI / lint (push) Successful in 2s
CI / frontend-build (push) Successful in 17s
CI / backend-lint-and-test (push) Successful in 26s
CI / integration (push) Failing after 3m24s
9326a82b29
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
fix(test): disable switch explicitly now that auto-apply defaults ON
CI / lint (push) Successful in 2s
CI / frontend-build (push) Successful in 18s
CI / backend-lint-and-test (push) Successful in 26s
CI / integration (push) Successful in 3m29s
a5a95320df
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
feat(heads): auto-apply UI on the Concept-heads card (#114 auto-apply C)
CI / lint (push) Successful in 2s
CI / frontend-build (push) Successful in 20s
CI / backend-lint-and-test (push) Successful in 26s
CI / integration (push) Successful in 3m23s
1463794778
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
bvandeusen merged commit bc0f00c51b into main 2026-06-29 07:30:41 -04:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: bvandeusen/FabledCurator#143