Day 36 — Stage the Workflow
The original script already contains an important design insight: do not ask for research, strategy, an outline, a full article, HTML, metadata, and JSON-LD in one prompt. Rankforge turns that insight into an executable workflow contract.
Make order a type
Section titled “Make order a type”#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]pub enum Stage { Research, Angle, Outline, Article, SeoPackage,}
impl Stage { pub const REQUIRED: [Self; 5] = [ Self::Research, Self::Angle, Self::Outline, Self::Article, Self::SeoPackage, ];}Before Rankforge creates the output directory, it checks that exactly five responses exist in this order. A run that skips the angle gate is invalid even if the remaining prose looks good.
Keep the model behind one narrow boundary
Section titled “Keep the model behind one narrow boundary”An optional live implementation can expose the capability the workflow needs:
pub trait StageModel { fn run(&self, request: StageRequest<'_>) -> Result<StageResponse, ModelError>;}
pub struct StageRequest<'a> { pub stage: Stage, pub source: &'a str, pub accepted_inputs: &'a [StageArtifact],}The request passes only accepted earlier artifacts. The article stage needs the selected angle and outline. It does not need an unbounded transcript containing rejected attempts.
The required ScriptedRun adapter loads saved content and provider usage from JSON. It proves all
orchestration, validation, artifact, and reporting behavior without pretending to be a model.
Write an artifact at every gate
Section titled “Write an artifact at every gate”Rankforge maps stages to stable filenames:
pub const fn artifact(self) -> &'static str { match self { Stage::Research => "blog-1-research.md", Stage::Angle => "blog-2-angle.md", Stage::Outline => "blog-3-outline.md", Stage::Article => "blog-4-article.md", Stage::SeoPackage => "blog-5-seo-package.md", }}These are not debug leftovers. They are the workflow’s inspection surface. A human or evaluator can reject an angle before the expensive article call, compare outline coverage with the final article, and see whether packaging made the work thinner.
Break it deliberately
Section titled “Break it deliberately”Copy responses.json and swap the angle and outline entries. Rankforge must report the exact position,
expected stage, and actual stage. Then remove the outline step entirely. That is a stage-count failure,
not a model-quality failure.
Add a test that validates the whole run before creating an output directory. A half-written delivery pack should not look complete after a structural failure.
What you built
Section titled “What you built”The model produces content, but a deterministic state machine owns the allowed transition sequence. You learned enums and exhaustive matching because the product needs visible gates and diagnosable failure.