video_editing_poc/.claude/skills/video-editing-architecture-.../SKILL.md

25 KiB

name description
video-editing-architecture-contract Load when changing module boundaries, workflow orchestration, domain models, ports/adapters, project-folder protocols, state transitions, render approval, local-model integration, or persistence in the video-editing service; use it to preserve current contracts while moving the code toward a production-grade modular monolith.

Video Editing Architecture Contract

Use this contract before changing a workflow boundary, state transition, persisted JSON shape, project directory, scheduler, renderer, model worker, or adapter. Treat it as a map of the implementation as verified on 2026-07-21, not as proof that the target architecture already exists.

Do not use this skill for

Need Load instead
Decide whether a proposed change is allowed, gated, or ready to merge video-editing-change-control
Reproduce a build or repair a workstation/runtime video-editing-build-and-env
Look up a property, default, or environment variable video-editing-config-and-flags
Diagnose a concrete failure or stuck project video-editing-debugging-playbook
Decide whether media output meets its evidence bar video-editing-validation-and-qa
Operate schedulers, inputs, projects, or outputs video-editing-run-and-operate
Improve highlight selection or cinematic quality video-editing-cinematic-highlights-campaign

Do not use architecture language to waive a quality gate. Route every behavior-changing proposal through video-editing-change-control.

Define the terms

  • Capability: a cohesive business responsibility, such as upload-and-clipping or cinematic highlight production.
  • Domain: business concepts and invariants that can be tested without Spring, HTTP, JPA, FFmpeg, or the filesystem.
  • Application service: orchestration for one use case; it calls domain behavior and outbound ports.
  • Port: an interface owned by the core that expresses a required inbound or outbound capability.
  • Adapter: framework or infrastructure code implementing a port, such as JPA, S3, FFmpeg, or a controller.
  • Filesystem state machine: a workflow in which directory location and artifact presence act as queue and state signals.
  • Contract artifact: a persisted file whose name, location, or JSON shape is consumed by another stage.
  • Modular monolith: one deployable application with enforced capability boundaries and inward-pointing dependencies. One Maven module or one JVM process alone does not make the code modular.

Start with the honest baseline

As of 2026-07-21, this is one Spring Boot application and one Maven module. It contains four workflows with different contracts. The upload-and-clipping workflow has recognizable ports and adapters. The folder workflow is a small vertical slice. The editing package contains multi-clip and single-source highlight domains, orchestration, filesystem persistence, process execution, and model-worker integration together.

The project does not currently have Spring Modulith, ArchUnit, automated package-boundary rules, a module diagram in code, or enforced clean-architecture dependency direction. VideoAssetService imports HTTP request/response DTOs, and EditProjectController invokes EditRenderer directly. Do not describe the current tree as a completed hexagonal architecture.

Map the four workflows

Workflow Trigger and orchestrator Durable state Main outputs Representative tests
API upload and clipping /v1/video-assets, /v1/clip-jobs, and /v1/clips; VideoAssetService; ClipJobQueuePort; ClipProcessor Repository records, object keys, and optionally queue_messages Uploaded clip objects plus Clip records and signed download URLs VideoAssetControllerTest, DatabaseBackedClipJobQueueAdapterTest
Folder segmentation Poll input-directory; FolderVideoScanScheduler -> FolderVideoValidator -> FolderFfmpegClipper File location: source -> working -> processed/rejected; .failed is last-resort quarantine output-directory/<source-base>[-N]/clip_%05d.<container> FolderVideoScanSchedulerTest, FolderFfmpegIntegrationTest
Multi-clip cinematic edit REST API or poll editing.local-director.source-directory; LocalDirectorScheduler, EditPlanInboxScanner, FfmpegEditRenderer project.json, analysis/plan/manifest JSON, inbox file names, and source-folder location One project final.mp4, render-manifest.json, qa-report.json CinematicEditingIntegrationTest, EditPlanInboxScannerTest, FfmpegEditRendererTest
Single-source highlights Poll editing.highlight-scheduler.source-directory; HighlightSourceScheduler, HighlightDirectorPlanScanner, HighlightDirectorFlowService Highlight project tree, project.json, director-plan presence, approval marker, final-file presence Per-highlight final/preview/QA/manifests plus project final.mp4 HighlightSourceSchedulerTest, HighlightDirectorFlowServiceTest, HighlightFfmpegRendererTest

Do not connect two workflows merely because both call FFmpeg or share records. First identify the owner of the use case, state, retry semantics, and output contract.

