DI

Didactic Substeps Development

Documents how to add step-by-step intermediate expressions to ExpliCAS engine rules.

Install

mkdir -p .claude/skills/didactic-substeps-development && curl -L -o skill.zip "https://agentskills.codes/api/skills/download/13871" && unzip -o skill.zip -d .claude/skills/didactic-substeps-development && rm skill.zip

Installs to .claude/skills/didactic-substeps-development

Activation

This is the description your AI agent reads to decide when to run this skill — the better it matches your request, the more reliably it fires.

How to develop step-by-step didactic substeps for ExpliCAS engine rules, showing concrete expressions with minimal performance impact
133 charsno explicit “when” trigger
Advanced

Key capabilities

  • Add pedagogical substeps to ExpliCAS engine rules
  • Attach lightweight metadata to Rewrites
  • Compute rich post-processing substeps
  • Gate expensive didactic computations behind proof data
  • Use `new_compact` for non-display steps
  • Register new substep generation functions

How it works

The skill details how to add two types of substeps: engine-level (lightweight metadata) and didactic-layer (rich post-processing with expressions), ensuring expensive computations only run when step-by-step display is active.

Inputs & outputs

You give it
ExpliCAS engine rule and intermediate data
You get back
JSON output with concrete intermediate expressions in step-by-step view

When to use Didactic Substeps Development

  • Add pedagogy to engine rules
  • Visualize intermediate expressions
  • Enrich math step-by-step output

About this skill

Didactic Substeps Development

This skill documents how to add pedagogical substeps to ExpliCAS engine rules so users can see concrete intermediate expressions in the step-by-step view.

Architecture Overview

There are two independent SubStep systems that both feed into the JSON output:

graph TD
  Rule["Rule (apply)"] --> Rewrite["Rewrite"]
  Rewrite -->|"poly_proof / substeps"| Step["Step (step.rs)"]
  Step -->|"Post-processing"| DidacticEnrich["didactic::enrich_steps()"]
  DidacticEnrich --> EnrichedStep["EnrichedStep { base_step, sub_steps }"]
  EnrichedStep -->|"Serialization"| JSON["eval_json.rs → SubStepJson"]

1. Engine-Level SubSteps (step.rs::SubStep)

Lightweight metadata attached to a Rewrite during rule application via Rewrite::substep(). These are title + bullet lines format (no LaTeX). They are always computed but are cheap.

// In a rule's apply() method:
Rewrite::new(before, after, "Rule description")
    .substep(step::SubStep::new(
        "Pattern Recognition",
        vec!["Detected a² - b²".into(), "a = x, b = 3".into()],
    ))

2. Didactic-Layer SubSteps (didactic::SubStep)

Rich post-processing substeps computed by didactic::enrich_steps(). These have before/after expressions with optional LaTeX. They are the ones shown in the web REPL with MathJax rendering.

// In a generate_*_substeps() function:
SubStep {
    description: "Expand products".to_string(),
    before_expr: "(t+1)(t+2)".to_string(),
    after_expr: "t² + 3t + 2".to_string(),
    before_latex: Some("(t+1)(t+2)".to_string()),
    after_latex: Some("{t}^{2} + 3t + 2".to_string()),
}

Performance Contract

[!IMPORTANT] Didactic computations MUST NOT run during normal simplification. They should only execute when step-by-step display is active.

Pattern: Gate Expensive Computation Behind Proof Data

Expensive didactic work (e.g., computing expanded polynomial forms) should be done in the rule itself only when proof data is already being computed. The rule already produces proof data as part of its correctness verification, so the marginal cost is low.

// In a rule like try_opaque_zero():
// The polynomial expansion is already computed for verification.
// We just need to also store it for display:
let expanded_form_expr =
    crate::multipoly_display::expand_additive_terms(ctx, display_expr, &display_vars);

let proof = PolynomialProofData {
    // ... standard fields ...
    expanded_form_expr,  // ← Piggyback on existing computation
};

Pattern: Post-Processing Only When Steps Are Requested

