//! Prompt templates and length-window constants for the workspace
//! summariser.
//!
//! The summariser runs two sequential inference passes:
//!
//! 1. **Short pass** — compress every enabled skill's description into a
//!    single comma-separated topic list (≤ 800 chars). The output feeds
//!    the MCP server's tool description (FR-425).
//! 2. **Long pass** — using the short output as input, write a 4–6
//!    sentence rules section for the harness's `RULES.md` file
//!    (≤ 2500 chars). The output feeds the workspace's composed
//!    `RULES.md`.
//!
//! Both prompts are pinned `&'static str` constants — research §R-15
//! treats prompt wording as code, not configuration, so a wording change
//! ships in a Tome release rather than as a runtime tweakable.
//!
//! The length windows below are advisory: outputs at or below `MAX`
//! pass; outputs strictly above `MAX` emit a `tracing::warn!` and
//! continue (per `contracts/summariser.md` §"Length windows" — a
//! too-long short summary that's been embedded into the MCP tool
//! description is a warning, not a hard error). Empty outputs are a
//! hard failure (`SummariserFailureKind::OutputEmpty`, exit 24).
//!
//! Rust 1.93's stable `const` evaluator does not support construction of
//! `RangeInclusive<usize>` in a `const` initialiser, so the target
//! range is exposed as two `MIN` / `MAX` constants and the call site
//! composes the range as needed (e.g. `SHORT_TARGET_MIN..=SHORT_TARGET_MAX`).
//! Contract: `contracts/summariser.md` §"Prompts" + §"Length windows".

/// First-pass prompt. `{descriptions}` is substituted via `String::replace`
/// with one line per skill of the form `<plugin>: <skill-name> — <skill-description>`.
pub const SHORT_PROMPT: &str =
    "You are summarising a developer's skill library. Given the descriptions below,
produce a single comma-separated phrase listing the topics these skills cover.
No prose, no lead-in, no bullet points. Maximum 700 characters.

Skill descriptions:
{descriptions}";

/// Second-pass prompt. `{topics}` is substituted with the output of the
/// short pass (cascading from short to long; the long prompt benefits
/// from the short summary's already-compressed topic list).
pub const LONG_PROMPT: &str =
    "You are writing a short rules section for an AI coding agent. The agent has access
to a search tool that retrieves skills relevant to a task. Below are the topics the
user's skill library covers. Write a 4–6 sentence rules section that
(1) tells the agent which topics the skill library covers,
(2) instructs the agent to call the search_skills tool when working on tasks
   involving those topics,
(3) is written for the agent to read at session start.
Plain prose, no headings, no bullet points. Maximum 2500 characters.

Topics:
{topics}";

// The hard upper bounds [`SHORT_MAX_CHARS`] and [`LONG_MAX_CHARS`] live
// in [`crate::summarise`] as the single source of truth across the
// inference loop (`llama.rs`) and the regen-summary command
// (`workspace::regen_summary`). US4.d-1 consolidation. Re-exported here
// for backwards-compatibility with the existing `prompts::*` import
// paths.
pub use super::{LONG_MAX_CHARS, SHORT_MAX_CHARS};

/// Target lower bound for the short summary (advisory, no warning emitted).
pub const SHORT_TARGET_MIN: usize = 400;

/// Target upper bound for the short summary (advisory, no warning emitted).
pub const SHORT_TARGET_MAX: usize = 800;

/// Target lower bound for the long summary (advisory, no warning emitted).
pub const LONG_TARGET_MIN: usize = 1500;

/// Target upper bound for the long summary (advisory, no warning emitted).
pub const LONG_TARGET_MAX: usize = 2500;

// Compile-time guards on the length-window constants. A future tweak
// that flips the inequality wakes the build, not the test suite.
const _: () = {
    assert!(SHORT_TARGET_MIN < SHORT_TARGET_MAX);
    assert!(SHORT_TARGET_MAX <= SHORT_MAX_CHARS);
    assert!(LONG_TARGET_MIN < LONG_TARGET_MAX);
    // After US4.d-1's consolidation `LONG_MAX_CHARS == LONG_TARGET_MAX`
    // — the long target band aligns with the hard cap rather than
    // extending past it. The assert below stays valid (strict <).
    assert!(LONG_TARGET_MIN < LONG_MAX_CHARS);
};

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn short_prompt_includes_substitution_marker() {
        assert!(SHORT_PROMPT.contains("{descriptions}"));
    }

    #[test]
    fn long_prompt_includes_substitution_marker() {
        assert!(LONG_PROMPT.contains("{topics}"));
    }
}