Preserve the implemented dependency map

Upload and clipping

Follow this current call direction while improving it incrementally:

api controllers
    -> application.VideoAssetService
        -> application.VideoClippingRepository
        -> queue.ClipJobQueuePort
        -> storage.ObjectStoragePort

queue adapters -> processing.ClipProcessor
processing.ClipProcessor
    -> application.VideoClippingRepository
    -> processing.VideoClipperPort
    -> storage.ObjectStoragePort

outbound implementations:
    infrastructure.InMemoryVideoAssetRepository
    persistence.JpaVideoClippingRepository
    queue.InMemoryClipJobQueueAdapter / DatabaseBackedClipJobQueueAdapter
    processing.StubVideoClipperAdapter / FfmpegVideoClipperAdapter
    storage.InMemoryObjectStorageAdapter / S3ObjectStorageAdapter

Treat VideoClippingRepository, ClipJobQueuePort, VideoClipperPort, and ObjectStoragePort as existing contracts. Do not make core business behavior depend on JPA entities, Spring Data repositories, S3 types, or ProcessBuilder.

Current exception: VideoAssetService consumes types from api.dto. Move transport-to-command mapping to the inbound adapter when changing this surface; preserve API compatibility and add characterization tests first.

Folder segmentation

Treat org.example.videoclips.folder as a self-contained file-ingestion capability. It currently calls FFprobe and FFmpeg directly through package-private functional test seams. Do not route folder jobs through the API repository or object storage unless a stated product requirement unifies their ownership and recovery model.

Editing workflows

Recognize these existing interfaces, but do not mistake their package location for enforced isolation:

Port-like interface Current owner and purpose
EditProjectStore Multi-clip project tree and top-level JSON artifacts
HighlightProjectStore Single-source highlight project tree and nested artifacts
EditRenderer Render a saved multi-clip project by project ID
VoiceoverGenerator Produce project voiceover from timed lines
EditAssetProvider / EditAssetLibrary Resolve or select local media assets
VisualAnalysisProvider Analyze a clip from metadata, thumbnails, and shots

Keep application-facing interfaces free of Process, HTTP client, and framework-specific return types. Introduce a port only for a concrete substitution, isolation, or testability need.

Preserve each state machine

API upload and clipping states

VideoAsset: PENDING_UPLOAD -> UPLOADED -> PROCESSING -> READY
                                         | failure/retry -> UPLOADED
             any retained terminal asset -> DELETED

ClipJob: QUEUED -> RUNNING -> SUCCEEDED
                    |        -> FAILED
         QUEUED or RUNNING -> CANCEL_REQUESTED -> CANCELLED

Enforce these invariants:

  • Create a job only for an UPLOADED or READY asset.
  • Keep tenant ownership checks at every read or mutation boundary.
  • Preserve idempotency for create-asset and create-job requests.
  • Store generated media before exposing a persisted clip as downloadable.
  • Return an asset to UPLOADED after a failed or cancelled processing attempt.
  • Keep DB-queue retry ownership in DatabaseBackedClipJobQueueAdapter; do not persist terminal failure from ClipProcessor before retries are exhausted.

Folder file states

source candidate --atomic move--> working
working --valid + FFmpeg success--> processed
working --invalid or processing failure--> rejected
working --rejection move failure--> <name>.failed beside active file

Preserve one-file-per-scan behavior, natural filename ordering, refusal to overwrite a destination, and exclusion of hidden/partial/failed files. Atomic moves assume directories share a filesystem; treat cross-device deployment as unsupported until tested and designed.

Multi-clip edit states

The implemented local-director path normally follows:

CREATED -> ANALYZING -> ANALYZED -> WAITING_FOR_DIRECTOR
        -> PLANNED -> RENDERING -> RENDERED
             any handled workflow/render failure -> FAILED

PLANNING exists in EditProjectStatus but is not part of the normal scheduler sequence. EditProjectService.updateProject does not validate legal transitions. Add transition enforcement before relying on status as a concurrency or authorization boundary.

The source folder also moves source -> working -> processed/rejected. An accepted inbox plan moves from inbox/edit-plan.json to inbox/edit-plan.json.accepted; a rejected plan becomes .rejected. Approval is signaled by inbox/approved.flag when configured.

Single-source highlight states

The observed new-project path is:

CREATED -> WAITING_FOR_DIRECTOR -> RENDERING -> RENDERED
             invalid empty director plan -> FAILED

