From e96b9e9aae364926d63d1af84f86cb2b13a94d51 Mon Sep 17 00:00:00 2001 From: Ethanfel Date: Mon, 29 Jun 2026 04:24:52 +0200 Subject: [PATCH] Add Krea2 footjob pose candidate --- categories/krea2_pov_pose_variants.json | 36 +++++++++++++++++++++++++ docs/krea2-pov-pose-atlas.md | 10 +++++++ tools/prompt_smoke.py | 35 +++++++++++++++++++++--- 3 files changed, 77 insertions(+), 4 deletions(-) diff --git a/categories/krea2_pov_pose_variants.json b/categories/krea2_pov_pose_variants.json index edec0f1..1a9c4bf 100644 --- a/categories/krea2_pov_pose_variants.json +++ b/categories/krea2_pov_pose_variants.json @@ -143,6 +143,42 @@ "guide_section": "", "notes": "Atlas supports low-head geometry, but this route still needs controlled fixed-seed tests before promotion to proven." } + }, + { + "key": "pov_footjob_frontal_sole_stroke", + "family": "footjob", + "status": "candidate", + "atlas_folders": ["footjob"], + "action_family": "outercourse", + "position_keys": ["footjob"], + "canonical_geometry": "Frontal first-person footjob view: viewer reclines with thighs framing the lower foreground, penis upright near the center, and the woman sits opposite with both soles and toes pressing around the shaft while her body and face stay behind the feet.", + "prompt_cues": [ + "POV footjob position", + "viewer reclines with thighs framing the lower foreground", + "woman sits opposite facing him with legs open toward the camera", + "both soles press around the upright penis", + "toes curl around the shaft as the feet stroke from base toward glans", + "woman's body and face remain visible behind her feet" + ], + "avoid_cues": [ + "generic foot contact without both soles around the shaft", + "hands replacing the feet as the main contact", + "mouth or hand action competing with the footjob", + "feet off to the side without centered penis contact" + ], + "reference_images": [ + "footjob/59_footjob.png", + "footjob/86_footjob.png" + ], + "generator_hook": { + "module": "krea_pov_actions.py", + "route_terms": ["footjob", "foot job", "feet stroking"] + }, + "evidence": { + "fixed_seed_tests": [], + "guide_section": "", + "notes": "Small atlas family with consistent frontal sole-contact geometry; needs fixed-seed Krea2 tests before promotion to proven." + } } ] } diff --git a/docs/krea2-pov-pose-atlas.md b/docs/krea2-pov-pose-atlas.md index a5a848c..8a81925 100644 --- a/docs/krea2-pov-pose-atlas.md +++ b/docs/krea2-pov-pose-atlas.md @@ -99,3 +99,13 @@ The handjob folder repeats a centered first-person layout: the viewer's thighs frame the lower edges, the woman faces the viewer between his legs, and her hand is the contact anchor on the shaft. Prompt the woman's hand ownership directly; viewer hands should not cover the action unless that is the intended variant. + +## Candidate Notes + +### Footjob + +The footjob folder is small but visually consistent: the viewer reclines with +thighs framing the lower foreground, the penis is upright near the center, and +the woman's soles/toes are the contact anchor while her body and face remain +behind the feet. Treat `pov_footjob_frontal_sole_stroke` as a candidate until it +has fixed-seed Krea2 evidence. diff --git a/tools/prompt_smoke.py b/tools/prompt_smoke.py index 4c3a190..2b67641 100644 --- a/tools/prompt_smoke.py +++ b/tools/prompt_smoke.py @@ -6788,6 +6788,7 @@ def smoke_krea2_pose_variant_catalog_policy() -> None: "pov_boobjob_upright_cleavage", "pov_handjob_upright_centered", "pov_ballsucking_low_head", + "pov_footjob_frontal_sole_stroke", ], f"Krea2 pose-variant outercourse filtering changed unexpectedly: {outercourse}", ) @@ -6799,6 +6800,12 @@ def smoke_krea2_pose_variant_catalog_policy() -> None: handjob["prompt_cues"].append("mutation should not leak") clean_handjob = krea2_pose_variant_catalog.get_variant("pov_handjob_upright_centered") _expect("mutation should not leak" not in clean_handjob.get("prompt_cues", []), "Catalog loader leaked caller mutation") + footjob = krea2_pose_variant_catalog.get_variant("pov_footjob_frontal_sole_stroke") + _expect(footjob.get("status") == "candidate", "Footjob variant should remain a candidate until fixed-seed evidence exists") + _expect( + any("both soles press" in str(cue) for cue in footjob.get("prompt_cues", [])), + "Footjob variant lost sole-contact cue", + ) refs = krea2_pose_variant_catalog.reference_paths("pov_boobjob_upright_cleavage") _expect(refs and all(path.name.endswith(".png") for path in refs), "Boobjob reference paths are not image paths") _expect(all("bg" not in str(path).lower() for path in refs), "Reference paths should not include background-only atlas images") @@ -6856,16 +6863,26 @@ def smoke_krea2_tuning_report_policy() -> None: ballsucking = by_key.get("pov_ballsucking_low_head") or {} _expect(ballsucking.get("coverage_state") == "needs_fixed_seed_tests", "Ballsucking report should need fixed-seed tests") _expect(ballsucking.get("accepted_evidence_count") == 0, "Ballsucking report should not have accepted evidence yet") + footjob = by_key.get("pov_footjob_frontal_sole_stroke") or {} + _expect(footjob.get("coverage_state") == "needs_fixed_seed_tests", "Footjob report should need fixed-seed tests") + _expect(footjob.get("accepted_evidence_count") == 0, "Footjob report should not have accepted evidence yet") summary = krea2_tuning_report.coverage_summary() _expect(summary.get("status_counts", {}).get("proven") == 3, "Krea2 tuning report proven count changed") - _expect(summary.get("status_counts", {}).get("candidate") == 1, "Krea2 tuning report candidate count changed") + _expect(summary.get("status_counts", {}).get("candidate") == 2, "Krea2 tuning report candidate count changed") _expect( - summary.get("variants_without_accepted_evidence") == ["pov_ballsucking_low_head"], + summary.get("variants_without_accepted_evidence") == [ + "pov_ballsucking_low_head", + "pov_footjob_frontal_sole_stroke", + ], f"Krea2 tuning report missing-evidence set changed: {summary.get('variants_without_accepted_evidence')}", ) plans = krea2_tuning_report.next_test_plans() - _expect([plan.get("key") for plan in plans] == ["pov_ballsucking_low_head"], "Krea2 tuning report next plans changed") - ballsucking_plan = plans[0] + _expect( + [plan.get("key") for plan in plans] == ["pov_ballsucking_low_head", "pov_footjob_frontal_sole_stroke"], + "Krea2 tuning report next plans changed", + ) + plan_by_key = {plan.get("key"): plan for plan in plans} + ballsucking_plan = plan_by_key["pov_ballsucking_low_head"] _expect( "woman bends forward and kneels very low" in " ".join(ballsucking_plan.get("prompt_cues") or []), "Ballsucking test plan lost atlas-backed prompt cue", @@ -6878,6 +6895,15 @@ def smoke_krea2_tuning_report_policy() -> None: any(str(path).endswith("ballsucking/101_ballsucking.png") for path in ballsucking_plan.get("reference_paths") or []), "Ballsucking test plan lost atlas reference path", ) + footjob_plan = plan_by_key["pov_footjob_frontal_sole_stroke"] + _expect( + "both soles press" in " ".join(footjob_plan.get("prompt_cues") or []), + "Footjob test plan lost sole-contact cue", + ) + _expect( + any(str(path).endswith("footjob/59_footjob.png") for path in footjob_plan.get("reference_paths") or []), + "Footjob test plan lost atlas reference path", + ) with tempfile.TemporaryDirectory() as tmpdir: atlas_root = Path(tmpdir) for folder in ("doggy", "doggy_control", "custom_pose", "custom_pose_control", "bg", "woman", "doggy_bg"): @@ -6910,6 +6936,7 @@ def smoke_krea2_tuning_report_policy() -> None: _expect("pov_custom_pose_candidate" in atlas_markdown, "Krea2 tuning report markdown lost suggested gap key") markdown = krea2_tuning_report.markdown_report() _expect("pov_ballsucking_low_head" in markdown, "Krea2 tuning report markdown lost candidate variant") + _expect("pov_footjob_frontal_sole_stroke" in markdown, "Krea2 tuning report markdown lost footjob candidate variant") _expect("needs_fixed_seed_tests" in markdown, "Krea2 tuning report markdown lost coverage state") _expect("Prompt cues" in markdown, "Krea2 tuning report markdown lost next-test cue section") _expect("Avoid cues" in markdown, "Krea2 tuning report markdown lost next-test avoid section")