Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions crates/renderflow-core/data/profiles/coloring-book-v1.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
schema: renderflow.profile/v1
description: Versioned rights-aware coloring-book release bundle
intermediates: cache_only
targets:
- id: coloring-book.print.book
role: print/book
format: pdf
- id: coloring-book.print.proof
role: print/proof
format: pdf
- id: coloring-book.pages.raster
role: pages/raster
format: png
requirement: optional
- id: coloring-book.pages.vector
role: pages/vector
format: svg
requirement: optional
- id: coloring-book.accessible.web
role: accessible/web
format: html
requirement: optional
policy:
validation:
required: true
failure_mode: branch_local
allow_unavailable: false
network: deny
ai: deny
11 changes: 11 additions & 0 deletions crates/renderflow-core/src/app.rs
Original file line number Diff line number Diff line change
Expand Up @@ -283,6 +283,17 @@ pub fn run_cli(cli: Cli) -> Result<()> {
EbookCommands::Capabilities { format } => commands::ebook::run_capabilities(&format)?,
},
Some(Commands::Publication { subcommand }) => match subcommand {
PublicationCommands::ColoringBookPreflight {
contract,
output,
format,
allow_remote,
} => commands::publication::run_coloring_book_preflight(
&contract,
output.as_deref(),
&format,
allow_remote,
)?,
PublicationCommands::MagazineCandidates {
config,
asset_role,
Expand Down
23 changes: 20 additions & 3 deletions crates/renderflow-core/src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ pub enum Commands {
renderflow build --target pdf Build only the PDF output via graph resolution\n \
renderflow build --profile everything Build the maximal available artifact forest
\
renderflow build --profile magazine Build a versioned magazine release bundle")]
renderflow build --profile magazine Build a versioned magazine release bundle
\
renderflow build --profile coloring-book Build a rights-aware coloring-book bundle")]
Build {
/// Path to the renderflow configuration file
#[arg(long, default_value = "renderflow.yaml", value_name = "FILE")]
Expand All @@ -90,7 +92,7 @@ pub enum Commands {
#[arg(long, value_name = "FORMAT", conflicts_with_all = ["all", "profile"])]
target: Option<String>,

/// Build a named, versioned derivative profile. `everything` and `magazine` are bundled.
/// Build a named, versioned derivative profile. `everything`, `magazine`, and `coloring-book` are bundled.
#[arg(long, value_name = "PROFILE", conflicts_with_all = ["target", "all"])]
profile: Option<String>,

Expand Down Expand Up @@ -424,6 +426,21 @@ pub enum VideoCommands {

#[derive(Subcommand)]
pub enum PublicationCommands {
/// Validate a rights-aware coloring-book contract entirely offline.
ColoringBookPreflight {
/// Coloring-book source contract in YAML or JSON.
#[arg(long, value_name = "FILE")]
contract: String,
/// Optional validation report output file.
#[arg(long, value_name = "FILE")]
output: Option<String>,
/// Output format: text (default), json, or yaml.
#[arg(long, default_value = "text", value_name = "FORMAT")]
format: String,
/// Acknowledge review of provenance from remote providers. No provider is invoked.
#[arg(long)]
allow_remote: bool,
},
/// Produce candidate-only magazine briefs and metadata from Artifact DNA.
MagazineCandidates {
/// Renderflow v2 publication specification.
Expand Down Expand Up @@ -876,7 +893,7 @@ pub enum GraphCommands {
#[arg(long, value_name = "FORMAT", conflicts_with = "profile")]
target: Option<String>,

/// Resolve a named, versioned derivative profile. `everything` and `magazine` are bundled.
/// Resolve a named, versioned derivative profile. `everything`, `magazine`, and `coloring-book` are bundled.
#[arg(long, value_name = "PROFILE", conflicts_with = "target")]
profile: Option<String>,

Expand Down
40 changes: 40 additions & 0 deletions crates/renderflow-core/src/commands/publication.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use crate::ai::{
};
use crate::artifact::ArtifactStore;
use crate::dna::ArtifactDna;
use crate::publication::coloring_book::{evaluate_coloring_book, ColoringBookValidationReport};
use crate::publication::lulu::{
evaluate_request, LuluConformanceReport, LuluEligibility, LuluRulePack,
};
Expand All @@ -19,6 +20,45 @@ use crate::publication::magazine::{
};
use crate::spec::load_spec;

pub fn run_coloring_book_preflight(
contract: &str,
output: Option<&str>,
format: &str,
allow_remote: bool,
) -> Result<()> {
let report = evaluate_coloring_book(Path::new(contract), allow_remote)?;
emit_coloring_book_report(&report, format, output)?;
if !report.is_valid() {
anyhow::bail!("coloring-book contract is not release-ready; see report")
}
Ok(())
}

fn emit_coloring_book_report(
report: &ColoringBookValidationReport,
format: &str,
output: Option<&str>,
) -> Result<()> {
if format.eq_ignore_ascii_case("text") {
let mut text = format!(
"Coloring-book preflight\nStatus: {:?}\nRelease eligible: {}\nOffline: yes\nRemote provenance opt-in: {}\n\nFindings:\n",
report.status, report.release_eligible, report.policy.remote_provider_opt_in
);
if report.findings.is_empty() {
text.push_str(" none\n");
}
for finding in &report.findings {
text.push_str(&format!(
" [{:?}] {} at {}: {}\n",
finding.severity, finding.code, finding.path, finding.message
));
}
write(&text, output)
} else {
emit(report, format, output)
}
}

#[allow(clippy::too_many_arguments)]
pub fn run_magazine_candidates(
config: &str,
Expand Down
4 changes: 4 additions & 0 deletions crates/renderflow-core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ pub use intake::{
IntakeSignalKind, ProvenanceValue, ProviderInspection, ResolvedArtifactProfile,
INTAKE_SCHEMA_V1,
};
pub use publication::coloring_book::{
evaluate_coloring_book, validate_coloring_book, ColoringBookContract,
ColoringBookValidationReport, COLORING_BOOK_REPORT_SCHEMA_V1, COLORING_BOOK_SCHEMA_V1,
};
pub use publication::magazine::{
build_magazine_candidates, create_magazine_ai_request, MagazineAiCandidate, MagazineAiStatus,
MagazineCandidateEnvelope, MagazineCandidatePolicy, MAGAZINE_CANDIDATE_SCHEMA_V1,
Expand Down
10 changes: 8 additions & 2 deletions crates/renderflow-core/src/planning.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1402,14 +1402,20 @@ fn apply_request_overrides(spec: &mut SpecV2, request: &PlanningRequest) -> Resu
..TargetSelection::default()
};
} else if let Some(profile) = &request.profile {
if matches!(profile.as_str(), "everything" | "magazine")
&& !spec.profiles.contains_key(profile)
if matches!(
profile.as_str(),
"everything" | "magazine" | "coloring-book"
) && !spec.profiles.contains_key(profile)
{
let (source, label) = match profile.as_str() {
"magazine" => (
include_str!("../data/profiles/magazine-v1.yaml"),
"magazine",
),
"coloring-book" => (
include_str!("../data/profiles/coloring-book-v1.yaml"),
"coloring-book",
),
_ => (
include_str!("../data/profiles/everything-v1.yaml"),
"everything",
Expand Down
1 change: 1 addition & 0 deletions crates/renderflow-core/src/publication.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Provider-neutral publication contracts and deterministic release metadata.

pub mod coloring_book;
pub mod lulu;
pub mod magazine;

Expand Down
Loading
Loading