The enrich_steps() function is only called by the JSON/CLI presentation layer when steps_mode == "on". The StepMeta field in Step is Option<Box<StepMeta>> — when steps are not needed, it stays None and no allocation occurs.

Pattern: Use new_compact for Non-Display Steps

When step recording is in compact mode, use Step::new_compact() which skips the format!() call and sets meta: None.

How to Add Substeps for a New Rule

Step 1: Identify the Proof/Metadata Data

Determine what intermediate data the rule computes that would be pedagogically valuable. Common examples:

Rule TypeProof DataDisplay Value
Polynomial identityPolynomialProofDataExpanded terms before cancellation
Fraction sumFractionSumInfoLCD, numerator construction
RationalizationConjugate pairBefore/after multiplication
Nested fractionMultiply-through structureStep-by-step clearing

Step 2: Attach Proof Data to the Rewrite

Store the intermediate data in the Rewrite so it flows to the Step:

// Option A: Use existing poly_proof mechanism
let rewrite = Rewrite::new(before, after, "description")
    .poly_proof(proof_data);

// Option B: Use engine-level substeps (for simple title+lines)
let rewrite = Rewrite::new(before, after, "description")
    .substep(step::SubStep::new("Title", vec!["line1".into()]));

Step 3: Create a generate_*_substeps() Function

Add a new function in either didactic/nested_fractions.rs or didactic/fraction_steps.rs:

pub fn generate_my_rule_substeps(ctx: &Context, step: &Step) -> Vec<SubStep> {
    let mut sub_steps = Vec::new();
    
    // Extract proof data from the step
    let proof = match step.poly_proof() {
        Some(p) => p,
        None => return sub_steps,
    };
    
    // Build substeps with CONCRETE expressions
    if let Some(expanded_id) = proof.expanded_form_expr {
        let expanded_latex = cas_ast::LaTeXExpr {
            context: ctx,
            id: expanded_id,
        }.to_latex();
        
        sub_steps.push(SubStep {
            description: "Expand products".to_string(),
            before_expr: format!("{}", DisplayExpr { context: ctx, id: original_id }),
            after_expr: format!("{}", DisplayExpr { context: ctx, id: expanded_id }),
            before_latex: Some(original_latex),
            after_latex: Some(expanded_latex),
        });
    }
    
    sub_steps
}

Step 4: Register in the Dispatcher

In didactic/mod.rs::enrich_steps(), add a condition to call your new function:

// In the step loop inside enrich_steps():
if step.rule_name.contains("MyNewRule") {
    sub_steps.extend(generate_my_rule_substeps(ctx, step));
}

Step 5: Gate by is_chained()

If the rule uses ChainedRewrite (multi-step decomposition already visible as separate engine Steps), skip substep generation to avoid duplication:

if step.rule_name.contains("GCD") && !step.is_chained() {
    sub_steps.extend(generate_gcd_substeps(ctx, step));
}

LaTeX Rendering Contract

[!WARNING] Plain text in substeps is automatically wrapped in \text{} by the JSON layer if before_latex/after_latex are None. If you want MathJax to render math formulas, you MUST set the LaTeX fields explicitly.

Converting ExprId to LaTeX

let latex = cas_ast::LaTeXExpr {
    context: ctx,
    id: expr_id,
}.to_latex();

Converting ExprId to Plain Text

let plain = format!("{}", cas_ast::DisplayExpr {
    context: ctx,
    id: expr_id,
});

Reference: Existing Substep Generators

GeneratorFileTriggered By
generate_fraction_sum_substepsfraction_steps.rsFraction sums in original expr
generate_gcd_factorization_substepsfraction_steps.rs"Simplified fraction by GCD"
generate_nested_fraction_substepsnested_fractions.rs"complex/nested fraction" rule names
generate_rationalization_substepsnested_fractions.rs"Rationalize" in description/rule
generate_polynomial_identity_substepsnested_fractions.rsstep.poly_proof().is_some()
generate_sum_three_cubes_substepsnested_fractions.rs"Sum of Three Cubes" rule name
generate_root_denesting_substepsnested_fractions.rs"Root Denesting" rule name

