forge: always --effort max + multi-chapter batch (cap 20)

forge.rs threads Effort::Max on gen + cleanup. Audit + summarize stay
default — they're structured-output / tool-shaped tasks where extended
thinking doesn't help. Bumps subprocess timeout from 600s to 1800s so
max-effort prose-craft has the wall clock it needs.

continue_story::run takes a chapter_count param; loops gen+cleanup per
chapter with each iteration's just-written prose appended to context.
Audit fires once at end against the combined batch vs parent canon.
Cap is 20 (~5h wall clock, ~$600 at max effort — beyond that is
operationally absurd).

CLI: 'skald continue --chapters N'. Web: numeric field on both new-
story and continue forms, 1..=20, defaults to 1.

Vendored clawdforge SDK refreshed for the Effort enum.
This commit is contained in:
Kayos 2026-05-13 14:26:53 -07:00
parent 20e262c85d
commit 3a749b7643
6 changed files with 168 additions and 54 deletions

View file

@ -30,7 +30,7 @@
use std::time::Duration;
use clawdforge::{Client, ClientBuilder, RunRequest, RunResult, SystemMode};
use clawdforge::{Client, ClientBuilder, Effort, RunRequest, RunResult, SystemMode};
use serde::{Deserialize, Serialize};
use crate::authors::AuthorWithRevision;
@ -87,11 +87,10 @@ impl Forge {
let client = ClientBuilder::default()
.base_url(&cfg.base_url)
.token(&cfg.app_token)
// Generation passes are slow — 600s is the clawdforge
// server-side max anyway, and gen passes routinely hit
// 5+ minutes on opus max-effort. Default 120s would
// strand them.
.timeout(Duration::from_secs(600))
// Generation passes at --effort max can run 1020 min wall
// clock. clawdforge's server-side cap is 1800s — match it.
// Default 120s would strand any prose-craft pass.
.timeout(Duration::from_secs(1800))
.user_agent(concat!("skald/", env!("CARGO_PKG_VERSION")))
.build()?;
Ok(Self {
@ -125,7 +124,8 @@ impl Forge {
model: Some(self.model.clone()),
system: Some(system),
system_mode: Some(mode),
timeout_secs: Some(600),
effort: Some(Effort::Max),
timeout_secs: Some(1800),
..Default::default()
};
let r = self.client.run(body).await?;
@ -148,7 +148,8 @@ impl Forge {
model: Some(self.model.clone()),
system: Some(system),
system_mode: Some(mode),
timeout_secs: Some(600),
effort: Some(Effort::Max),
timeout_secs: Some(1800),
..Default::default()
};
let r = self.client.run(body).await?;