ANALYZING and PLANNED exist in HighlightProjectStatus, but the primary scheduler/flow does not currently persist them. The render scanner considers a project renderable when director/edit-plan.json exists and project final.mp4 does not. Its in-process AtomicBoolean prevents overlapping scans only inside one JVM; it is not a distributed claim.

Do not infer progress solely from the enum. Check artifacts and logs. Before horizontal scaling, add durable claims, leases, idempotent stages, and recovery tests.

Preserve the two editing contracts separately

Multi-clip contract

The root is video-clipping.editing.project-directory (default ./output/edit-projects). A project currently uses:

<project-id>/
  project.json
  analysis.json
  cinematic-highlight-analysis.json
  category.json
  highlight-candidates.json
  thumbnails/  contact-sheets/  proxies/  audio/
  inbox/
  edit-plan.json
  final.mp4
  render-manifest.json
  qa-report.json

EditProject describes an input directory containing multiple clips. EditPlan.decisions can reference multiple clip IDs and builds one combined timeline.

Single-source highlight contract

The root is video-clipping.editing.highlight-project-directory (default ./output/highlight-projects). FileSystemHighlightProjectStore creates:

<project-id>/
  project.json
  manifest.json
  source/
  analysis/
    ffprobe.json
    scene-segments.json
    audio-analysis.json
    visual-analysis.json
    source-analysis.json
    frames/  contact-sheets/  proxies/  audio/
  director/
    director-brief.md
    director-prompt.md
    edit-plan.json
    approved.flag                 # only when approval is required
  assets/{voiceover,music,sfx,overlays}/
  highlights/<highlight-id>/
    storyboard.md
    edit-plan.json
    assets/
    final.mp4
    preview.mp4
    render-manifest.json
    qa-report.json
  final.mp4
  render-manifest.json

HighlightFolderContract.standard() lists intended names such as shots.json, scenes.json, transcript.json, audio-events.json, category.json, and highlight-candidates.json. The source analyzer writes its five technical analysis JSON files; the scheduler's HighlightCandidateGenerator separately writes category.json and highlight-candidates.json. shots.json, scenes.json, transcript.json, and audio-events.json remain manifest intentions, so do not claim every listed artifact exists.

HighlightDirectorPlan selects several windows from exactly one source and carries story purpose, visual treatment, music/SFX directions, voiceover, and overlays. HighlightDirectorFlowService converts each item to a single-decision EditPlan, renders per-highlight outputs, then concatenates them.

Why the contracts remain separate

Keep them separate because they have different aggregates, input cardinality, director schemas, folder protocols, approval locations, output cardinality, scheduler ownership, and recovery signals. Sharing EditPlan, RenderManifest, or analysis helpers is implementation reuse, not schema equivalence.

Do not merge the stores, statuses, or roots until all of these exist:

  • A versioned replacement contract and explicit migration/compatibility decision.
  • Golden JSON fixtures for both schemas and backward-read tests.
  • Recovery tests from every durable stage boundary.
  • A collision strategy for project IDs and artifact names.
  • An approval model that cannot be bypassed by REST or scheduler entry points.
  • A rollout and rollback plan approved through video-editing-change-control.

Enforce load-bearing invariants

Apply this checklist to every architecture change:

  • Keep project IDs restricted to [A-Za-z0-9][A-Za-z0-9._-]{0,127} and reject ...
  • Normalize and containment-check every project-relative path before reading or writing it.
  • Never accept an absolute path or backslash-containing relative artifact name in the highlight store.
  • Refuse overwrite when claiming source files; never silently replace an existing processed/rejected input.
  • Treat source media and completed contract artifacts as immutable inputs to later stages.
  • Validate project ID, source identity, time ranges, supported effects, and required licensed assets before rendering.
  • Persist an artifact successfully before advertising the status that promises it exists.
  • Make retries idempotent: never duplicate a rendered highlight, queue message, clip record, or asset charge.
  • Record model identity/version, input hash, asset provenance/license, FFmpeg command, and output hash for reproducibility.
  • Derive acceptance from measured media probes and approved creative review, never file existence or visual impression alone.
  • Keep transport DTOs, JPA entities, and worker-process details outside the domain.
  • Keep framework annotations out of new domain objects unless a decision record justifies them.

Enforce the local-runtime and render prohibitions

