Skip to content

ci(deps): bump actions/upload-artifact from 4 to 7#4

Open
dependabot[bot] wants to merge 192 commits into
masterfrom
dependabot/github_actions/actions/upload-artifact-7
Open

ci(deps): bump actions/upload-artifact from 4 to 7#4
dependabot[bot] wants to merge 192 commits into
masterfrom
dependabot/github_actions/actions/upload-artifact-7

Conversation

@dependabot
Copy link
Copy Markdown

@dependabot dependabot Bot commented on behalf of github May 17, 2026

Bumps actions/upload-artifact from 4 to 7.

Release notes

Sourced from actions/upload-artifact's releases.

v7.0.0

v7 What's new

Direct Uploads

Adds support for uploading single files directly (unzipped). Callers can set the new archive parameter to false to skip zipping the file during upload. Right now, we only support single files. The action will fail if the glob passed resolves to multiple files. The name parameter is also ignored with this setting. Instead, the name of the artifact will be the name of the uploaded file.

ESM

To support new versions of the @actions/* packages, we've upgraded the package to ESM.

What's Changed

New Contributors

Full Changelog: actions/upload-artifact@v6...v7.0.0

v6.0.0

v6 - What's new

[!IMPORTANT] actions/upload-artifact@v6 now runs on Node.js 24 (runs.using: node24) and requires a minimum Actions Runner version of 2.327.1. If you are using self-hosted runners, ensure they are updated before upgrading.

Node.js 24

This release updates the runtime to Node.js 24. v5 had preliminary support for Node.js 24, however this action was by default still running on Node.js 20. Now this action by default will run on Node.js 24.

What's Changed

Full Changelog: actions/upload-artifact@v5.0.0...v6.0.0

v5.0.0

What's Changed

BREAKING CHANGE: this update supports Node v24.x. This is not a breaking change per-se but we're treating it as such.

... (truncated)

Commits
  • 043fb46 Merge pull request #797 from actions/yacaovsnc/update-dependency
  • 634250c Include changes in typespec/ts-http-runtime 0.3.5
  • e454baa Readme: bump all the example versions to v7 (#796)
  • 74fad66 Update the readme with direct upload details (#795)
  • bbbca2d Support direct file uploads (#764)
  • 589182c Upgrade the module to ESM and bump dependencies (#762)
  • 47309c9 Merge pull request #754 from actions/Link-/add-proxy-integration-tests
  • 02a8460 Add proxy integration test
  • b7c566a Merge pull request #745 from actions/upload-artifact-v6-release
  • e516bc8 docs: correct description of Node.js 24 support in README
  • Additional commits viewable in compare view

Dependabot compatibility score

Dependabot will resolve any conflicts with this PR as long as you don't alter it yourself. You can also trigger a rebase manually by commenting @dependabot rebase.


Dependabot commands and options

You can trigger Dependabot actions by commenting on this PR:

  • @dependabot rebase will rebase this PR
  • @dependabot recreate will recreate this PR, overwriting any edits that have been made to it
  • @dependabot show <dependency name> ignore conditions will show all of the ignore conditions of the specified dependency
  • @dependabot ignore this major version will close this PR and stop Dependabot creating any more for this major version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this minor version will close this PR and stop Dependabot creating any more for this minor version (unless you reopen the PR or upgrade to it yourself)
  • @dependabot ignore this dependency will close this PR and stop Dependabot creating any more for this dependency (unless you reopen the PR or upgrade to it yourself)

SysAdminDoc and others added 30 commits March 26, 2026 11:31
- Extract shared StateFlowExt.kt (deduplicate CAS-loop from 7 delegates)
- Fix shadowed `it` in removeBatchExportItem and deleteTimelineMarker
- Fix ExportService lifecycle (stop on completion/error/exception)
- Remove artificial delay(1000) from face tracking
- CloudInpaintingEngine: config persistence, validation, dynamic isAvailable()
- FFmpegEngine: reflective invocation, concat, speed change with atempo chain
- NoiseReductionEngine: runtime ML detection, cascading fallback
- PiperTtsEngine: Android system TTS fallback via synthesizeToFile()
- AudioEngine: LRU waveform cache (64 entries) to avoid redundant decoding
- Timeline: haptic feedback on trim handle grab and magnetic snap
- ToolPanel: unique Material icons per transition type with selection border
- ClipEditingDelegate: reorderClip() and moveClipToTrack() methods
- EditorViewModel: wire new delegate methods, clear waveform cache on cleared

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix export state permanently stuck at EXPORTING, selection state leak,
ExportService lifecycle. Optimize Timeline by replacing pointerInput key
recreation with rememberUpdatedState. Add delete confirmation dialog,
buffering indicator, AI error handling, VideoEngine deduplication.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract 90+ hardcoded UI strings to strings.xml across 15+ panels
for localization readiness. Replace getPixel() per-pixel loops with
batch getPixels() in AiFeatures for ~10x faster bitmap analysis.
Fix bitmap leak in calculateFrameDifference() with try-finally.
Add Log.w to silent catches in Whisper, Segmentation, ProjectAutoSave.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…bs with direct APIs

Wire 6 engines to real dependencies: PiperTtsEngine (Sherpa-ONNX OfflineTts),
NoiseReductionEngine (DeepFilterNet), FFmpegEngine (FFmpegX with EBU R128),
FrameInterpolationEngine (NCNN RIFE v4.6), InpaintingEngine (ONNX Runtime LaMa),
CloudInpaintingEngine (OkHttp multipart). Add ProGuard keep rules for all new
JNI/native bridges.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add v3.0.0-v3.4.0 version history to CLAUDE.md with engine details and
architectural decisions. Update README changelog, activate dependency table,
add known limitations, update architecture tree with wired engine details,
remove outdated "Activating Optional Dependencies" section.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… fixes

Remove 5 unresolvable Maven dependencies (sherpa-onnx, deepfilternet, ncnn,
ffmpegx, opentimelineio) that don't exist on Maven Central. Convert direct
API calls in PiperTtsEngine, NoiseReductionEngine, FFmpegEngine, and
FrameInterpolationEngine back to reflection-based invocation with runtime
fallbacks. Fix compile errors: duplicate showLutPicker property, missing
Mocha. prefix on colors in ChapterMarkerPanel/TransformOverlay, invalid
OutlinedTextFieldDefaults params in SnapshotHistoryPanel, @composable calls
outside composable context in BatchExportPanel/ProjectListScreen. Regenerate
signing keystore. Version bump to v3.5.0.
…on, panel abstraction

Phase 1: Version sync to 3.5.0, remove 5 dead ProGuard keep-rule blocks
Phase 2: Documentation truthfulness — un-check 14 stub ROADMAP items, correct
  README feature claims, mark stub engines as "Planned" in tech stack
Phase 3: Clean 11 stub engines (~2,000 lines removed) — remove fabricated Maven
  coords, fake HuggingFace URLs, Class.forName reflection, fake download/inference.
  Fix NoiseReduction empty-file bug, PiperTts dead Sherpa path, SoundpipeDsp
  Moog filter coefficient and reverb naming
Phase 4: Remove duplicate FCPXML export from EdlExporter (~110 lines, zero callers)
Phase 5: Decompose EditorState — replace 40 boolean panel flags with PanelVisibility
  class using Set<PanelId>. EditorState reduced from ~96 to ~57 fields
Phase 6: Create BottomSheetSlot composable, replace 40 AnimatedVisibility blocks.
  Remove dead CloudBackupPanel from EditorScreen. ~89 lines saved
Phase 7: Replace O(n*k) brute-force DFT with radix-2 Cooley-Tukey FFT in
  BeatDetectionEngine (~12x faster, 513 bins vs 64)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…g fixes

- Split Project.kt (1,081 lines, 70 symbols) into 9 focused model files:
  Effect.kt, ExportConfig.kt, Timeline.kt, Overlay.kt, Caption.kt,
  ColorGrading.kt, Mask.kt, EditorModels.kt (Project.kt now 230 lines)
- Add @immutable to 16 model data classes (Project, Track, Clip, Effect,
  Transition, Keyframe, TextOverlay, etc.)
- Add @stable to EditorState, @immutable to PanelVisibility
- Add derivedStateOf for hasOpenPanel, hasClips, isClipMode in EditorScreen
- Change waveforms from Map<String, FloatArray> to Map<String, List<Float>>
  for Compose structural equality
- Remove dead TtsPanel Piper voice selector (empty click handlers)
- Move hardcoded hilt-work deps to version catalog
- Pin all dependency versions (no more floating versions)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Extract 3 files from VideoEngine (1,439 → 672 lines, 53% reduction):
- EffectBuilder.kt — stateless effect mapping (NovaCut model → Media3)
- VolumeAudioProcessor.kt — audio fade/volume/keyframe processor
- ExportTextOverlay.kt — text overlay animation state machine

Split Project.kt (1,081 lines) into 9 focused model files:
- Effect.kt, ExportConfig.kt, Timeline.kt, Overlay.kt, Caption.kt,
  ColorGrading.kt, Mask.kt, EditorModels.kt (Project.kt now 230 lines)

Compose stability improvements:
- @immutable on 16 model data classes, @stable on EditorState
- derivedStateOf for hasOpenPanel/hasClips/isClipMode in EditorScreen
- Waveforms Map<String, List<Float>> for structural equality

Fix ProxyWorkflowEngine missing flow.update import and type inference.
Fix EditorViewModel showVoiceoverRecorder → PanelId pattern.
Remove dead TtsPanel Piper voice selector. Pin all dependency versions.
…lVisibility

@stable on EditorState told Compose to aggressively skip recomposition,
combined with derivedStateOf caching stale state snapshots. Buttons and
panels appeared correct visually but clicks had no effect because Compose
was not recomposing when state.panels changed.

- Remove @stable from EditorState
- Remove @immutable from PanelVisibility
- Replace derivedStateOf with direct property reads for hasOpenPanel,
  isClipMode, selectedClip, allCaptions, hasClips
- Remove unused Stable/Immutable imports from EditorViewModel
…wiring

Bug fixes:
- normalizeAudio() persistence: undo after async, saveProject() added
- setClipLut() null guard + saveUndoState before mutation
- setTransitionDuration() missing updatePreview() call
- StateFlowExt CAS loop 100-retry safety limit
- AI tool clip re-validation in coroutine (prevents NPE on deleted clip)
- prepareTimeline() filters invisible tracks for preview
- Audio effect add/remove now persisted via saveProject()
- MaskPoint handles serialized (was only x,y — lost handleIn/Out)
- Keyframe + effect keyframe deduplication on deserialization
- Orphaned linkedClipId validated + nullified after deserialization

New features:
- Export: elapsed time + ETA display during export
- Export: estimated file size from bitrate * duration
- Export: codec availability filtering (greyed-out unsupported codecs)
- Export: audio codec selector (AAC/Opus/FLAC)
- Export: chapter markers toggle
- Settings: Editor Mode (Easy/Pro) + Haptic Feedback toggles

Architecture:
- runAiTool() + cancelAiTool() now route through AiToolsDelegate
- ViewModel AI methods thin-delegate (~200 lines removed)
- Settings "Manage" model links wired (was TODO stub)
- Timeline touch targets 24dp -> 36dp (Material 3 compliance)
- AiToolsPanel download icons: contentDescription added
- Remove 348 lines of dead AI code from EditorViewModel (17 methods
  orphaned after delegate wiring: aiSceneDetect, aiAutoCaptions,
  aiAutoColor, aiStabilize, aiDenoise, aiRemoveOrReplaceBg,
  aiTrackMotion, aiStyleTransfer, aiUpscale, applyFrameInterpolation,
  applyObjectRemoval, applyVideoUpscale, applyAiBackground,
  applyStabilization, applyStyleTransfer, updateClipByIdWithDuration)
- Remove orphaned aiJob field, use delegate cancelAiTool in onCleared
- Wire CloudBackupPanel in EditorScreen (was defined but never rendered)
- Wire ExportProgressOverlay as floating card during background export
- Add saveUndoState + saveProject to chapter marker add/update/delete
- Add chapterMarkers field to AutoSaveState data class
- Serialize chapter markers (timeMs + title) in auto-save JSON
- Deserialize chapters with null-safe fallback on load
- Pass chapterMarkers in both auto-save paths (periodic + snapshot)
- Restore chapterMarkers from auto-save recovery and snapshot restore
- Add saveProject() to restoreSnapshot for persistence
- Apply clip anchorX/Y in export transform pipeline (was ignored)
  Pre-translate by -anchor, scale/rotate, post-translate by +anchor
  Both keyframed and static transform paths updated
- OverlayDelegate: add saveProject callback
  Text overlay add/update/remove now call saveProject()
  Image overlay add/remove now call saveProject()
  Timeline marker add/delete now call saveUndoState() + saveProject()
- Add saveProject callback to ColorGradingDelegate constructor
- Save project on hideColorGrading (persists adjustments on panel close)
- Save project after LUT application
- Wire saveProject in ViewModel delegate instantiation
Critical persistence fixes in ClipEditingDelegate:
- Add endTrim(): disables scrub mode, rebuilds timeline, saves project
- Add endSpeedChange(): rebuilds timeline, saves project
- reorderClip(): add saveProject after state update
- moveClipToTrack(): add saveProject after state update
- setClipReversed(): add saveProject after state update

UI wiring:
- Timeline: add onTrimDragEnded callback, fire on drag end + cancel
- ToolPanel: add onDragEnded to EffectSlider, fire on slider release
- SpeedPanel: add onSpeedDragEnded, wire to EffectSlider
- EditorScreen: wire endTrim + endSpeedChange to ViewModel
- Add init{require()} blocks to Transition, Track, Clip, Caption, Mask, ImageOverlay
- Delete 5 unused engines (CloudInpainting, PiperTts, Rive, Soundpipe, SubtitleRender)
- Add Mutex to ProxyWorkflowEngine.generateAllProxies() preventing duplicate runs
- Extract LottieOverlaySpec to own file
- Update README with v3.6.0 changelog
- Fix createCompoundClip() crash: set sourceDurationMs = compoundDurationMs
- Fix imageOverlays/timelineMarkers not serialized in auto-save (data loss)
- Fix setTransitionDuration crash on clips < 200ms (coerceIn range inversion)
- Track mute/visibility/lock/opacity now persist via saveProject()
- setClipOpacity now persists via saveProject()
- autoDuck() boxing: ShortArray constructor instead of map+toShortArray
- Snapshot create/restore includes imageOverlays + timelineMarkers
- Harden Clip deserialization: coerce trimEndMs/trimStartMs/sourceDurationMs
  before construction to prevent require() crash on legacy auto-save data
- TextOverlay deserialization returns null for empty text instead of crashing
  on require(text.isNotEmpty())
- Batch export resets videoEngine state between items to prevent race
  condition where previous COMPLETE state blocks next item's progress tracking
- TTS clips now get waveform extracted (was showing flat in timeline)
- setClipVolume now persists via saveProject()
- setClipFadeIn/FadeOut now persist via saveProject()
- setClipTransform now persists via saveProject()
- Remove duplicate ExportProgressOverlay composable (was rendering two
  overlapping floating cards during export)
- Captions action in project-mode text menu now shows toast when no clip
  selected instead of opening an empty non-functional panel
- WhisperEngine, SegmentationEngine, InpaintingEngine all used bare
  File.renameTo() for atomic temp-file writes during model downloads.
  renameTo() silently returns false on some Android filesystems (e.g.
  cross-partition). Added copy+delete fallback matching ProjectAutoSave
  pattern. Without this fix, model downloads could appear to succeed
  while the model file doesn't actually exist.
Competitive research across CapCut, VN, KineMaster, PowerDirector,
DaVinci Resolve iPad, and FOSS landscape. Implemented 14 features:

Speed & Export:
- Speed range extended from 16x to 100x (matching CapCut)
- Transparent video export via WebM VP9 alpha channel (from KineMaster)

AI & Automation:
- Filler word auto-strip from caption text (from CapCut)
- Contextual AI suggestion banner above timeline (from CapCut)
- Script-to-video: text input for smarter auto-edit clip selection
- Auto-generated effect parameter UI from metadata (from Pitivi pattern)

Editing Tools:
- Manual beat tap mode during playback (from VN)
- Pinch-to-transform gestures on preview (from CapCut)
- Drawing/annotation overlay with color picker + brush (from KineMaster)
- Radial quick-action menu on long-press (from KineMaster Media Wheel)
- Multi-cam angle switching panel with 2x2 grid (from PowerDirector)

Project Management:
- Community template export/import (.novacut-template files)
- Project backup panel replacing cloud stub (export/import archives)
- F-Droid fastlane metadata structure

New files: AiSuggestionBanner, DrawingOverlayPanel, MultiCamPanel,
RadialActionMenu, fastlane/metadata/
- versionCode 67 -> 68, versionName 3.7.0 -> 3.8.0
- Synced across build.gradle.kts, NovaCutApp.VERSION, strings.xml
- Added v3.8.0 section to .claude/CLAUDE.md with all 14 new features
- Updated CLAUDE.md version reference
- Updated novacut.md memory file with v3.8.0 features and new file list
- Updated MEMORY.md index entry
- Transparent export now uses .webm extension (was .mp4 with VP9 codec)
- drawingPaths serialized in auto-save (was lost on crash/recovery)
- dismissedPanelState resets isDrawingMode (drawing mode leaked across panels)
- Radial menu uses proper show*() methods instead of raw showPanel()
  (was bypassing pauseIfPlaying and editingTextOverlayId reset)
- cleanCaptionText "like" regex narrowed to filler context only
  (was stripping all "like" including "I like dogs")
- Fix drawingColor default: 0xFFF38BA8 -> 0xFFF38BA8L (Int sign-extended
  to wrong Long value, default color never matched palette selection)
- Add drawingPaths to UndoAction + saveUndoState/undo/redo/jumpToUndoState
  (drawing annotations were not preserved across undo/redo operations)
- Fix SPEED ParamRange max: 16f -> 100f (was inconsistent with the 100x
  speed limit applied everywhere else in v3.8.0)
Three root causes for jerky timeline movement and viewport mismatch:

1. Auto-scroll during playback jumped instantly when playhead crossed
   80% threshold (scrollOffsetMs snapped from current to target). Now
   uses smooth lerp interpolation (15% per frame toward target) for
   fluid following behavior.

2. Scroll offset only updated every 5th frame (165ms) during playback
   while playhead updated every frame (33ms). Playhead drifted relative
   to clips/ruler between state updates. Now updates scroll immediately
   when auto-scroll produces a new value.

3. Pinch-to-zoom on timeline used old pixelsPerMs for pan calculation
   after zoom changed. Viewport shifted unexpectedly during pinch
   gestures. Now adjusts scroll offset to keep the pinch center point
   stable using the new zoom level for both center calculation and
   pan delta.

Fixes #1
GIF export with frame rate/width config, frame capture (PNG/JPEG),
subtitle export (SRT/VTT/ASS), stems + chapter markers toggles.
7 new AppSettings (waveforms, snap, track height, cache, export quality).
MarkerListPanel with search/filter/edit. Track header fields (waveform,
height, collapsed) with auto-save. Snap-to-beat/marker in timeline.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Wire track collapse/expand, per-track height, and waveform toggle into
Timeline UI. Connect confirmBeforeDelete, defaultExportQuality, and
showWaveforms settings into editor behavior. Auto-populate chapter
markers from timeline markers on export. Add ClipLabel enum with
colored top border on clips. External keyboard shortcuts (Space,
Ctrl+Z, arrows, M, S, +/-, Delete, Ctrl+S, Ctrl+C/V).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
MavenImaging and others added 28 commits May 16, 2026 15:23
R6.4 — Meta SAM 3 (Nov 2025) and SAM 3.1 (Mar 2026) introduce text-prompted
concept segmentation and video object multiplexing, but the 848M-parameter
model targets H100-class GPUs and no mobile-viable ONNX export has shipped
as of 2026-05. NovaCut should stay on SAM 2.1 Hiera Tiny as the default
tracked-mask target, but close the API contract today so the eventual
upgrade is a flag flip plus a single inference branch.

Code changes:

- TapSegmentEngine.ModelFamily gains SAM3 with a docstring linking the
  upstream repo and explaining the placeholder status.
- TapSegmentEngine.ModelVariant gains SAM3_HIERA_TINY_ONNX_PLACEHOLDER with
  placeholder size estimates (240 MB model + 128 MB state cache, 8 GB RAM
  floor) derived from SAM 2.1 Hiera Tiny working-set characteristics.
  Estimates are placeholders — fill in real numbers when the export ships.
- SAM3_PLACEHOLDER_ENABLED feature flag defaults to false. The
  recommendedModelForDevice() policy keeps the SAM 2.1 Hiera Tiny default
  regardless of caller flags until SAM3_PLACEHOLDER_ENABLED is flipped on.
- New SAM3_SOURCE_URL constant locks the watch-item URL at compile time.
- New segmentByTextPrompt(bitmap, textPrompt) suspend method returns null
  today with an explicit "unavailable" log. When SAM 3 ships an ONNX
  export, only the SAM3 branch of the implementation needs to land;
  callers and UI integrations don't change.

Tests (2 new in TapSegmentEngineTest):

- SAM 3 placeholder enum exists with correct family, premium-tier flag,
  and supportsVideoPropagation; the recommender stays on SAM 2.1 even at
  16 GB RAM + premium-models allowed while the flag is off.
- SAM3_SOURCE_URL pinned to facebookresearch/sam3 so a regression is
  caught immediately.

Marks ROADMAP R6.4a and R6.4b as done.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
R6.21 audit found that the audio picker launcher used a narrow
arrayOf("audio/*") MIME filter. Some Android system pickers (particularly
older Storage Access Framework providers and certain third-party file
managers) still label .opus files with the legacy application/ogg
container MIME rather than audio/opus or audio/ogg, so those files were
invisible in the audio picker even though:

- The resolver-side MIME check at line 142 already accepts both audio/*
  and application/ogg for the same reason.
- The extension probe at line 538 already routes .opus to "audio".

The fix is a one-line expansion of the launcher MIME filter. Opus files
that were already importable via the OpenDocument fallback are now
discoverable in the modern Photo Picker path as well.

Marks Opus audio support (#35 in Open Video Editor's enhancement issue
list) as fully covered in the R6.21 competitive-gap analysis.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
R6.10a wants to swap the custom LottieOverlayEffect for the official
androidx.media3:media3-effect-lottie module shipped in Media3 1.10
(which NovaCut already pulls). Before the swap can happen, three
feature-parity gaps need to be verified against the official module:

1. Time-windowed overlay alpha — this class uses overlayStartUs and
   overlayDurationUs to gate the alpha to zero outside the window. The
   Media3 effect chain typically expects an OverlaySettings per frame; the
   1.10.x surface for per-frame alpha control needs an audit.
2. Lottie TextDelegate text substitution — this class uses
   textReplacements: Map<String, String> for caption / lower-third
   templates. If the official module renders only a static composition,
   NovaCut must keep this engine for text-driven templates.
3. HDR-aware sampling — this class accepts useHdr from the GlEffect
   contract.

Recording the gaps in the engine docstring rather than blindly swapping
preserves the working text-template export path while signalling to a
future migrator exactly what to verify.

Marks ROADMAP R6.10a as in-progress.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
AudioMasteringEngine already shipped the 5 preset recipes (Podcast Voice,
Music Master, Dialogue Clean, ASMR, Social Loud) but they were defined
data with no caller. This commit closes the gap by wiring the presets all
the way through to a single applyMasteringPreset() call on the audio mixer.

Engine:
- New buildEffectChain(preset) converts a MasteringChain into the ordered
  AudioEffect list the track-level pipeline expects:
  HighPass → ParametricEQ → De-esser → Compressor → Limiter.
- Skips HighPass / EQ / De-esser slots when the preset has no value for
  them, so a minimal preset still produces a valid chain.
- PARAMETRIC_EQ is mapped 5 slots <- min(preset.eqBands, 5); unused slots
  zero-gained so the EQ contributes nothing for those bands.
- De-esser threshold scales with amount: 0 -> -10 dB, 1 -> -30 dB.
- noiseReductionMode is intentionally NOT translated into the per-track
  audio effect chain. NR lives at the clip pre-process stage via
  NoiseReductionEngine and is keyed off the preset's mode separately
  (lines up with R6.6 DeepFilterNet 3 plan).

Delegate:
- New constructor parameter audioMasteringEngine on AudioMixerDelegate.
- New methods getMasteringPresets() and applyMasteringPreset(trackId,
  presetId). The apply path:
    * Rejects unknown preset ids and missing tracks with a toast.
    * Rejects non-audio non-video tracks (text/overlay tracks have no
      audio chain to replace).
    * Replaces the track's audioEffects list in one saveUndoState /
      refreshPreview / saveProject pass so undo restores the prior chain.

EditorViewModel:
- New @Inject constructor parameter audioMasteringEngine forwarded to
  the delegate. Hilt auto-provides the singleton engine — no module change.

Tests (6 new in AudioMasteringEngineTest):
- podcast_voice emits all 5 stages in canonical order.
- A preset with no HP/EQ/deEsser produces Compressor + Limiter only.
- EQ slot zero-fill: 3-band podcast_voice fills all 5 slots, slots 4-5
  have gain 0.
- De-esser amount scales threshold (0.5 -> -20 dB).
- Limiter ceiling tracks preset.truePeakDb.
- Compressor params round-trip from preset to effect params.

Marks ROADMAP C.6 as done.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…(R5.4c)

R5.4c audit findings (2026-05-16):

The Round 5 roadmap claim that "engine stubs (UpscaleEngine,
StyleTransferEngine, etc.) emit user-facing copy via Log.d /
Toast.makeText with literal strings" turned out to be incorrect for the
current codebase. A grep for Toast.makeText / Snackbar.make across the
entire engine package returned zero hits.

The only English-only user-facing surfaces in engines today are
structured diagnostic message fields on result records:

  - ProjectArchive.errorMessage           (4 strings)
  - TemplateCompatibility.message         (3 strings)
  - TimelineExchangeValidator.message    (26 strings)

These reach the user through the dialog/report rendering path, not via
toasts, and carry their own routing story. Localizing them is a separate
workstream and not in scope for this audit pass.

This commit:
- Adds EngineStringExtractionAuditTest, which walks
  app/src/main/java/com/novacut/editor/engine and fails the build if any
  engine ever calls Toast.makeText or Snackbar.make directly. The lane
  stays closed for the future.
- Tracks the engine source file count (102 at audit time, with a tolerance
  band 95–130) so a future commit that adds a new engine prompts the
  author to update docs/models.md.
- Marks ROADMAP R5.4c as done with a full audit summary.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
… layer)

R5.5d wants a "Save diagnostic ZIP" action in Settings so users can attach
a triage bundle to a GitHub issue without NovaCut ever shipping a
telemetry pipe. This commit lands the engine layer; the ~10-line Settings
UI wiring (FileProvider + ACTION_SEND) is a separate follow-up commit.

DiagnosticExportEngine writes a ZIP under filesDir/diagnostics/ with six
entries:

  app-info.txt       — applicationId, versionName, versionCode, targetSdk
  device-info.txt    — manufacturer, brand, model, ABIs, fingerprint
  media-codecs.txt   — MediaCodecList.REGULAR_CODECS summary for triage of
                       "AV1 export fails on my device" tickets
  model-registry.txt — registered models from ModelDownloadManager:
                       id + installed flag + size + source URL only;
                       no file contents are included
  logcat-tail.txt    — last 200 logcat lines from the current process,
                       with PII redacted before write
  manifest.txt       — entry list with sizes

Privacy guarantees baked into the API contract:

  - Engine never opens a network connection.
  - Never includes project JSON, media URIs, autosave snapshots, user
    content, or captions/transcripts.
  - All redaction patterns (content://, file://, /storage/, /data/data/,
    URLs with query strings, email addresses) run BEFORE write — the
    written bytes never carry the raw substring.
  - Self-prunes past 3 ZIPs to keep on-device footprint bounded.

Tests (8 in DiagnosticExportEngineTest):

  - Redacts content://, file://, /storage/, /data/data/ paths.
  - Redacts URLs that include query strings; leaves plain URLs intact.
  - Redacts email addresses.
  - Leaves unsensitive lines untouched.
  - Verifies the bundle entry-name contract.
  - Verifies ModelSnapshot value-object semantics.

Marks ROADMAP R5.5d as in-progress (engine + tests shipped; Settings UI
wiring follows in a separate commit).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
B.5 wants the export path to stop falling back to a full Transformer pass
whenever any segment needs re-encoding. The plan is to group consecutive
same-flag segments into runs, export each run with the right engine
(StreamCopy for pass-through, Transformer/FFmpeg for re-encode), and
concatenate the outputs.

This commit ships the run-planner scaffold:

- SmartRenderEngine.RenderRun data class: startMs, endMs, needsReEncode,
  clipIds. Includes a durationMs convenience.
- SmartRenderEngine.planRuns(segments): groups ordered per-clip segments
  into runs. Two consecutive segments share a run when they have the same
  needsReEncode flag AND no timeline gap between them. A gap forces a new
  run even when the flags match because gap-bridging needs a re-encode
  pass and is the composer's job to handle.

Tests (8 in SmartRenderEngineRunTest):
- Empty input → empty runs.
- Single segment → one run, durationMs correct.
- Three contiguous pass-through clips → 1 run.
- pass-through / re-encode / pass-through → 3 runs.
- Two pass-through clips with a gap → 2 runs (gap-break rule).
- Out-of-order input is sorted first.
- Sum of run durations equals sum of input durations.
- Alternating flags produce four single-clip runs.

The composer step that consumes RenderRun and concatenates outputs lives
in a follow-up — it depends on R6.5 (ffmpeg-kit-16kb pin) for the concat
demuxer. ROADMAP B.5 is now in-progress with this scaffold; the composer
is the only remaining piece.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summarizes the Round 6 Now-tier work landed in this session:
R6.1 (16 KB CI gate), R6.1c/R5.6b (docs/models.md), R6.2a (NNAPI cleanup),
R6.4a/b (SAM 3 placeholder), R6.5a (ffmpeg-kit-16kb activation path),
R6.6a (DeepFilterNet 3 target), R6.8a/b (Whisper Turbo three-target),
R6.10a (Lottie module migration plan), R6.21 (Opus picker fix),
C.6 (audio mastering presets wired end-to-end), B.5 (run planner
scaffold), R5.4c (strings audit + regression guard), R5.5d (diagnostic
export engine).

35+ new JVM unit tests added. Gradle test run not executed in this
environment (no JDK / JAVA_HOME).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
A.10 wants Google Oboe's sinc-based resampler for 44.1<->48 kHz mixing
to replace the lossy Media3 fallback. Activation needs the
com.google.oboe:oboe:1.9.0 Maven coord wired plus a JNI bridge for
MultiChannelResampler.

This commit ships the scaffold so the rest of the audio path can
integrate today without waiting for the dep:

- Rewrite OboeResamplerEngine docstring with the four-step activation
  path (catalog entry, build.gradle line, MultiChannelResampler bridge,
  isAvailable reflection probe).
- isAvailable() now does a reflection probe against
  com.google.oboe.MultiChannelResampler with a cached result. Audio
  callers can branch on the gate without an explicit feature flag.
- resample() returns null when stubbed (unchanged behavior), but logs
  the requested rates + quality for triage. Will become the real call
  once the dep lands.
- New estimatedOutputFrames(inputFrames, fromHz, toHz) pure-math helper
  that audio mix sizing code can rely on TODAY. Uses Long math so 8-hour
  buffers at 48 kHz don't overflow when scaled to 44.1 kHz.
- TARGET_OBOE_VERSION / TARGET_MAVEN_GROUP / TARGET_MAVEN_NAME constants
  surface the pinned activation target.

Tests (8 in OboeResamplerEngineTest):
- isAvailable returns false when dep not on classpath.
- resample returns null when stubbed.
- Metadata constants pinned.
- Same-rate identity, upsample 44.1->48 round-up, downsample 48->44.1
  round-up, zero / negative input returns zero, zero rate throws,
  8-hour buffer doesn't overflow Long.

Updates ROADMAP A.10 to in-progress and adds the Oboe row to
docs/models.md §2 (16 KB alignment must be re-verified per release).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…6.16

Sweeps the remaining Tier A engine stubs to the same activation-path
docstring pattern that R6.5 (FFmpegEngine), R6.6 (NoiseReductionEngine),
R6.8 (SherpaAsrEngine), and A.10 (OboeResamplerEngine) now follow. Each
engine's class doc now records: target Maven coord or source URL, license,
SHA-256 prerequisite, 16 KB / ABI-split obligations, and the line-by-line
replacement for the stub method bodies.

Engines updated:

- FrameInterpolationEngine (A.4) -- RIFE v4.6 via NCNN + Vulkan with the
  zero-copy AHardwareBuffer pipeline, NDK r28+ build for 16 KB alignment,
  tile-cap-256 rule for sub-6-GB devices, single-compute-queue note for
  Adreno. New TARGET_* / LOW_VRAM_RAM_MB constants.
- UpscaleEngine (A.5) -- Real-ESRGAN x4plus + x4v3 via ONNX Runtime,
  tile-and-blend at 256x256, default CPU EP per R6.2. New constants for
  filenames, byte counts, default tile size + overlap.
- VideoMattingEngine (A.6) -- RobustVideoMatting MobileNet (15 MB FP32 /
  5 MB int8) via ONNX Runtime, recurrent-state pattern through frames,
  preview vs export downsample ratio constants.
- StabilizationEngine (A.3) -- OpenCV 4.10 LK optical flow + Kalman +
  MatrixTransformation export warp. R6.9 directive: prefer importing a
  sibling .gyroflow JSON via MediaImportEngine before reimplementing gyro
  math from scratch. OpenCV ABI-split obligation called out.
- StyleTransferEngine (A.11) -- AnimeGANv2 + Fast Neural Style Transfer
  via ONNX Runtime, per-style opt-in download, source URL constants,
  AnimeGAN model variant license-audit note.
- RiveTemplateEngine (A.13) -- Now records that R6.16 (Lottie state
  machines + dotLottie) likely obviates Rive for the interactive-template
  use case. Engine + stub retained for the "we specifically want Rive for
  X" decision, but A.13 is downgraded to Under Consideration in the
  Forward View. Reflection probe added so isAvailable() flips
  automatically when the Rive AAR is added.
- LottieTemplateEngine -- R6.16 path documented: bump to lottie-compose
  7.x, add loadDotLottie() + getStateMachineInputs(), then re-evaluate A.13.

No new dependencies added; no method-body behavior change. All edits are
docs + new public constants. Engines stay stubs until the corresponding
deps are wired through gradle/libs.versions.toml.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
R5.9a — Dependabot config:

  .github/dependabot.yml watches Gradle (libs.versions.toml + AGP plugin)
  and GitHub Actions weekly. Grouped PRs prevent one-PR-per-artifact spam
  on a single-maintainer project. Groups: androidx-media3, androidx-compose,
  androidx-core, hilt, ml (onnxruntime/mediapipe), kotlin, coil. One PR
  per ecosystem per week into master.

R5.9b — Non-bypassable checksum verification:

  ModelDownloadManager already records SHA-256 per model and verifies on
  download. The gap: when a registry entry has no SHA-256 yet (every
  docs/models.md row currently flagged as ⚠ TBD), isValidModelFile()
  silently returned true after the minimum-size check — a missing hash
  meant trust-by-default.

  Changes:
  - isValidModelFile() gains requireChecksum: Boolean = false. When true,
    a null expectedSha256 returns false instead of true. Logs the reason
    so triage is obvious.
  - New verifyChecksumOrDelete(file, minimumBytes, expectedSha256) public
    method is the explicit first-run verification entry point. Wraps
    isValidModelFile with requireChecksum = true.
  - Behavior on mismatch unchanged (delete the corrupt file). Behavior on
    missing hash: do NOT delete — bytes stay so a future SHA-256 fill can
    validate without a re-download.

  Caller migration plan (separate commits): every Tier A engine that
  loads a distribution-critical model gets one verifyChecksumOrDelete()
  call at first-use, after the docs/models.md SHA-256 column is filled
  in for that model.

  4 new tests:
  - requireChecksum + null sha → false (R5.9b strict gate).
  - verifyChecksumOrDelete + missing hash → false, file preserved.
  - verifyChecksumOrDelete + correct hash → true.
  - verifyChecksumOrDelete + mismatch → false, file deleted.

Marks ROADMAP R5.9a + R5.9b as done.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Android 16 ships native APV decoder support; Galaxy S26 Ultra is the
first phone with hardware APV. APV is intra-frame coding designed for
pro post-production with 4:2:2 10-bit at up to 2 Gbps. NovaCut needs to
recognize APV sources on import without ever trying to encode to APV
(output files would be 10-50x larger than HEVC equivalents — not a
sensible default for mobile creators).

EncoderCapabilityProbe gains:
- MIME_APV public constant pinned to "video/apv" (Android 16 value).
- probeApvIngest(): ApvSupport with hasDecoder, isHardwareDecoder, and
  decoderNames. Returns hasDecoder=false on a plain JVM (MediaCodecList
  throws and the catch returns the empty case).
- matchingDecoderEntries(mimeTypes) private helper mirroring the
  existing matchingEncoderEntries walker.

Deliberately omitted (R6.11c): an apvEncoder probe path. APV stays
ingest-only — the lack of an encoder probe codifies that decision.

5 new tests in EncoderCapabilityProbeApvTest cover the MIME constant,
the ApvSupport value-object contract, the isUsable accessor, and the
JVM-empty fallback. On-device behavior (Galaxy S26 Ultra returns
hasDecoder=true) verified via device smoke test not by mock.

Marks ROADMAP R6.11a and R6.11c as done; R6.11b (ExportSheet chip)
remains for the UI commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
….14)

R6.14 wants DaVinci Resolve 20-style multicam auto-cut: bind active
speaker (from Whisper diarization or VAD) to a preferred camera angle,
emit the resulting cut plan, and let the multicam panel apply it with a
single "Auto-switch by speaker" toggle.

This commit ships the planner — pure Kotlin, no Android dependencies, no
I/O. Bindings to the live Whisper / MultiCamEngine output happen in the
delegate layer at a separate commit.

Algorithm:
- Walk speaker turns in timeline order.
- For each turn resolve to an angle:
    explicit `assigned` mapping > round-robin from free pool > modulo wrap.
- Coalesce consecutive turns that resolve to the same angle.
- Drop cuts that would violate minDwellMs (flicker guard, default 800ms).
- Always emit an initial cut at the first turn's start so the plan is
  self-contained and doesn't assume editor state.

API:
- SpeakerTurn(speakerId, startMs, endMs) — validates endMs > startMs.
- Angle(angleIndex, assignedSpeakerId = null) — supports manual mapping.
- SwitchPolicy(minDwellMs = 800, initialAngleIndex = 0).
- plan(turns, angles, policy): CutPlan(cuts, speakerAngleMap).

11 new tests:
- Empty inputs → no cuts (both empty turns and empty angles).
- Single turn → one seed cut.
- A/B/A/B alternation → 4 cuts on 2 angles.
- Consecutive same-speaker → 1 cut, no spam.
- Flicker guard drops sub-dwell switches.
- Explicit angle assignment overrides round-robin.
- More speakers than angles wraps via modulo.
- Out-of-order input is sorted first.
- Policy initialAngleIndex respected.
- SpeakerTurn validation (endMs > startMs).
- SwitchPolicy validation (minDwellMs >= 0).

Marks ROADMAP R6.14a as done; R6.14b (multicam panel UI integration)
remains for the next commit on this item.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…6.17)

R6.17 wants Larix-style live streaming output (RTMP / SRT / WebRTC /
RIST / NDI) as part of the future Live Studio mode (R4.6). The actual
library decision (Stream-Pack vs Larix SDK vs LibSRT-Android) is
deferred until R4.6 lands, but the rest of the system needs a stable
shape to compose against.

New OutputStreamingEngine:
- Protocol enum with 6 entries (RTMP, RTMPS, SRT, RIST, WebRTC, RTSP),
  each carrying its display name + URL scheme.
- OutputDestination value type: id, displayName, protocol, url,
  credentials, target bitrate / fps / resolution.
- StreamState enum: IDLE / CONNECTING / STREAMING / RECONNECTING /
  ENDED / ERROR.
- isAvailable() reflection probe checks for Stream-Pack
  (SingleStreamer), Larix (Streamer), or LibSRT (Srt) on the classpath
  in that priority order. The first hit flips the gate. Cached.
- validateDestination(protocol, url): pure pre-flight URL validation.
  Rejects blank, wrong scheme, whitespace / control chars, scheme-only.
- recommendedBitrateBps(width, height, fps): pure bitrate math from a
  curated 540p / 720p / 1080p / 1440p / 4K base-rate table scaled to fps.
  Hard floor at 500 kbps so we never recommend zero. Aspect-agnostic by
  design (vertical Reels gets the same budget as horizontal at matching
  pixel count).
- start() / stop() are stubs that surface a clean "not yet enabled"
  ERROR state until the library lands.

Activation candidates and their license / control trade-offs are
documented in the class docstring: Stream-Pack (Apache-2.0, OSS),
Larix SDK (proprietary, full protocol matrix), LibSRT + custom RTMP
mux (MPL-2.0, max maintenance). Whichever wins must respect:
- Adaptive bitrate via network probing (R6.17c).
- StateFlow surface so Live Studio shows connection chip.
- Credentials in EncryptedSharedPreferences, never plain DataStore.
- 16 KB alignment gate (R6.1).

11 new tests:
- Protocol enum metadata.
- URL validation (accept conformant, reject blank, wrong scheme,
  control chars, scheme-only).
- Bitrate recommendation: 1080p30 → 3.25 Mbps, 4K60 → 25 Mbps,
  720p30 → 1.75 Mbps, vertical = horizontal at matching pixels, hard
  floor at 500 kbps, invalid args throw.
- isAvailable returns false with no streaming library on classpath.

Marks ROADMAP R6.17a as done. R6.17b (CameraCaptureEngine compose)
waits on R4.6 Live Studio. R6.17c (adaptive bitrate) lands with the
chosen library.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
R6.15 wants DaVinci Resolve 20-style AI Animated Subtitles: animate each
word as it is spoken on top of the existing karaoke caption pipeline.
NovaCut already has Whisper word timestamps + KaraokeCaptionEngine; this
commit lands the math for the per-word emphasis preset library.

New WordEmphasisAnimator (pure Kotlin object):

- Animation enum: NONE / POP / BOUNCE / GLOW / SLIDE_IN, each with a
  matching display name for the caption style gallery.
- WordRenderState(scale, offsetXPx, offsetYPx, alpha, emphasisMix,
  emphasisColor) is the per-word render delta the Canvas overlay
  blends on top of the base CaptionStyleTemplate.
- emphasisFor(animation, wordProgress, baselineFontSizePx, emphasisColor)
  computes the state. wordProgress clamps to 0..1; baseline font size
  scales the BOUNCE and SLIDE_IN deltas so a 60 px caption bounces
  proportionally to a 20 px caption.
- wordProgress(playhead, start, end, windowMs) bridges Whisper word
  timestamps into the 0..1 animation domain. Falls back to the word's
  spoken duration when shorter than the requested window (a 50 ms word
  can't carry a 200 ms animation).
- DEFAULT_MAX_CONCURRENT_ANIMATING_WORDS = 3 codifies R6.15b's
  performance budget.

17 new tests:

- NONE returns identity at every t.
- POP peaks at t=0.5 (scale 1.18) and starts/ends at identity.
- BOUNCE offset is zero at boundaries, negative at peak.
- GLOW mix peaks at t=0.5, zero at boundaries.
- SLIDE_IN starts off-screen + transparent, ends at rest + opaque.
- emphasisFor clamps progress outside [0, 1].
- wordProgress is 0 before start, 0.5 at midpoint, 1 at window end.
- Short-word case uses word duration not requested window.
- Invalid wordProgress args throw.
- DEFAULT_MAX_CONCURRENT_ANIMATING_WORDS is pinned at 3.

Marks ROADMAP R6.15a and R6.15b as done. The renderer-side integration
(KaraokeCaptionEngine + CaptionPreviewOverlay updates that consume
emphasisFor per frame) lands in a follow-up commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…trix (R5.7)

R5.7a — Plugin format family:

  PluginRegistry classifies share-able assets under one detection +
  share-intent surface so the UI stops repeating type-detection logic.
  Kinds: TEMPLATE (.novacut-template), EFFECT_PACK (.ncfx), STYLE_PACK
  (.ncstyle), LUT_CUBE (.cube), LUT_3DL (.3dl), OPENFX_DESCRIPTOR
  (.ncfxd). Longest-extension-first detection so .ncfxd wins over
  .ncfx false-positive substring matches.

  10 new tests in PluginRegistryTest cover case-insensitivity, longest-
  extension precedence, unknown-extension null return, whitespace
  handling, allSupportedExtensions invariants, MIME mapping.

R5.7b — OpenFX descriptor:

  OpenFxDescriptor defines the .ncfxd JSON schema. Schema version 1
  carries novaCutEffectId, openfxId, displayName, and a per-parameter
  list mapping NovaCut effect parameters to OpenFX-named equivalents
  with scale + offset + range + type. The descriptor is metadata-only
  — no OpenFX runtime is hosted on Android. The point is C.14
  (NLE round-trip import) preserving effect intent when projects move
  to DaVinci / Premiere.

  ParameterMapping.toOpenFx / fromOpenFx round-trip the values; zero-
  scale fromOpenFx falls back to the NovaCut range start so a malformed
  scale doesn't produce NaN.

  Parser is permissive: invalid parameter entries are skipped instead
  of failing the whole file. Schema version > CURRENT_SCHEMA_VERSION
  is rejected.

  10 new tests in OpenFxDescriptorTest cover conversion math, round
  trip, schema version gate, malformed input rejection, inverted-range
  skipping, missing-required-field rejection.

R5.7c — Animation-tool compatibility matrix:

  docs/templates.md documents the plugin format family, the round-trip
  compatibility matrix for Lottie / dotLottie / Rive / Glaxnimate, the
  kind-driven validation pipeline, license hygiene rules, and
  reproducibility hooks. Records:
   - NovaCut is import-only for upstream animation formats; export is
     not in scope.
   - R6.16 dotLottie path is the recommended target once
     lottie-compose:7.x lands (state machines + theming + 10-15x smaller
     bundle).
   - A.13 Rive parking decision (Under Consideration in the Forward
     View) explained.
   - License + reproducibility responsibilities split: creator owns
     embedded content rights; NovaCut surfaces declared license on the
     import sheet.

Marks ROADMAP R5.7a, R5.7b, R5.7c as done.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
R5.4a needs the in-editor side-by-side translation preview: source/target
rows, per-row regenerate action, per-language-pair quality chip. This
commit lands the data model; Compose panel rendering follows in a UI
commit.

CaptionTranslationEngine changes:

- New BERGAMOT_PER_PAIR ModelVariant per R6.7. Bergamot models are
  per-language-pair Firefox offline translation models (~100 MB) with
  excellent quality on European pairs.
- MADLAD-400 languageCount bumped 400 → 419 to match the R6.7 doc.
- New EditorRowState enum: TRANSLATED / USER_EDITED / REGENERATE_PENDING.
  Lets the panel show the right affordance per row.
- New LanguagePairQuality enum: EXCELLENT / GOOD / FAIR / EXPERIMENTAL /
  UNKNOWN. Surfaced as a chip ahead of each target so the user sees
  when they're about to translate into a known-weak pair.
- New pairQuality(variant, src, tgt) pure-function lookup. Locale-suffix
  aware ("en-US" → "en"), case-insensitive, identity returns EXCELLENT.
  Recognizes the curated European + East Asian quality bands per variant.
- TranslatedSegment.editorState defaults to TRANSLATED so existing call
  sites continue to compile and behave.

12 new tests cover identity, blank-langs, each variant's quality
bracket, locale-suffix handling, case-insensitivity, editor-state
default, USER_EDITED override, Bergamot metadata, and the MADLAD 419
language count.

Marks ROADMAP R5.4a as in-progress (model + accessors ship; panel
Compose work is the next commit).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
R5.4d wants Noto CJK + Noto Arabic + Noto Devanagari subsets bundled and
routed through caption layout when the source language is not Latin.
Bundling Noto CJK alone is ~20 MB per writing system, so the actual
bundles live behind R5.6a Play Asset Delivery — but the routing decision
should be made in code today so the renderer can pick the right family
the moment the asset bundle lands.

CaptionFontFallbackPolicy:

- FontFamily enum carries the family name + approxBundleBytes +
  coversWritingSystems for every fallback target:
    SYSTEM_SANS_SERIF (Latin / Cyrillic / Greek, 0 MB),
    NOTO_CJK_SC / TC / JP / KR (~20 MB each),
    NOTO_ARABIC, NOTO_HEBREW, NOTO_DEVANAGARI, NOTO_BENGALI,
    NOTO_TAMIL, NOTO_THAI.
- fallbackFor(languageTag) — pure-Kotlin BCP-47 / ISO-639-1 lookup:
    * zh-Hant / zh-TW / zh-HK → NOTO_CJK_TC
    * zh / zh-Hans → NOTO_CJK_SC
    * ja → JP, ko → KR, ar/fa/ur/ps → Arabic, he/yi → Hebrew,
      hi/mr/sa/ne → Devanagari, bn/as → Bengali, ta → Tamil,
      th/lo → Thai.
    * unknown → SYSTEM_SANS_SERIF.
    * Case-insensitive; "JA", "ZH-HANT", etc. all work.
- totalBundleBytes() — sum across a family set for the Settings
  disclosure copy ("Caption fonts bundle: ~64 MB total").
- rendersWithSystemFontsOnly(languageTag) — convenience for skipping
  the disclosure sheet on Latin targets.

17 new tests cover every supported language family, the Chinese
script-subtag split, locale-region suffix handling, case
insensitivity, unknown-language fallback, total-byte math, and the
system-only convenience accessor.

Marks ROADMAP R5.4d as in-progress. The font asset bundle install and
the renderer integration are the next two commits on this item.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
R5.5c needs a Settings → Privacy dashboard that lists every category
NovaCut collects, lets the user export + delete each, and serves as the
single source of truth for any Play Store data-safety form changes.

This commit lands the data model; Compose panel rendering follows.

PrivacyDashboard:

- Category enum: project content, media metadata, ML models, app
  preferences, template library, diagnostic logs, cloud generative
  video, opt-in telemetry.
- StorageLocation enum: DEVICE_INTERNAL / DEVICE_SHARED /
  CLOUD_ON_DEMAND.
- Controls(canExport, canDelete, hasOptOut) — what the user can do.
- DashboardEntry — the per-category row: location, controls,
  collectedBy engines, retention policy copy, collectedByDefault flag.
- entries: List<DashboardEntry> — canonical ordered list. Iteration
  order matches the Category enum declaration so the displayed list
  is deterministic.
- entryFor(category), cloudOrTelemetryCategories() accessors.

10 invariant tests lock the privacy contract:

- Every Category has exactly one DashboardEntry (1:1).
- Every entry allows delete (R5.5c hard requirement).
- Every entry references a retention policy.
- Every entry records at least one collector engine.
- Cloud + telemetry rows are NEVER collected by default and always
  expose an opt-out (matches R5.9c contract).
- Cloud/telemetry filter returns only CLOUD_ON_DEMAND entries.
- ML models row advertises opt-out and is not collected by default.
- Controls value object equality.
- Display order matches enum declaration.

Marks ROADMAP R5.5c as in-progress. The Compose panel that reads this
model is the next commit on this item.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
C.12 needs a visual bezier-curve editor on top of NovaCut's existing
Keyframe + Easing system. The runtime evaluation path (KeyframeEngine)
already supports 12 easings; this commit adds the authoring data model
the visual editor needs without touching the runtime.

KeyframeBezierGraph (pure object):

- BezierSegment(startValue, endValue, c0t, c0v, c1t, c1v) — the cubic
  bezier the editor's draggable handles describe. Tangent t values are
  clamped to [0, 1] in the constructor; tangent v values can overshoot
  to support BACK / ELASTIC easings.
- evaluate(segment, t) — cubic bezier y-axis evaluation, clamps t.
- evaluatePoint(segment, t) — returns (x, y) for the visual editor's
  curve geometry.
- presets: Map<Easing, BezierSegment> — canonical CSS / Material
  control points for all 12 NovaCut easings (LINEAR / EASE_IN /
  EASE_OUT / EASE_IN_OUT / SPRING / BOUNCE / CUBIC / EXPO / SINE /
  CIRCULAR / BACK / ELASTIC).
- presetFor(easing) — safe lookup with LINEAR fallback.
- rescale(segment, startValue, endValue) — denormalizes a unit preset
  into the runtime's value range (opacity 0..1, scale 0..10, etc.).

14 new tests:
- Constructor validates c0t/c1t in [0, 1]; out-of-range throws.
- Overshoot control values (BACK/ELASTIC) accepted.
- evaluate at t=0 → startValue, at t=1 → endValue, out-of-range clamps.
- LINEAR preset evaluates to t.
- EASE_IN_OUT is symmetric around midpoint.
- evaluatePoint returns anchors at boundaries.
- presets covers all Easing entries.
- presetFor falls back to LINEAR.
- EASE_IN midpoint < 0.5; EASE_OUT midpoint > 0.5.
- rescale produces expected start/end/control values.
- Negative range still rescales correctly.

Marks ROADMAP C.12 as in-progress. The KeyframeGraphPanel Composable
follows in a UI commit; the data layer is ready to consume.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
SilenceDetectionEngine already shipped single-token filler detection and
silence-range detection. This commit closes the two C.2 follow-ups the
existing docstring tagged as TODO:

1. Multi-word filler patterns ("you know", "i mean", "at the end of
   the day"). New detectMultiWordFillers(words, config, phrases) does a
   longest-match-first sliding window over adjacent Whisper word
   timestamps. Lowercases + strips punctuation per token so "I Mean,"
   matches "i mean". Marks each matched token so a later short phrase
   doesn't double-count an already-consumed window.
   New DEFAULT_MULTI_WORD_FILLERS set: you know, i mean, sort of, kind
   of, a lot of, at the end of the day.

2. Proposal merge pass. New mergeProposals(cuts, mergeGapMs) collapses
   overlapping or near-adjacent CutProposal entries from the three
   detection sources (silence, single-token filler, multi-word filler)
   into a single deduplicated list. Mixed reasons collapse to SILENCE
   (clearer UX) and matched text is joined.

These let the Cut Assistant Review surface show one combined "this
range can be cut" entry per detected gap, instead of three stacked
proposals for the same gap when silence + filler + multi-word filler
all fire on it.

12 new tests:
- Multi-word "you know" matches; longest match wins ("at the end of
  the day" beats "kind of" prefix); case + punctuation insensitive;
  non-overlapping repeats counted twice; empty input returns empty;
  disabled flag suppresses output.
- Merge: empty in / empty out; non-overlapping preserved; overlapping
  merged; sub-mergeGap merged; mixed reasons collapse to SILENCE;
  out-of-order input sorted first.

Marks ROADMAP C.2 as in-progress (engine work complete; AI Hub Cut
Assistant panel UI follows in a separate commit).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
….11)

AdjustmentLayerEngine already shipped effectsForClip + partitionByLayer
Boundaries with 10 tests. The remaining C.11 gap is the EffectBuilder
bridge that applies adjustment-layer effects during export. This commit
closes the engine-side prep with a single-call helper EffectBuilder can
consume directly, plus the AdjustmentLayerSegment value type the
export segment loop iterates.

New API:
- AdjustmentLayerSegment(timelineStartMs, timelineEndMs, effects) carries
  one export-segment-worth of cumulative adjustment-layer effects + its
  timeline range.
- planForClip(clipStart, clipEnd, layers) combines partitionByLayer
  Boundaries + effectsForClip per sub-range and returns the ordered
  segment list. Caller composes with clip.effects via
  `clip.effects + segment.effects` per range.

Integration sketch lives in the engine docstring for the next commit
that touches EffectBuilder.

5 new tests:
- No layers / invalid range → empty.
- Single mid-clip layer → 3 segments (before/inside/after).
- Whole-clip layer → 1 segment with the layer effects.
- Multiple layers — total duration equals the clip range.

Brings the AdjustmentLayerEngineTest count to 15 covering effects-for
-clip, partition-by-boundaries, and the new plan-for-clip surface.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
C.13 wants the "tap a compound clip → enter its sub-timeline → exit
back" navigation flow. The Clip model already supports isCompound +
compoundClips children; the missing piece is the navigation state.

New CompoundNavStack:
- Level(parentClipId, parentClipName, depth) — one nav-stack entry.
  The root level is always present (parentClipId = null).
- push(clip), pop(), reset() — standard nav primitives.
- breadcrumb — full root-to-current path for the UI breadcrumb bar.
- depth, isAtRoot — derived view-state.
- toSerializedIds() / restore(clips) — autosave-friendly round trip so a
  user who quit while editing a compound clip resumes in that
  sub-timeline.
- MAX_DEPTH = 8 defensive cap against malformed projects.
- Cycle detection: re-entering a compound clip already on the stack
  throws IllegalStateException to prevent infinite editor recursion.

The serialize/restore split keeps the engine free of project-model
lookups; the caller resolves serialized ids back to Clips before
calling restore().

11 new tests:
- Fresh stack starts at root.
- push descends into compound clip.
- push rejects non-compound clip (IllegalArgumentException).
- push rejects cycle (IllegalStateException).
- pop unwinds one level.
- pop at root is no-op.
- reset returns to root.
- Serialized round trip preserves order.
- restore(empty) resets to root.
- MAX_DEPTH enforced.
- Breadcrumb starts with root.

The Composable layer (EditorScreen sub-timeline switcher + breadcrumb
chip) follows in a UI commit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Three small pure-Kotlin helpers + reflection probes added to three Later-
tier engine stubs so the rest of the system can branch on capability and
pre-validate user input without waiting for the engines to be fully wired.

StockAssetEngine (C.7):
- New validateQuery(SearchQuery) checks blank text, empty provider set,
  page < 1, pageSize out of 1..100, negative min duration, inverted
  min/max. Returns a UI-displayable error string or null.
- New attributionLine(asset) builds the credit string the renderer can
  overlay or include in social copy per provider terms.
- New PEXELS / PIXABAY / FREESOUND / FMA API_DOCS constants pinned at
  compile time for the future search UX.

CameraCaptureEngine (C.8):
- isCameraAvailable() upgraded to a cached reflection probe against
  androidx.camera.video.VideoCapture. Flips automatically when the
  CameraX deps are added.
- New teleprompterVisibleWordCount(config, visibleSeconds = 6) pure-math
  helper so the prompter renderer can size its visible window without
  re-querying CameraX.

TimelineImportEngine (C.14):
- New roundTripFidelity(format): RoundTripFidelity (EXCELLENT / GOOD /
  LIMITED) + warningCopy so the import UX can set user expectations for
  what's preserved. OTIO is EXCELLENT, FCPXML GOOD, EDL LIMITED.

No behavioral change for users until the engines are activated; this is
a docstring + helper sweep to make the integration handoffs explicit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summarizes the second autonomous pass on the Round 6 refresh:
A.10 (Oboe scaffold), R5.7a/b/c (PluginRegistry + OpenFX descriptor +
compatibility matrix), R5.9a/b (Dependabot + non-bypassable checksum),
R6.4a/b (SAM 3 placeholder), R6.11 (APV ingest probe), R6.14a (Speaker
SwitchPlanner), R6.15a/b (WordEmphasisAnimator), R6.17a (OutputStreaming
Engine), R5.4a/d (caption translation editor model + Noto fallback policy),
R5.5c (PrivacyDashboard), C.2 (multi-word filler + merge), C.11 (Adjustment
LayerSegment + planForClip), C.12 (KeyframeBezierGraph), C.13 (CompoundNav
Stack), and a Tier A activation-doc sweep across the remaining stubs.

~90 new JVM unit tests added in this pass. Gradle test run not executed
in this environment.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 4 to 7.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](actions/upload-artifact@v4...v7)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
@dependabot @github
Copy link
Copy Markdown
Author

dependabot Bot commented on behalf of github May 17, 2026

Labels

The following labels could not be found: dependencies, github-actions. Please create them before Dependabot can add them to a pull request.

Please fix the above issues or remove invalid values from dependabot.yml.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants