Skip to content

feat(skills): AutoSkill A6 — periodic heuristic promotion from ERL to full skills (#4452)#4523

Merged
bug-ops merged 5 commits into
mainfrom
heuristic-promotion-erl
May 28, 2026
Merged

feat(skills): AutoSkill A6 — periodic heuristic promotion from ERL to full skills (#4452)#4523
bug-ops merged 5 commits into
mainfrom
heuristic-promotion-erl

Conversation

@bug-ops
Copy link
Copy Markdown
Owner

@bug-ops bug-ops commented May 28, 2026

Summary

  • Add periodic background job (heuristic_promotion_enabled, default false) that scans skill_heuristics for skills with COUNT >= heuristic_promotion_threshold and calls an LLM to evaluate whether heuristics merit body enrichment or a new standalone skill
  • All promotion results are saved as quarantined drafts — no active-skill writes
  • Idempotency via skill_heuristic_promotions table (BLAKE3 batch hash primary key prevents re-evaluation of unchanged heuristic sets)

Changes

Area What changed
zeph-config Four new [skills.learning] fields with spec-compliant defaults
zeph-db Migrations 093 (SQLite) / 092 (Postgres) for skill_heuristic_promotions
zeph-memory Four DB helpers: count_heuristics_by_skill, load_heuristic_texts_for_promotion, promotion_already_evaluated, record_promotion_evaluation
zeph-skills New promoter.rs: compute_batch_hash (BLAKE3, order-independent), build_promotion_prompt, parse_promotion_response, PROMOTION_SYSTEM_PROMPT
zeph-core heuristic_promotion.rs background task; maybe_start_heuristic_promotion at agent startup; JoinHandle tracked in LearningEngine
binary CLI subcommand zeph skills promote-heuristics [--skill <name>] (dry-run inspection)
config/default.toml A6 block with defaults
CHANGELOG.md [Unreleased] entry

Key Invariants Honoured (spec 061)

  • heuristic_promotion_enabled = false by default — opt-in
  • Promotion task runs in tokio::spawn, failure cannot affect agent loop
  • No writes to active (non-quarantined) skills
  • Only heuristics above erl_min_confidence are eligible
  • Batch hash idempotency: INSERT OR IGNORE on (skill_name, batch_hash) PK
  • Provider resolved via [[llm.providers]] name — no hardcoded model
  • LLM call is bounded by 120s timeout; timeout does NOT record evaluation (retried next cycle)
  • LLM error records none to prevent infinite retries

Test Plan

  • cargo build --workspace --features full — clean (0 errors, 0 warnings)
  • cargo +nightly fmt --check — clean
  • cargo clippy --workspace --features full -- -D warnings — clean
  • RUSTFLAGS="-D warnings" cargo check --workspace --all-targets --features desktop,ide,server,chat,pdf,scheduler --locked — clean
  • cargo nextest run -p zeph-skills -p zeph-config -p zeph-memory -p zeph-core --lib — 3867 passed, 8 skipped
  • Live e2e: seed skill_heuristics with 5+ rows, run agent with heuristic_promotion_enabled=true, verify quarantined draft written and skill_heuristic_promotions row inserted (see playbook)

Closes #4452

@github-actions github-actions Bot added enhancement New feature or request documentation Improvements or additions to documentation skills zeph-skills crate memory zeph-memory crate (SQLite) rust Rust code changes core zeph-core crate config Configuration file changes size/XL Extra large PR (500+ lines) labels May 28, 2026
@bug-ops bug-ops enabled auto-merge (squash) May 28, 2026 15:32
@bug-ops bug-ops force-pushed the heuristic-promotion-erl branch 2 times, most recently from 2fb1d23 to 344c8ff Compare May 28, 2026 15:48
@bug-ops bug-ops disabled auto-merge May 28, 2026 15:50
@bug-ops bug-ops enabled auto-merge (squash) May 28, 2026 15:54
bug-ops added 5 commits May 28, 2026 17:56
… full skills (#4452)

Add a background job that scans skill_heuristics for skills with enough
heuristics (>= heuristic_promotion_threshold) and calls an LLM to decide
whether to promote them as body_enrichment or new_skill quarantined drafts.

Changes:
- crates/zeph-config: four new [skills.learning] fields with defaults
  (heuristic_promotion_enabled=false, heuristic_promotion_provider="",
   heuristic_promotion_threshold=5, heuristic_promotion_interval_hours=24)
- crates/zeph-db: migrations 093 (sqlite) / 092 (postgres) for
  skill_heuristic_promotions table (idempotency guard via batch_hash PK)
- crates/zeph-memory: count_heuristics_by_skill, load_heuristic_texts_for_promotion,
  promotion_already_evaluated, record_promotion_evaluation DB helpers
- crates/zeph-skills: promoter.rs with compute_batch_hash (BLAKE3),
  build_promotion_prompt, parse_promotion_response, PROMOTION_SYSTEM_PROMPT
- crates/zeph-core: heuristic_promotion.rs background task, agent startup
  integration via maybe_start_heuristic_promotion, JoinHandle tracked in
  LearningEngine
- src: CLI subcommand `zeph skills promote-heuristics [--skill <name>]`
  (dry-run; shows qualifying skills and batch hash)
- config/default.toml: A6 config block with defaults
- CHANGELOG.md: [Unreleased] entry

Key invariants honoured: opt-in default, no active-skill writes (always
quarantined), batch-hash idempotency, configurable provider, no per-turn
execution.
…AMD flow, tokio fs

Security: replace hardcoded has_injection_patterns=false with real scan_skill_body()
call in build_generated_skill(); warn (not block) on matches since write is quarantined.

FR-005: for new_skill candidates, run full Add/Merge/Discard decision via
add_merge_discard_decision() before quarantine write. Embeds candidate + existing
skills from output_dir; calls merger::decide() with configured thresholds. Falls back
to Add on embed timeout/error to preserve quarantined draft for human review.

Spec 061 frontmatter: add patch_frontmatter() that injects source=heuristic_promotion
and parent_skill=<originating-skill-name> into LLM-generated SKILL.md before write.
Applied to both body_enrichment and new_skill paths.

Minor: CLI promote-heuristics handler uses tokio::fs::read_to_string instead of
blocking std::fs. Remove unused PromotionResult struct.

Pass merge_threshold, dedup_threshold, and skill_merge_enabled from LearningConfig
through the call stack to write_draft().
…l parsing

Add in-memory SQLite tests for the four new heuristic promotion DB helpers
(count_heuristics_by_skill, load_heuristic_texts_for_promotion,
promotion_already_evaluated, record_promotion_evaluation) and two tests for
parent_skill frontmatter parsing in loader.rs.
…tion draft

Replace Option::None with None in promoter.rs (5 occurrences).
Add explicit patch_version(body, 0) for new_skill path to make version=0
spec compliance visible at the call site.
@bug-ops bug-ops force-pushed the heuristic-promotion-erl branch from 37da0e7 to e4724a1 Compare May 28, 2026 15:56
@bug-ops bug-ops merged commit 46b0283 into main May 28, 2026
32 checks passed
@bug-ops bug-ops deleted the heuristic-promotion-erl branch May 28, 2026 16:03
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

config Configuration file changes core zeph-core crate documentation Improvements or additions to documentation enhancement New feature or request memory zeph-memory crate (SQLite) rust Rust code changes size/XL Extra large PR (500+ lines) skills zeph-skills crate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(skills): periodic heuristic promotion from ERL to full skills (AutoSkill A6)

1 participant