The following are project directives, even where current defaults or code contradict them:

  1. Do not download dependencies or models automatically at startup, test time, or job time.
  2. Do not call external AI services. Model/media inference must use in-process calls or approved non-network inter-process communication; loopback HTTP is still network and is not a certified path.
  3. Do not permit model/media inference or acquisition to use any network, including loopback. Pre-provision and verify dependencies, model weights, and licenses during an approved artifact-build process. Production API, database, storage, and telemetry integrations remain separately approved and secured network boundaries.
  4. Do not ingest or generate assets without recorded licensing and provenance.
  5. Do not substitute silence, sine tones, or placeholder media for missing voiceover, music, or SFX.
  6. Do not render without explicit approval bound to the exact plan and source digest.
  7. Do not change production-facing defaults without change-control approval, compatibility analysis, and rollback evidence.

Current disqualifying gaps include:

  • tools/run_local_asset_worker.sh and tools/run_local_cv_worker.sh install Python dependencies in auto mode; the CV script can load/download YOLO weights, and AudioCraft get_pretrained can fetch models.
  • The 2026-07-21 working tree removes worker silence/tone/host-speech fallbacks and makes requested assets mandatory at both renderer boundaries. Standalone bootstrap scripts and local-CV heuristic fallback remain noncompliant gaps.
  • application.yml enables highlight ingestion but now defaults highlight rendering off and director approval on. Approval remains a bare, non-digest-bound file.
  • POST /v1/edit-projects/{projectId}:render invokes the renderer without an approval check.
  • HighlightFfmpegRenderer can copy video forward after audio mixing fails, and several QA checks are hard-coded true rather than measured.

Treat these as open architecture defects. Do not operate them as production behavior, and do not hide them with documentation. Repair them only through the change-control and validation skills.

Know the current weak points

Weak point Evidence and consequence Required direction
No enforced modules One Maven module; no ArchUnit/Modulith dependency test Add capability-oriented boundaries and fail the build on violations
Anemic state records Public records allow unchecked states; update methods accept arbitrary status Put invariants and legal transitions in domain/application code
Filesystem is database and queue Direct JSON writes, artifact-presence polling, local AtomicBoolean guards Add atomic publish, durable claim/lease, restart recovery, retention, and concurrency tests
Highlight analysis is incomplete New-source analyzer does not call CinematicHighlightAnalyzer; prompt tolerates absent category/candidates Make multimodal candidate production an explicit, measured stage
Director plan contract is gated but creative truth is not proven HighlightDirectorPlanValidator validates identity, category/candidate linkage, safe IDs, ranges, configured bounds, and required directions; it does not verify visible claims, asset licenses, or artistic fit Keep media review and explicit render approval; add provenance and content-grounding evidence before claiming production readiness
Local worker boundary is porous Shell bootstrap downloads; loopback endpoint is configurable; models are named rather than content-addressed Package locked runtime/model artifacts; verify hashes; replace loopback HTTP with in-process or approved non-network IPC and deny inference networking
QA can overstate success Highlight checks assert duration, overlays, assets, and mastering without measuring output Probe outputs and fail closed on production-required checks
Renderer configuration leaks Project concatenation uses literal ffmpeg/ffprobe while other stages use typed properties Route executables and process policy through an outbound media-tool port
OS assumptions leak macOS say, local paths, atomic rename, and worker virtualenvs are runtime details Prove equivalent macOS dev and Linux/VPS/cloud behavior; remove OS-specific production fallbacks
API/core dependency points outward Application service imports api.dto; controller directly calls renderer Map DTOs at inbound adapters and expose application use cases
Local project state blocks horizontal safety Edit/highlight projects require shared local paths and have no distributed ownership Define durable object/project storage before claiming cloud scalability

Move toward the target modular monolith

Use capabilities, not technical-layer-only packages, as the top-level target. Keep one deployable unless measured requirements justify distribution.

org.example.videoclips
  clipping/          # upload sessions, assets, clip jobs, clip publication
  folderingestion/   # watched-folder segmentation workflow
  multiedit/         # multi-source project, plan, render lifecycle
  highlights/        # single-source analysis, direction, assets, render lifecycle
  media/             # approved FFmpeg/FFprobe and local-model runtime adapters
  platform/          # configuration, persistence wiring, security, observability

Within each capability, point dependencies inward:

inbound adapters -> application use cases -> domain
outbound adapters -> application-owned outbound ports
configuration -> all adapters for composition only
domain -> no Spring, HTTP, persistence, filesystem, process, or sibling adapter package

