wip(microsteps): B3.3 + B3.10 part 1 — prompts + microstep_patterns schema

Storage-side scaffolding for B3.3 (granularity prompt variants) and
B3.10 (mastery fade). The user-facing wiring (decompose_and_store
granularity threading, complete_subtask_cmd count bump, MicroSteps
inline prompt + skip-check, Settings UI) is part 2 of this batch.

B3.3 (prompts.rs): three new system-prompt constants
DECOMPOSE_LIGHT_SYSTEM (3 atomic verb-first steps),
DECOMPOSE_DEFAULT_SYSTEM (4-5 balanced steps), DECOMPOSE_DETAILED_SYSTEM
(6-7 with brief context) all preserve the cue-anchored "When [cue],
[action]" framing from PR 1.1. DECOMPOSE_TASK_SYSTEM remains as an
alias of Default for back-compat with existing callers and tests.
4 snapshot tests assert the framing survives across variants and the
alias matches.

B3.10 (storage): migration v18 adds microstep_patterns
(normalized_title PK, sample_title, completed_count, skip_breakdown,
prompted_at) plus an index on completed_count. New storage functions:
normalize_microstep_title (lowercase + whitespace-collapse + trim),
get_microstep_pattern, upsert_microstep_pattern_increment,
set_microstep_pattern_decision. 5 storage tests cover normalisation,
upsert/bump semantics, decision persistence, and the threshold query.

74 storage tests pass (was 69), 9 prompts tests pass (was 5).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-26 19:58:02 +01:00
parent 423c0caca4
commit fcca6509cc
3 changed files with 211 additions and 10 deletions

View File

@@ -1,13 +1,40 @@
pub const DECOMPOSE_TASK_SYSTEM: &str = "\
pub const DECOMPOSE_LIGHT_SYSTEM: &str = "\
You are a task-decomposition assistant. Given a task description, produce \
between 3 and 7 concrete, physical micro-steps. Each step must be a short \
exactly 3 concrete, physical micro-steps. Each step must be a short, \
verb-first imperative sentence — atomic enough to do without thinking. \
No commentary. Where the task description contains a natural cue (a \
place, a time, a preceding action, an object the user will already be \
holding), phrase that step as \"When [cue], [action]\" so the cue \
triggers the action. Use this framing only where the cue is genuinely \
present in the input — do not invent cues. Output ONLY a JSON array of \
strings.";
pub const DECOMPOSE_DEFAULT_SYSTEM: &str = "\
You are a task-decomposition assistant. Given a task description, produce \
between 4 and 5 concrete, physical micro-steps. Each step must be a short \
imperative sentence, actionable today, with no commentary. Where the task \
description contains a natural cue (a place, a time, a preceding action, an \
object the user will already be holding), phrase that step as \
\"When [cue], [action]\" so the cue triggers the action. Use this framing \
only where the cue is genuinely present in the input — do not invent cues. \
Steps without a natural cue stay as plain imperatives. Output ONLY a \
JSON array of strings.";
description contains a natural cue (a place, a time, a preceding action, \
an object the user will already be holding), phrase that step as \
\"When [cue], [action]\" so the cue triggers the action. Use this \
framing only where the cue is genuinely present in the input — do not \
invent cues. Steps without a natural cue stay as plain imperatives. \
Output ONLY a JSON array of strings.";
pub const DECOMPOSE_DETAILED_SYSTEM: &str = "\
You are a task-decomposition assistant. Given a task description, produce \
between 6 and 7 concrete, physical micro-steps. Each step must be a short \
imperative sentence, actionable today. Brief context (one short clause) \
is allowed where it makes the next move obvious; otherwise no commentary. \
Where the task description contains a natural cue (a place, a time, a \
preceding action, an object the user will already be holding), phrase \
that step as \"When [cue], [action]\" so the cue triggers the action. \
Use this framing only where the cue is genuinely present in the input — \
do not invent cues. Steps without a natural cue stay as plain imperatives. \
Output ONLY a JSON array of strings.";
/// Back-compat alias — existing callers and tests that reference
/// `DECOMPOSE_TASK_SYSTEM` continue to compile unchanged.
pub const DECOMPOSE_TASK_SYSTEM: &str = DECOMPOSE_DEFAULT_SYSTEM;
// Phase 9 content-tag extraction. The model emits a {topic, intent}
// JSON pair under a strict GBNF (see grammars::CONTENT_TAGS_GRAMMAR).
@@ -139,6 +166,42 @@ pub fn build_conditioned_system_prompt(base: &str, examples: &[FeedbackExample])
mod tests {
use super::*;
// --- B3.3 snapshot tests ---
#[test]
fn light_prompt_contains_cue_anchored_framing() {
assert!(
DECOMPOSE_LIGHT_SYSTEM.contains("When [cue], [action]"),
"DECOMPOSE_LIGHT_SYSTEM must contain the cue-anchored framing"
);
}
#[test]
fn default_prompt_contains_cue_anchored_framing() {
assert!(
DECOMPOSE_DEFAULT_SYSTEM.contains("When [cue], [action]"),
"DECOMPOSE_DEFAULT_SYSTEM must contain the cue-anchored framing"
);
}
#[test]
fn detailed_prompt_contains_cue_anchored_framing() {
assert!(
DECOMPOSE_DETAILED_SYSTEM.contains("When [cue], [action]"),
"DECOMPOSE_DETAILED_SYSTEM must contain the cue-anchored framing"
);
}
#[test]
fn default_alias_matches_default_const() {
assert_eq!(
DECOMPOSE_TASK_SYSTEM, DECOMPOSE_DEFAULT_SYSTEM,
"DECOMPOSE_TASK_SYSTEM must be the same value as DECOMPOSE_DEFAULT_SYSTEM"
);
}
// --- existing conditioned-prompt tests ---
#[test]
fn builds_plain_prompt_when_no_examples() {
let out = build_conditioned_system_prompt(DECOMPOSE_TASK_SYSTEM, &[]);