Skip to content

feat(skills): distillation reviews one conversation at a time — recurring requests are structurally invisible, so habitual skills can never be proposed #14030

Description

@mrveiss

Problem

Skill distillation reviews one conversation at a time, so a habitual request
is structurally invisible to it. The skills a user would most value — the ones
they ask for every week — can never be proposed.

Evidence

  • autobot-backend/services/skill_management/skill_extractor.py:86 —
    extract_skills(conversation_history, ...) takes a single conversation.
  • autobot-backend/services/skill_management/skill_distillation_scheduler.py —
    feeds conversations one at a time behind a durable cursor, MAX_SESSIONS_PER_RUN
    each pass.
  • skill_extractor.py:35 — ExtractedSkill.usage_count exists but is populated
    per extraction, not across sessions.
  • Grep for distill|routine.?min|habit|recurren across the backend finds no
    cross-conversation pass.

Why a separate pass is required

This is not a tuning problem in the existing reviewer. Within one conversation
window a habitual request and a one-off task look identical, and the reviewer is
right to decline writing a skill for a one-off narrative. The signal it needs —
"this same request recurs" — is only visible across sessions. No prompt change to
a single-conversation reviewer can recover it.

Proposed approach

A recurrence miner alongside skill_extractor.py, not inside it:

  • Cluster the opening message of each recent conversation (shingles), after
    redacting URLs, filesystem paths, and long digit runs — those carry no routine
    identity and would both fragment clusters and leak secrets into candidate rows.
  • Count distinct conversations and distinct days per cluster. Days matter: ten
    openers in one afternoon is one task, not a habit.
  • Promote clusters past a threshold into skill candidates through the existing
    SkillProposer.

Recompute each sweep from scratch over the lookback window; do not increment
counters.
Two properties follow directly:

  • re-runs are idempotent — a repeated sweep cannot inflate a count;
  • an abandoned routine decays out of the window on its own, with no expiry job.

An accumulating counter has neither, and a double-counted sweep is invisible.

Blocked — do not build yet

AUTOBOT_SKILL_DISTILLATION_ENABLED defaults to False
(skill_distillation_scheduler.py:44 — "Ships inert. Enable deliberately once the
LLM cost of an hourly pass is accepted"). Building a recurrence miner for a
pipeline nobody has turned on adds maintained-but-dormant surface — the same shape
as #13685, where a whole context stack was built and never ran.

Gate: this issue starts only after distillation is enabled somewhere real and
has produced accepted proposals. Until then the correct state is filed-and-waiting.

Acceptance criteria

  • Distillation is enabled and has produced at least one accepted proposal
    (precondition, not work).
  • Two sweeps over an unchanged window produce identical candidate rows —
    asserted, since an inflating counter is exactly the failure being designed out.
  • A cluster whose conversations all fall on one day does not promote.
  • A routine absent from the lookback window disappears from candidates with no
    expiry job involved.
  • Redaction is asserted on the clustering input: a URL, an absolute path and a
    long digit run must not reach a stored candidate row.

Order

Wave 2 — blocked on the distillation-enabled gate above. No code dependency on
#14027/#14028/#14029.

Found during a comparative audit of an external agent-harness architecture, whose
routine miner documents this exact blind spot in a per-conversation reviewer and
solves it with a recompute-per-sweep cross-session pass.

Activity

  1. mrveiss commented on Aug 11, 2026

    @mrveiss
    OwnerAuthor

    Full audit context: docs/research/agent-harness-guard-and-context-audit.md — landing in PR #14032.

    The doc records what AutoBot already covers (with file:line evidence, so this issue is not re-audited later), where our design is deliberately stronger than the source's, and the do-not-re-file list — alongside all five gaps (#14027, #14028, #14029, #14030, #14031) and their wave order.

  2. added this to the v0.10.0 milestone on Sep 12, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions