pub(crate) mod install;
pub(crate) struct Skill {
pub(crate) name: &'static str,
pub(crate) content: &'static str,
}
impl Skill {
#[aristo::intent(
"Install paths MUST call resolved_content, never read .content \
directly. The template needs the build-time SDK version; \
writing .content to disk would ship a literal `{{SDK_VERSION}}` \
placeholder to user-installed SKILL.md files. The install \
outcome would look successful but the version pin would be \
garbage — silent staleness on every release.",
verify = "neural",
id = "skill_install_must_use_resolved_content"
)]
pub(crate) fn resolved_content(&self) -> String {
self.content
.replace("{{SDK_VERSION}}", env!("CARGO_PKG_VERSION"))
}
}
#[aristo::intent(
"Skill names in this set are part of the public install surface. \
Renaming or removing one is a breaking change — users on the old \
name have it on disk under that path; agents match by exact name.",
verify = "neural",
id = "bundled_skills_is_stable_set"
)]
pub(crate) fn bundled() -> &'static [Skill] {
BUNDLED
}
const AUTHORING_BODY: &str = concat!(
include_str!("aristo-authoring.md"),
"\n\n---\n\n## Canonical principles (verbatim from PHILOSOPHY.md)\n\n\
The section below is `include_str!`'d at build time from \
`aristo-authoring-philosophy.md` so the bundled skill cannot \
drift from the project's distilled principles. Edit the source \
file, not this section.\n\n",
include_str!("aristo-authoring-philosophy.md"),
);
const AUTHORING: Skill = Skill {
name: "aristo-authoring",
content: AUTHORING_BODY,
};
const VERIFY: Skill = Skill {
name: "aristo-verify",
content: include_str!("aristo-verify.md"),
};
const CRITIQUE: Skill = Skill {
name: "aristo-critique",
content: include_str!("aristo-critique.md"),
};
const INTENT_SUGGESTIONS: Skill = Skill {
name: "aristo-intent-suggestions",
content: include_str!("aristo-intent-suggestions.md"),
};
const AUTHORED_REVIEW: Skill = Skill {
name: "aristo-authored-review",
content: include_str!("aristo-authored-review.md"),
};
const STATUS: Skill = Skill {
name: "aristo-status",
content: include_str!("aristo-status.md"),
};
const HELP: Skill = Skill {
name: "aristo-help",
content: include_str!("aristo-help.md"),
};
const BUNDLED: &[Skill] = &[
AUTHORING,
VERIFY,
CRITIQUE,
INTENT_SUGGESTIONS,
AUTHORED_REVIEW,
STATUS,
HELP,
];
#[cfg(test)]
mod tests {
use super::*;
fn find(name: &str) -> Option<&'static Skill> {
bundled().iter().find(|s| s.name == name)
}
#[test]
fn authoring_skill_is_bundled() {
assert!(find("aristo-authoring").is_some());
}
#[test]
fn future_skill_names_not_yet_bundled() {
assert!(find("aristo-mine-assertions").is_none()); }
#[test]
fn intent_suggestions_skill_is_bundled() {
let s = find("aristo-intent-suggestions")
.expect("aristo-intent-suggestions must be bundled (§17 Slice 4)");
let body = s.resolved_content();
assert!(
body.contains("intent-review"),
"skill body must name the `intent-review` session kind it drives"
);
assert!(
body.contains("aristo canon show"),
"skill body must use `aristo canon show` as the decision card (reuse, not reinvent)"
);
assert!(
body.contains("batch"),
"skill body must teach the batch multi-Q decision pattern"
);
assert!(
body.contains("4 questions per page") || body.contains("4/page"),
"skill body must call out the ≤4-questions-per-page cap (paginate larger sets)"
);
assert!(
body.contains("PHASE 1") && body.contains("PHASE 2") && body.contains("PHASE 3"),
"skill body must teach the two-phase batch (decide → find sites → confirm placements)"
);
assert!(
body.contains("REWRITE") && body.contains("pure"),
"skill body must encode the −/+ rewrite vs pure-+ adopt snippet rule"
);
assert!(
body.contains("backlog"),
"skill body must teach the `backlog` mode (--skip-canon, no new match)"
);
assert!(
body.contains("status"),
"skill body must teach the `status` mode (--counts, no session, no writes)"
);
assert!(
body.contains("Stage A only") && body.contains("Stage B only"),
"skill body must teach the matches-only / suggestions-only stage selectors"
);
assert!(
body.contains("--filter parent=") && body.contains("--filter file="),
"skill body must teach the `cluster <objective>` / `for <file>` scope filters"
);
assert!(
body.contains("zero-argument slash command"),
"skill body must declare it is a zero-arg slash command (NL/menu routing, no flags)"
);
assert!(
body.contains("aristo stamp") && body.contains("aristo canon accept"),
"skill body must funnel adoption through stamp → canon accept (no new source mutator)"
);
assert!(
body.contains("parent-first") || body.contains("once per cluster"),
"skill body must encode the parent-first, once-per-cluster decision (D6)"
);
assert!(
body.contains("DRAGGED-IN") && body.contains("KEEP"),
"skill body must encode the D6 discard-dragged-in-only / keep-independent rule"
);
assert!(
body.contains("aristo session active"),
"skill body's step 0 must check for an active review session (Layer 3 enforcement)"
);
assert!(
body.contains("aristo session start intent-review"),
"skill body must open an intent-review session before interactive review"
);
assert!(
body.contains("aristo session decide --item"),
"skill body must record per-item decisions via the substrate"
);
assert!(
body.contains("aristo session exit --defer-undecided"),
"skill body must offer defer-undecided so open items go to backlog, not silently dropped"
);
assert!(
body.starts_with("---"),
"skill must start with YAML frontmatter"
);
assert!(
body.contains("name: aristo-intent-suggestions"),
"frontmatter must include the skill name"
);
}
#[test]
fn authored_review_skill_is_bundled() {
let s = find("aristo-authored-review")
.expect("aristo-authored-review must be bundled (Phase 18 #7)");
let body = s.resolved_content();
assert!(
body.starts_with("---"),
"skill must start with YAML frontmatter"
);
assert!(
body.contains("name: aristo-authored-review"),
"frontmatter must include the skill name"
);
assert!(
body.contains("aristo review --json"),
"skill body must teach listing the backlog via `aristo review --json`"
);
assert!(
body.contains("aristo review --mark"),
"skill body must teach marking reviewed via `aristo review --mark`"
);
assert!(
body.contains("Critique-first") && body.contains("Review now"),
"skill body must offer both the Critique-first and Review-now paths (D9)"
);
assert!(
body.contains("aristo critique --filter id="),
"Critique-first must kick `aristo critique --filter id=` over the new intents"
);
assert!(
body.contains("BEFORE") && body.contains("Guard ordering"),
"skill body must encode the D11 guard ordering (kick critique before a session)"
);
assert!(
body.contains("aristo session active"),
"skill body's step 0 must check for an active review session (Layer 3)"
);
assert!(
body.contains("AskUserQuestion"),
"skill body must drive the review via AskUserQuestion menus"
);
assert!(
body.contains("4 questions per page") || body.contains("4/page"),
"skill body must call out the ≤4-questions-per-page cap (paginate larger sets)"
);
assert!(
body.contains("Subject-only") || body.contains("subject-only"),
"skill body must encode the subject-only rule"
);
assert!(
body.contains("aristo stamp"),
"skill body must funnel source edits through `aristo stamp`"
);
}
#[test]
fn critique_skill_is_bundled() {
let s = find("aristo-critique").expect("aristo-critique must be bundled");
let body = s.resolved_content();
assert!(
body.contains("aristo critique --submit-findings"),
"skill body must teach the SDK CLI as the single write path"
);
assert!(
body.contains("aristo critique --pop-next"),
"skill body must teach the worker-loop pop pattern"
);
assert!(
body.contains("model=\"sonnet\""),
"skill body must specify the Sonnet model for critique workers \
(Opus is overkill for shallow prose work)"
);
assert!(
body.contains("Bash tools only"),
"skill body must restrict critique workers to Bash only \
(task body is self-contained; no Read needed)"
);
assert!(
body.contains("rephrasing")
&& body.contains("parent-shape")
&& body.contains("vocabulary")
&& body.contains("scope")
&& body.contains("clarity"),
"skill body must enumerate all five v0 finding categories"
);
assert!(
body.contains("strong-suggest") && body.contains("severity"),
"skill body must enumerate the severity scale"
);
assert!(
body.contains("self-contained"),
"skill body must teach that the task body is self-contained \
(no source reads needed)"
);
assert!(
body.contains("aristo session active"),
"skill body's step 0 must check for an active review session \
(slice 27.5 Layer 3 enforcement)"
);
assert!(
body.contains("aristo session start critique-review"),
"skill body's step 5 must open a session before interactive review"
);
assert!(
body.contains("aristo session decide --item"),
"skill body's step 5 must record per-finding decisions via the substrate"
);
assert!(
body.contains("aristo session exit --defer-undecided"),
"skill body must offer defer-undecided as the early-stop path \
so open items go to backlog, not silently dropped"
);
}
#[test]
fn verify_skill_is_bundled() {
let s = find("aristo-verify").expect("aristo-verify must be bundled (Phase 18 #1/#2)");
let body = s.resolved_content();
assert!(
body.contains("name: aristo-verify"),
"frontmatter must carry the renamed skill name"
);
assert!(
!body.contains("name: aristo-neural-verify"),
"the skill must be RENAMED, not aliased — no stale neural-verify name in frontmatter"
);
assert!(
body.contains("Changed · neural"),
"skill must open with the scope×mode menu (Changed·neural is the default)"
);
assert!(
body.contains("aristo verify --rerun"),
"skill must map 'Everything' to the existing --rerun flag"
);
assert!(
body.contains("aristo verify --view"),
"skill must teach re-attaching to the background full (server) session via --view"
);
assert!(
body.contains("aristo auth login"),
"skill must gate full/both on sign-in and offer `aristo auth login`"
);
assert!(
body.contains("SUBJECT-ONLY"),
"cards must be SUBJECT-ONLY (never reference the internal verification model)"
);
assert!(
body.contains("render the card first"),
"skill must encode RENDER-THEN-MENU (the user sees the card before the action menu)"
);
assert!(
body.contains("✗ Refuted") && body.contains("✓ Verified"),
"skill must define both the failure (DifferentialReport) and success cards"
);
assert!(
body.contains("Successes only"),
"results-review opening must include the Successes-only lane"
);
assert!(
body.contains("aristo verify --accept") && body.contains("--because"),
"the failure card's Waive action must use `aristo verify --accept … --because`"
);
assert!(
body.contains("`.aristo/pending-neural.toml`"),
"skill body must reference the pending-request file the SDK writes"
);
assert!(
body.contains("aristo verify --apply-verdicts"),
"skill body must teach the agent to call the SDK validator after producing proofs"
);
assert!(
body.contains("aristo verify --submit-verdict"),
"skill body must teach the subagent to submit verdicts via the SDK CLI \
(single write path; no direct file writes from agents)"
);
assert!(
body.contains("`Bash` and `Read` tools only"),
"skill body must restrict subagent tools to Bash + Read (no Write)"
);
assert!(
body.contains("ONE-SHOT") || body.contains("one-shot"),
"skill body must teach the one-shot-per-worker pattern \
(context pollution risk if workers loop)"
);
assert!(
body.contains("aristo verify --queue-status"),
"skill body must teach the orchestrator to use --queue-status \
as the non-destructive peek between worker dispatches"
);
assert!(
body.contains("run_in_background"),
"skill body must teach continuous dispatch via Agent(run_in_background=true) \
— waves of N workers waste time waiting on the slowest in each batch"
);
assert!(
body.contains("accepted: sha256:"),
"skill body must teach the subagent how to parse the SDK's accept stdout"
);
assert!(
body.contains("compare with the reported hash"),
"skill body must teach the orchestrator's hash-comparison integrity check"
);
assert!(
body.contains("do NOT inline it as a discovered ground"),
"skill body must encode the strict-on-discovered-assumptions rule"
);
assert!(
body.contains("DO NOT write hash fields"),
"skill body must instruct the agent to omit hash fields (SDK stamps them)"
);
assert!(
body.contains("Cited id discipline"),
"skill body must encode the cited-id-discipline rule (open #2 fix)"
);
assert!(
body.contains("`supports` is NOT a valid variant"),
"skill body must explicitly disallow the `supports` enum variant \
(broke 2/4 of the first submit-flow dogfood run)"
);
assert!(
body.contains("CANNOT cite"),
"skill body must explicitly disallow child-as-prior-step \
(broke verify_bool_true on first dogfood run)"
);
assert!(
body.contains("\"1-200\""),
"skill body must call out the over-broad line-range failure mode \
(broke pending_neural on first dogfood run)"
);
assert!(
body.contains("prior_attempts + 1") || body.contains("prior_attempts}} + 1"),
"skill body must teach the agent to compute attempts as prior_attempts + 1 \
(GAP-9: repair budget accumulates across re-spawns)"
);
assert!(
body.contains("AskUserQuestion"),
"skill body must drive the review via AskUserQuestion menus"
);
assert!(
body.contains("Every suggested annotation gets surfaced as an actionable question"),
"skill body must encode the GSD-style interactive-suggestions rule"
);
assert!(
body.contains("second `AskUserQuestion`"),
"skill body must require two-step confirmation before any source edit \
(no silent source mutation from skill orchestration)"
);
assert!(
body.contains("aristo session active"),
"skill body's step 0 must check for an active review session \
(slice 27.5 Layer 3 enforcement)"
);
assert!(
body.contains("aristo session start proof-review"),
"skill body's step 7 must open a session before interactive review"
);
assert!(
body.contains("aristo session decide --item"),
"skill body's step 7 must record per-proof decisions via the substrate"
);
assert!(
body.contains("aristo session exit --defer-undecided"),
"skill body must offer defer-undecided as the early-stop path \
so un-decided proofs go to backlog, not silently dropped"
);
}
#[test]
fn help_skill_is_bundled_and_covers_the_whole_suite() {
let s = find("aristo-help").expect("aristo-help must be bundled (Phase 18 #11)");
let body = s.resolved_content();
assert!(body.starts_with("---"), "skill must start with frontmatter");
assert!(
body.contains("name: aristo-help"),
"frontmatter must carry the skill name"
);
for skill in bundled() {
assert!(
body.contains(skill.name),
"aristo-help must mention `{}` — a bundled skill is undocumented in the suite overview",
skill.name
);
}
assert!(
body.contains("scenario flows") || body.contains("Example scenario"),
"aristo-help must give example end-to-end scenario flows"
);
assert!(
body.contains("fix-or-waive") || body.contains("fix or waive"),
"aristo-help must cover the failure → fix-or-waive flow"
);
}
#[test]
fn status_skill_is_bundled() {
let s = find("aristo-status").expect("aristo-status must be bundled (Phase 18 #12)");
let body = s.resolved_content();
assert!(body.starts_with("---"), "skill must start with frontmatter");
assert!(
body.contains("name: aristo-status"),
"frontmatter must carry the skill name"
);
assert!(
body.contains("aristo metrics --json"),
"skill must read metrics via `aristo metrics --json`"
);
assert!(
body.contains("aristo status"),
"skill must read the full breakdown via `aristo status`"
);
assert!(
body.contains("aristo review --json"),
"skill must surface the review backlog via `aristo review --json`"
);
assert!(
body.contains("aristo nudge"),
"skill must take its recommended next action from `aristo nudge` (the #9 engine)"
);
assert!(
body.contains("read-only") || body.contains("Read-only"),
"skill must declare it is read-only (no mutation)"
);
assert!(
body.contains("Subject-only") || body.contains("subject-only"),
"skill must encode the subject-only rule"
);
}
#[test]
fn bundled_skill_names_are_unique() {
let mut names: Vec<_> = bundled().iter().map(|s| s.name).collect();
names.sort_unstable();
let len_before = names.len();
names.dedup();
assert_eq!(
names.len(),
len_before,
"duplicate skill name in BUNDLED — would clobber on install"
);
}
#[test]
fn authoring_skill_references_intent_stmt_not_intent_bang() {
let s = find("aristo-authoring").unwrap();
assert!(
s.content.contains("intent_stmt!"),
"authoring skill must teach intent_stmt! (the actual macro name)"
);
assert!(
!s.content.contains("aristo::intent!("),
"authoring skill must NOT teach intent!() — that name doesn't exist (E0428)"
);
}
#[test]
fn authoring_skill_teaches_diff_mode() {
let s = find("aristo-authoring").unwrap();
let body = s.content;
assert!(
body.contains("Diff mode"),
"authoring skill must teach the diff (backfill) mode (#5)"
);
assert!(
body.contains("uncommitted"),
"diff mode must scope to uncommitted changes"
);
assert!(
body.contains("git diff"),
"diff mode must use git diff as the change source"
);
assert!(
body.contains("IN-CONTEXT") || body.contains("in-context"),
"diff mode must require running in-context (rationale still recoverable)"
);
assert!(
body.contains("AskUserQuestion"),
"diff mode must propose + confirm (no silent backfill edits)"
);
assert!(
body.contains("body-drift") || body.contains("body-drifted"),
"diff mode must reconcile body-drifted existing intents (stamp --check)"
);
}
#[test]
fn authoring_skill_references_aristos_namespace_warning() {
let s = find("aristo-authoring").unwrap();
assert!(
s.content.contains("aristos:"),
"skill must warn agents not to write the aristos: prefix"
);
assert!(
s.content.contains("aret_"),
"skill must warn agents not to write the aret_ prefix"
);
}
#[test]
fn authoring_skill_embeds_philosophy_principles_verbatim() {
let s = find("aristo-authoring").unwrap();
for principle in [
"P-SPEC-STYLE",
"P-CHECK-TYPE-SYSTEM-FIRST",
"P-NO-DOUBLE-INTENT",
"P-INVARIANT-AT-LOAD-BEARING-SITE",
"P-INVARIANT-NOT-IMPL",
"P-WHY-AS-INVARIANT",
"P-NAME-THE-REFACTOR-TRAP",
"P-AGENT-PROOFING",
"P-VERIFY-MATCHES-SHAPE",
] {
assert!(
s.content.contains(principle),
"bundled skill is missing `{principle}` — did the \
PHILOSOPHY.md include path break?"
);
}
assert!(
s.content.contains("`include_str!`'d at build time from"),
"missing include-marker phrase — skill may not be \
auto-wiring from PHILOSOPHY.md"
);
}
#[test]
fn authoring_skill_has_yaml_frontmatter() {
let s = find("aristo-authoring").unwrap();
let mut lines = s.content.lines();
assert_eq!(
lines.next(),
Some("---"),
"skill must start with frontmatter"
);
assert!(
s.content.contains("name: aristo-authoring"),
"frontmatter must include the skill name"
);
}
}