Expose cross-capability access through a small documented application API or durable event. Do not import another capability's adapter or persistence entity. Do not create a generic base service, universal repository, universal mapper, CQRS infrastructure, event bus, or microservice without a measured requirement.

Execute boundary migration safely

  1. Characterize the current API, JSON, file names, state changes, and restart behavior with tests.
  2. Write an ADR and route it through video-editing-change-control.
  3. Define the destination capability and its public application API.
  4. Move domain types without changing behavior; keep compatibility mapping at the old boundary.
  5. Move outbound interfaces inward, then adapt JPA, filesystem, FFmpeg, and model workers outside them.
  6. Add ArchUnit or Spring Modulith verification only when its rule corresponds to the documented dependency direction.
  7. Run narrow tests, full tests, clean-checkout verification, and artifact contract tests.
  8. Remove compatibility code only after persisted-project and API migration evidence is approved.

For macOS development, Linux/VPS production, and cloud deployment, use the same platform-neutral application artifact and contract versions. Build signed platform-specific runtime/model/image bundles, then promote each bundle without rebuild for the same OS/CPU target. Permit environment-specific adapters/configuration, not environment-specific domain behavior.

Record every major decision

For every module split, new dependency, persistence change, state change, model/runtime choice, or contract version, record:

  1. Requirement being addressed.
  2. Selected approach.
  3. Alternatives considered.
  4. Benefits and trade-offs.
  5. Operational consequences.
  6. Security implications.
  7. Automated and manual verification.
  8. Revisit conditions.

Label the target as proposed, accepted, implemented, or verified. Never call it production-ready until clean-checkout build, security, observability, recovery, load, media-quality, and operational gates all pass.

Review checklist

  • Name the affected workflow and aggregate.
  • List every changed API, JSON, file, state, metric, and approval contract.
  • Show dependency direction before and after.
  • Keep domain behavior testable without Spring.
  • Add a port only where a real adapter boundary exists.
  • Prove restart and retry behavior at the changed durable boundary.
  • Prove path containment and tenant/project isolation.
  • Prove no startup/job-time downloads or external network calls occur.
  • Prove all assets and models are pre-provisioned, hashed, and licensed.
  • Prove approval binds source, plan, configuration, model, and asset digests.
  • Prove QA from measurements; reject placeholder audio and degraded render fallback.
  • Run the relevant workflow tests plus the full change-control gates.

Provenance and maintenance

Re-verify the package inventory: find src/main/java/org/example/videoclips -type d | sort

Re-verify application wiring and schedulers: rg -n '@SpringBootApplication|@EnableAsync|@EnableScheduling|@Scheduled|@ConditionalOn' src/main/java

Re-verify existing ports: rg -n '^public interface ' src/main/java/org/example/videoclips/{application,processing,queue,storage,editing}

Re-verify outward application imports: rg -n '^import org\.example\.videoclips\.(api|persistence|storage|queue|processing)' src/main/java/org/example/videoclips/{domain,application}

Re-verify statuses: for f in src/main/java/org/example/videoclips/domain/ClipJobStatus.java src/main/java/org/example/videoclips/editing/{EditProjectStatus,HighlightProjectStatus}.java; do sed -n '1,120p' "$f"; done

Re-verify filesystem writes and render signals: rg -n 'writeJson\(|Files\.(move|copy|write)|final\.mp4|approved\.flag' src/main/java/org/example/videoclips/{folder,editing}

Re-verify highlight artifacts against the declared folder contract: rg -n 'analysis/.*\.json|director/.*\.json|HighlightFolderContract' src/main/java/org/example/videoclips/editing

Re-verify prohibited downloads and fallbacks: rg -n 'pip install|get_pretrained|YOLO\(|write_silence|fallback[_ -]tone|anullsrc|sine=' tools src/main/java src/main/resources

Re-verify production-facing defaults: sed -n '1,180p' src/main/resources/application.yml && sed -n '1,120p' src/main/resources/application.properties

Re-verify architecture enforcement dependencies/tests: rg -n 'archunit|spring-modulith|ApplicationModules|ArchRule' pom.xml src/test || true

Re-run focused contract tests after architecture changes: mvn -o -Dtest=VideoAssetControllerTest,DatabaseBackedClipJobQueueAdapterTest,FolderVideoScanSchedulerTest,EditPlanInboxScannerTest,FileSystemEditProjectStoreTest,FileSystemHighlightProjectStoreTest,HighlightDirectorFlowServiceTest test