Add Krea2 footjob pose candidate

This commit is contained in:
2026-06-29 04:24:52 +02:00
parent 5a5d5dd6fe
commit e96b9e9aae
3 changed files with 77 additions and 4 deletions
+36
View File
@@ -143,6 +143,42 @@
"guide_section": "", "guide_section": "",
"notes": "Atlas supports low-head geometry, but this route still needs controlled fixed-seed tests before promotion to proven." "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."
}
} }
] ]
} }
+10
View File
@@ -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 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; 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. 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.
+31 -4
View File
@@ -6788,6 +6788,7 @@ def smoke_krea2_pose_variant_catalog_policy() -> None:
"pov_boobjob_upright_cleavage", "pov_boobjob_upright_cleavage",
"pov_handjob_upright_centered", "pov_handjob_upright_centered",
"pov_ballsucking_low_head", "pov_ballsucking_low_head",
"pov_footjob_frontal_sole_stroke",
], ],
f"Krea2 pose-variant outercourse filtering changed unexpectedly: {outercourse}", 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") handjob["prompt_cues"].append("mutation should not leak")
clean_handjob = krea2_pose_variant_catalog.get_variant("pov_handjob_upright_centered") 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") _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") 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(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") _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 {} 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("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") _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() 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("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( _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')}", f"Krea2 tuning report missing-evidence set changed: {summary.get('variants_without_accepted_evidence')}",
) )
plans = krea2_tuning_report.next_test_plans() 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") _expect(
ballsucking_plan = plans[0] [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( _expect(
"woman bends forward and kneels very low" in " ".join(ballsucking_plan.get("prompt_cues") or []), "woman bends forward and kneels very low" in " ".join(ballsucking_plan.get("prompt_cues") or []),
"Ballsucking test plan lost atlas-backed prompt cue", "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 []), any(str(path).endswith("ballsucking/101_ballsucking.png") for path in ballsucking_plan.get("reference_paths") or []),
"Ballsucking test plan lost atlas reference path", "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: with tempfile.TemporaryDirectory() as tmpdir:
atlas_root = Path(tmpdir) atlas_root = Path(tmpdir)
for folder in ("doggy", "doggy_control", "custom_pose", "custom_pose_control", "bg", "woman", "doggy_bg"): 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") _expect("pov_custom_pose_candidate" in atlas_markdown, "Krea2 tuning report markdown lost suggested gap key")
markdown = krea2_tuning_report.markdown_report() markdown = krea2_tuning_report.markdown_report()
_expect("pov_ballsucking_low_head" in markdown, "Krea2 tuning report markdown lost candidate variant") _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("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("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") _expect("Avoid cues" in markdown, "Krea2 tuning report markdown lost next-test avoid section")