Concrete Example: Opaque Polynomial Identity

For (sin(u)+1)·(sin(u)+2) - sin(u)² - 3·sin(u) - 2:

Step 1 (in rule try_opaque_zero): Compute expanded_form_expr using expand_additive_terms() which splits into signed terms, expands each through multipoly independently, and recombines — gives t² + 3t + 2 - t² - 3t - 2.

Step 2 (in generate_polynomial_identity_substeps): Build three substeps:

  1. "Sustitución para simplificar": Show original → Sea t = sin(u)
  2. "Expresión sustituida": Show (t+1)(t+2) - t² - 3t - 2t² + 3t + 2 - t² - 3t - 2
  3. "Todos los términos se cancelan": Show "Expandir y agrupar" → = 0

Each substep has both before_latex and after_latex so MathJax renders the math correctly.

Key Files

  • cas_engine/src/didactic/mod.rs — Entry point, dispatcher, EnrichedStep/SubStep types
  • cas_engine/src/didactic/nested_fractions.rs — Polynomial identity, rationalization, nested fraction generators
  • cas_engine/src/didactic/fraction_steps.rs — Fraction sum, GCD factorization generators
  • cas_engine/src/step.rsStep, StepMeta, engine-level SubStep
  • cas_engine/src/rule.rsRewrite::poly_proof(), Rewrite::substep()
  • cas_engine/src/multipoly_display.rsPolynomialProofData, expand_additive_terms()
  • cas_cli/src/commands/eval_json.rs — JSON serialization, \text{} wrapping

When not to use it

  • When didactic computations should run during normal simplification

Limitations

  • Didactic computations MUST NOT run during normal simplification.
  • The `enrich_steps()` function is only called by the JSON/CLI presentation layer when `steps_mode == "on"`.
  • When step recording is in compact mode, use `Step::new_compact()` which skips the `format!()` call and sets `meta: None`.

How it compares

This skill provides a structured approach to adding pedagogical substeps with concrete expressions, ensuring performance by gating computations, unlike an unstructured method that might impact simplification speed.

Compared to similar skills

Didactic Substeps Development side by side with the closest alternatives in the catalog.

SkillInstallsUpdatedSafetyDifficulty
Didactic Substeps Development (this skill)06moNo flagsAdvanced
streamlit869moNo flagsIntermediate
manim296moReviewIntermediate
d3-visualization74moNo flagsAdvanced

Try saying

Example prompts that trigger this skill in your AI assistant.

You might also like

streamlit

sverzijl

When working with Streamlit web apps, data dashboards, ML/AI app UIs, interactive Python visualizations, or building data science applications with Python

86239

manim

davila7

Comprehensive guide for Manim Community - Python framework for creating mathematical animations and educational videos with programmatic control

29113

d3-visualization

lyndonkl

Use when creating custom, interactive data visualizations with D3.js—building bar/line/scatter charts from scratch, creating network diagrams or geographic maps, binding changing data to visual elements, adding zoom/pan/brush interactions, animating chart transitions, or when chart libraries (Highcharts, Chart.js) don't support your specific visualization design and you need low-level control over data-driven DOM manipulation, scales, shapes, and layouts.

7107

threejs

mrgoonie

Build 3D web apps with Three.js (WebGL/WebGPU). Use for 3D scenes, animations, custom shaders, PBR materials, VR/XR experiences, games, data visualizations, product configurators.

3554

lecture-transcript-slide-matcher

az9713

Combines YouTube lecture transcripts with PDF slides to create an interactive HTML page. Matches each slide to corresponding transcript segments, organized by key concepts. Use when users want to create synchronized lecture notes from transcript text files and slide PDFs.

669

playground

anthropics

Creates interactive HTML playgrounds — self-contained single-file explorers that let users configure something visually through controls, see a live preview, and copy out a prompt. Use when the user asks to make a playground, explorer, or interactive tool for a topic.

1252

Search skills

Search the agent skills registry