Enforce Genarrative play-type SOP and update docs

Rewrite Genarrative play-type integration guidance across .codex and .hermes to define a platform-level SOP: default to form/image workbench, unify single-image asset slots (CreativeImageInputPanel), standardize series-material sheet->cut->transparent->OSS pipeline, and forbid copying legacy chat/agent workflows as the default. Add decision-log entry freezing the SOP and a pitfalls note warning against direct reuse of old play tools. Update CONTEXT.md and docs/README.md, add a new PRD file, and apply related small server-side changes (module-auth, spacetime-client mappers and runtime) to align back-end code with the new contracts and flows.
This commit is contained in:
2026-05-20 12:12:00 +08:00
parent f370539a6f
commit 3931442249
123 changed files with 15514 additions and 3419 deletions

View File

@@ -0,0 +1,401 @@
use serde::{Deserialize, Serialize};
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub enum JumpHopDifficulty {
Easy,
Standard,
Advanced,
Challenge,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub enum JumpHopStylePreset {
MinimalBlocks,
PaperToy,
NeonGlass,
ForestStone,
FutureMetal,
Custom,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub enum JumpHopGenerationStatus {
Draft,
Generating,
Ready,
Failed,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub enum JumpHopTileType {
Start,
Normal,
Target,
Finish,
Bonus,
Accent,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub enum JumpHopActionType {
CompileDraft,
RegenerateCharacter,
RegenerateTiles,
UpdateWorkMeta,
UpdateDifficulty,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub enum JumpHopRunStatus {
Playing,
Failed,
Cleared,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "kebab-case")]
pub enum JumpHopJumpResult {
Miss,
Hit,
Perfect,
Finish,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct JumpHopWorkspaceCreateRequest {
pub template_id: String,
pub work_title: String,
pub work_description: String,
pub theme_tags: Vec<String>,
pub difficulty: JumpHopDifficulty,
pub style_preset: JumpHopStylePreset,
pub character_prompt: String,
pub tile_prompt: String,
#[serde(default)]
pub end_mood_prompt: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct JumpHopActionRequest {
pub action_type: JumpHopActionType,
#[serde(default)]
pub work_title: Option<String>,
#[serde(default)]
pub work_description: Option<String>,
#[serde(default)]
pub theme_tags: Option<Vec<String>>,
#[serde(default)]
pub difficulty: Option<JumpHopDifficulty>,
#[serde(default)]
pub style_preset: Option<JumpHopStylePreset>,
#[serde(default)]
pub character_prompt: Option<String>,
#[serde(default)]
pub tile_prompt: Option<String>,
#[serde(default)]
pub end_mood_prompt: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct JumpHopCharacterAsset {
pub asset_id: String,
pub image_src: String,
pub image_object_key: String,
pub asset_object_id: String,
pub generation_provider: String,
pub prompt: String,
pub width: u32,
pub height: u32,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct JumpHopTileAsset {
pub tile_type: JumpHopTileType,
pub image_src: String,
pub image_object_key: String,
pub asset_object_id: String,
pub source_atlas_cell: String,
pub visual_width: u32,
pub visual_height: u32,
pub top_surface_radius: f32,
pub landing_radius: f32,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct JumpHopScoring {
pub charge_to_distance_ratio: f32,
pub max_charge_ms: u32,
pub hit_bonus: u32,
pub perfect_bonus: u32,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct JumpHopPlatform {
pub platform_id: String,
pub tile_type: JumpHopTileType,
pub x: f32,
pub y: f32,
pub width: f32,
pub height: f32,
pub landing_radius: f32,
pub perfect_radius: f32,
pub score_value: u32,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct JumpHopPath {
pub seed: String,
pub difficulty: JumpHopDifficulty,
pub platforms: Vec<JumpHopPlatform>,
pub finish_index: u32,
pub camera_preset: String,
pub scoring: JumpHopScoring,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct JumpHopLastJump {
pub charge_ms: u32,
pub jump_distance: f32,
pub target_platform_index: u32,
pub landed_x: f32,
pub landed_y: f32,
pub result: JumpHopJumpResult,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct JumpHopDraftResponse {
pub template_id: String,
pub template_name: String,
#[serde(default)]
pub profile_id: Option<String>,
pub work_title: String,
pub work_description: String,
pub theme_tags: Vec<String>,
pub difficulty: JumpHopDifficulty,
pub style_preset: JumpHopStylePreset,
pub character_prompt: String,
pub tile_prompt: String,
#[serde(default)]
pub end_mood_prompt: Option<String>,
#[serde(default)]
pub character_asset: Option<JumpHopCharacterAsset>,
#[serde(default)]
pub tile_atlas_asset: Option<JumpHopCharacterAsset>,
#[serde(default)]
pub tile_assets: Vec<JumpHopTileAsset>,
#[serde(default)]
pub path: Option<JumpHopPath>,
#[serde(default)]
pub cover_composite: Option<String>,
pub generation_status: JumpHopGenerationStatus,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct JumpHopSessionSnapshotResponse {
pub session_id: String,
pub owner_user_id: String,
pub status: JumpHopGenerationStatus,
#[serde(default)]
pub draft: Option<JumpHopDraftResponse>,
pub created_at: String,
pub updated_at: String,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct JumpHopSessionResponse {
pub session: JumpHopSessionSnapshotResponse,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct JumpHopActionResponse {
pub action_type: JumpHopActionType,
pub session: JumpHopSessionSnapshotResponse,
#[serde(default)]
pub work: Option<JumpHopWorkProfileResponse>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct JumpHopWorkSummaryResponse {
pub runtime_kind: String,
pub work_id: String,
pub profile_id: String,
pub owner_user_id: String,
#[serde(default)]
pub source_session_id: Option<String>,
pub work_title: String,
pub work_description: String,
pub theme_tags: Vec<String>,
pub difficulty: JumpHopDifficulty,
pub style_preset: JumpHopStylePreset,
#[serde(default)]
pub cover_image_src: Option<String>,
pub publication_status: String,
pub play_count: u32,
pub updated_at: String,
#[serde(default)]
pub published_at: Option<String>,
pub publish_ready: bool,
pub generation_status: JumpHopGenerationStatus,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct JumpHopWorkProfileResponse {
#[serde(flatten)]
pub summary: JumpHopWorkSummaryResponse,
pub draft: JumpHopDraftResponse,
pub path: JumpHopPath,
pub character_asset: JumpHopCharacterAsset,
pub tile_atlas_asset: JumpHopCharacterAsset,
pub tile_assets: Vec<JumpHopTileAsset>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct JumpHopWorksResponse {
pub items: Vec<JumpHopWorkSummaryResponse>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct JumpHopWorkDetailResponse {
pub item: JumpHopWorkProfileResponse,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct JumpHopWorkMutationResponse {
pub item: JumpHopWorkProfileResponse,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct JumpHopGalleryCardResponse {
pub public_work_code: String,
pub work_id: String,
pub profile_id: String,
pub owner_user_id: String,
pub author_display_name: String,
pub work_title: String,
pub work_description: String,
#[serde(default)]
pub cover_image_src: Option<String>,
pub theme_tags: Vec<String>,
pub difficulty: JumpHopDifficulty,
pub style_preset: JumpHopStylePreset,
pub publication_status: String,
pub play_count: u32,
pub updated_at: String,
#[serde(default)]
pub published_at: Option<String>,
pub generation_status: JumpHopGenerationStatus,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct JumpHopGalleryResponse {
pub items: Vec<JumpHopGalleryCardResponse>,
pub has_more: bool,
#[serde(default)]
pub next_cursor: Option<String>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct JumpHopGalleryDetailResponse {
pub item: JumpHopWorkProfileResponse,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct JumpHopRuntimeRunSnapshotResponse {
pub run_id: String,
pub profile_id: String,
pub owner_user_id: String,
pub status: JumpHopRunStatus,
pub current_platform_index: u32,
pub score: u32,
pub combo: u32,
pub path: JumpHopPath,
#[serde(default)]
pub last_jump: Option<JumpHopLastJump>,
pub started_at_ms: u64,
#[serde(default)]
pub finished_at_ms: Option<u64>,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct JumpHopRunResponse {
pub run: JumpHopRuntimeRunSnapshotResponse,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct JumpHopStartRunRequest {
pub profile_id: String,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct JumpHopJumpRequest {
pub charge_ms: u32,
pub client_event_id: String,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct JumpHopRestartRunRequest {
pub client_action_id: String,
}
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "camelCase")]
pub struct JumpHopJumpResponse {
pub run: JumpHopRuntimeRunSnapshotResponse,
}
#[cfg(test)]
mod tests {
use super::*;
use serde_json::json;
#[test]
fn jump_hop_workspace_request_uses_camel_case() {
let payload = serde_json::to_value(JumpHopWorkspaceCreateRequest {
template_id: "jump-hop".to_string(),
work_title: "跳一跳".to_string(),
work_description: "俯视角跳跃闯关".to_string(),
theme_tags: vec!["休闲".to_string()],
difficulty: JumpHopDifficulty::Easy,
style_preset: JumpHopStylePreset::MinimalBlocks,
character_prompt: "角色".to_string(),
tile_prompt: "地块".to_string(),
end_mood_prompt: None,
})
.expect("payload should serialize");
assert_eq!(payload["templateId"], json!("jump-hop"));
assert_eq!(payload["difficulty"], json!("easy"));
assert_eq!(payload["stylePreset"], json!("minimal-blocks"));
}
}

View File

@@ -12,6 +12,7 @@ pub mod creation_audio;
pub mod creation_entry_config;
pub mod creative_agent;
pub mod hyper3d;
pub mod jump_hop;
pub mod llm;
pub mod match3d_agent;
pub mod match3d_runtime;