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
UPLOADEDorREADYasset. - 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
UPLOADEDafter a failed or cancelled processing attempt. - Keep DB-queue retry ownership in
DatabaseBackedClipJobQueueAdapter; do not persist terminal failure fromClipProcessorbefore 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:
- Do not download dependencies or models automatically at startup, test time, or job time.
- 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.
- 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.
- Do not ingest or generate assets without recorded licensing and provenance.
- Do not substitute silence, sine tones, or placeholder media for missing voiceover, music, or SFX.
- Do not render without explicit approval bound to the exact plan and source digest.
- Do not change production-facing defaults without change-control approval, compatibility analysis, and rollback evidence.
Current disqualifying gaps include:
tools/run_local_asset_worker.shandtools/run_local_cv_worker.shinstall Python dependencies inautomode; the CV script can load/download YOLO weights, and AudioCraftget_pretrainedcan 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.ymlenables 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}:renderinvokes the renderer without an approval check.HighlightFfmpegRenderercan copy video forward after audio mixing fails, and several QA checks are hard-codedtruerather 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
- Characterize the current API, JSON, file names, state changes, and restart behavior with tests.
- Write an ADR and route it through
video-editing-change-control. - Define the destination capability and its public application API.
- Move domain types without changing behavior; keep compatibility mapping at the old boundary.
- Move outbound interfaces inward, then adapt JPA, filesystem, FFmpeg, and model workers outside them.
- Add ArchUnit or Spring Modulith verification only when its rule corresponds to the documented dependency direction.
- Run narrow tests, full tests, clean-checkout verification, and artifact contract tests.
- 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:
- Requirement being addressed.
- Selected approach.
- Alternatives considered.
- Benefits and trade-offs.
- Operational consequences.
- Security implications.
- Automated and manual verification.
- 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