From 657499a30543a35537567ada3ce23a7387947749 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 10:19:35 +0900 Subject: [PATCH 1/6] docs: make Codec Carver product-first and license-aware --- README.md | 573 ++++++++++-------------------------------------------- 1 file changed, 99 insertions(+), 474 deletions(-) diff --git a/README.md b/README.md index 75f027cb..98756080 100644 --- a/README.md +++ b/README.md @@ -1,525 +1,150 @@ # Codec Carver -Python CLI for carving long recordings into metadata-preserved FLAC/Opus files. +[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/ContextualWisdomLab/codec-carver) -For the long-recording curation contract (TMK/VAD evidence precedence, -provenance, and late-TMK selective reconciliation), see -[`docs/architecture/segmentation-reconciliation.md`](docs/architecture/segmentation-reconciliation.md). +**Turn long recordings into reviewable, metadata-preserved audio assets without overwriting the originals.** -Convert supported audio recordings to FLAC or, only when needed to fit each output under a target size, high-bitrate Opus. The tool preserves originals and writes generated files to a separate output directory. Each generated output is kept below the configured size target and below four hours; longer sources are split at long silence intervals when possible. +Codec Carver is a local-first audio curation toolkit. Its primary job is to inspect long recordings, plan bounded conversions, and write generated outputs to a separate location while preserving source metadata and provenance. The repository also contains optional web, MCP, transcription, and evidence-backed audio-library workflows built around the same “inspect first, mutate explicitly” boundary. -## Install +> **Commercial runtime status:** Codec Carver source is MIT-licensed, but the current conversion/probing path requires FFmpeg/FFprobe. Upstream FFmpeg is LGPL-2.1-or-later by default and can become GPL-2.0-or-later depending on the build, which is outside ContextualWisdomLab's inbound commercial-license policy. Issue [#513](https://github.com/ContextualWisdomLab/codec-carver/issues/513) owns replacement of that execution boundary. Until it is resolved, do not present the current FFmpeg-backed conversion path as a commercially approved deployment. -Requires Python 3.10+ and `ffmpeg`/`ffprobe` on `PATH`. +## What it does -```bash -pip install -e . # CLI core (stdlib only) -pip install -e ".[web]" # + FastAPI upload service -pip install -e ".[mcp]" # + MCP server -``` +Codec Carver keeps four user-visible responsibilities distinct: -This installs the `codec-carver` console command: +| Job | Current responsibility | +| --- | --- | +| Carve recordings | Plan size- and duration-bounded generated audio while preserving originals. | +| Preserve evidence | Retain source metadata, conversion reports, content identity, and mutation history needed to review what happened. | +| Curate libraries | Inventory recordings, reconcile Sony TMK/VAD timing evidence, identify exact duplicates, and stage recoverable rename/quarantine plans. | +| Add optional understanding | Produce local transcripts, speaker-aware evidence, and description/title candidates when an explicitly configured model profile is available. | -```bash -codec-carver /path/to/recordings --execute --output-dir under_2gb -``` +The default mutation posture is conservative: source files are not overwritten, generated output belongs in a separate directory, irreversible deletion is not the normal cleanup path, and advanced library mutations require an explicit execution step after planning and revalidation. -## Web service (Docker) +## Current maturity -```bash -docker build -t codec-carver . -docker run -p 8000:8000 codec-carver # upload UI at http://localhost:8000 -``` +`pyproject.toml` records source version `0.1.0`. The repository currently has **no published GitHub release**, so that version is source metadata rather than an immutable supported release. + +The protected `main` branch contains substantial conversion and audio-library functionality, but commercial completion is blocked by the FFmpeg/FFprobe license boundary in #513. Optional model packages and model weights also retain their own licenses and must be evaluated independently from Codec Carver's MIT grant. -## Verified command for this folder +## Quick start for source development -Run from `media_shrink_tool/`: +Codec Carver requires Python 3.10 or newer. ```bash -python3 media_shrinker.py .. \ - --execute \ - --download-icloud \ - --include-under-limit \ - --flac-all \ - --exclude-dir-prefix split_over \ - --max-duration-seconds 14400 \ - --workers 2 \ - --ffmpeg-threads 0 \ - --output-dir under_2gb \ - --report under_2gb/conversion_report.json +git clone https://github.com/ContextualWisdomLab/codec-carver.git +cd codec-carver +python3 -m venv .venv +source .venv/bin/activate +python -m pip install -e . ``` -Outputs are written under `../under_2gb/`. Existing generated output directories and `split_over*` directories should be excluded from scans to avoid reconverting generated media. Files under 2GB are included by default; use `--over-limit-only` only when intentionally processing oversized sources exclusively. +On Windows, activate `.venv\Scripts\activate` instead. -## Config file for repeat workflows +This installs the repository's Python source and declared runtime dependencies. It does **not** make an external codec binary or optional model profile commercially approved. -Instead of re-typing long flag sets, store them once in a `.codec-carver.json` file in the scan root (checked first) or the current working directory: +Run the source-level verification suite with: -```json -{ - "flac_all": true, - "exclude_dir_prefix": ["split_over"], - "max_duration_seconds": 14400, - "workers": 2, - "output_dir": "under_2gb" -} +```bash +python3 -m unittest discover -s tests +python3 -m py_compile media_shrinker.py ``` -Then repeat runs collapse to `python3 media_shrinker.py .. --execute --download-icloud`. +## CLI workflow + +The primary CLI is `codec-carver`. A safe operating sequence is: -- Keys map 1:1 to CLI options with dashes replaced by underscores (`--target-bytes` becomes `target_bytes`). -- Explicit CLI flags always override config values; without a config file, behavior is identical to a plain invocation. -- `root` and `--execute` are intentionally not configurable: the config file is discovered via the scan root, and a config file must never silently turn a dry run into a real conversion. -- Unknown keys, wrong value types, and malformed JSON abort with a clear error listing the valid keys. -- JSON is used instead of TOML because the stdlib TOML parser requires Python 3.11+, while this project also supports Python 3.10. +1. choose a source recording tree; +2. keep generated outputs in a dedicated output directory; +3. inspect the planned work before enabling mutation; +4. execute only after the source/output boundary is correct; +5. retain the generated report as the review record. -## Duration splitting +The historical FFmpeg-backed conversion command remains part of the current source implementation, but it is **not an approved commercial execution path while #513 is open**. Developers evaluating the existing behavior should read the architecture and issue evidence before installing or invoking FFmpeg. -- `--max-duration-seconds 14400` keeps every generated file below four hours. -- When a source is at or above that duration, the tool runs FFmpeg `silencedetect` and prefers the latest safe point inside a long silence before the four-hour boundary. -- If no suitable silence is detected before a boundary, the tool hard-splits just under the configured maximum so the duration rule is still enforced. -- Split outputs are named with part suffixes, for example `meeting.wav.part0001.flac`, `meeting.wav.part0002.flac`. -- Tune silence detection with `--silence-noise` and `--silence-min-duration-seconds` when recordings need stricter or looser silence boundaries. +Configuration can be stored in `.codec-carver.json`; explicit CLI options override file values, and configuration cannot silently turn a dry run into an executing mutation. -## Metadata tagging +## Web and MCP surfaces -- `--set-title`, `--set-artist`, `--set-album`, and `--set-comment` stamp the corresponding tags on every generated output, so archived files stay searchable in players and music libraries. -- Generated commands already copy source metadata with `-map_metadata 0`; the `--set-*` values are injected after it, so each provided key overrides that specific source tag while all other source metadata is preserved (standard ffmpeg semantics). -- When none of the `--set-*` options are passed, generated ffmpeg commands are byte-identical to the untagged behavior. -- Values are passed to ffmpeg as single argv items without a shell, so spaces, quotes, and other special characters are safe as given. +The repository exposes optional integration surfaces through package extras: ```bash -python3 media_shrinker.py .. --execute \ - --set-album "Board Meetings 2026" \ - --set-comment "archived by codec-carver" +python -m pip install -e ".[web]" +python -m pip install -e ".[mcp]" ``` -## Output format +The web surface provides the upload-oriented application boundary, while the MCP surface lets an authorized host call Codec Carver capabilities without importing private implementation modules. These adapters do not change the underlying source/mutation or third-party license boundaries. -- `--format auto` (default) keeps the original behaviour: FLAC for lossless (or `--flac-all`) input, high-bitrate Opus otherwise. -- `--format flac` / `--format opus` force that codec. -- `--format aac` (`.m4a`) and `--format mp3` produce broadly-compatible lossy output fitted to the target size — useful for players/devices that don't handle FLAC or Opus. +## Audio-library workflow -## Transcription (optional) - -Turn each shrunk recording into searchable text. With `--transcribe`, a text and -JSON transcript sidecar is written next to every generated audio file -(`recording.wav.flac` → `recording.wav.flac.txt` / `.json`): +`codec-carver-library` is the higher-level curation surface for large recording collections. It separates evidence collection from mutation: ```bash -python3 media_shrinker.py .. --execute --output-dir under_2gb --transcribe +codec-carver-library /path/to/recordings inventory +codec-carver-library /path/to/recordings plan +codec-carver-library /path/to/recordings apply # validation only +codec-carver-library /path/to/recordings apply --execute ``` -Transcription is opt-in and uses [`faster-whisper`](https://github.com/SYSTRAN/faster-whisper), -imported lazily. Install it to enable the feature: +The library workflow uses full SHA-256 content identity, keeps transcript/evidence state separate from raw recordings, treats late TMK evidence as a reconciliation event rather than silently rewriting history, and sends exact duplicates to a recoverable quarantine boundary rather than permanently deleting them by default. -```bash -pip install faster-whisper # then pass --transcribe -``` +For the detailed timing/evidence contract, see [segmentation reconciliation](docs/architecture/segmentation-reconciliation.md). For the Rust/GPU library architecture, model pinning, iCloud materialization, mutation safety, and recovery rules, see [GPU transcription and Rust backend architecture](docs/architecture/gpu-transcription-rust-backend.md). -If it is not installed, conversion runs normally and transcription is skipped -with a `TRANSCRIBE_SKIP` notice. A failing transcript never aborts a conversion. -Choose a model with `--transcribe-model` (default `base`). +## Optional transcription and model-assisted description -## GPU audio-library curation (Python API + Rust backend) +Transcription and description are optional capabilities, not prerequisites for basic source inspection or package import. Current source contains integrations for pinned Whisper-compatible, MOSS, and MLX model profiles. -The audio-library workflow standardizes recording names from recording time, -known location, transcript content, and SHA-256; parses Sony `.tmk` markers; and -quarantines exact duplicates. Byte-heavy scanning and mutations run in Rust, -while Python keeps one GPU transcription model loaded for the batch. The -default MLX path jointly transcribes and separates anonymous speakers with -MOSS; legacy Whisper remains available explicitly. Ollama is never used and GPU -mode does not fall back to CPU. +Third-party Python packages, native runtimes, and model weights are **not** relicensed by Codec Carver. Their exact package/model revision and license must be approved for the intended distribution before a profile is treated as commercially supported. In particular, do not infer approval from a model name appearing in source or from the repository's MIT license. -The editable install below is for local checkout development only. The hardened -persistent macOS GPU bootstrap installs hash-locked dependencies and runs the -checkout directly instead of installing the project editable. +The current MOSS-Transcribe-Diarize upstream model is published under Apache-2.0, while the pinned Whisper conversion advertises MIT terms; other model profiles can use different terms and remain independently reviewable. The README intentionally does not turn those implementation pins into blanket procurement approval. -```bash -cargo build --release --manifest-path rust-core/Cargo.toml -python3.12 -m venv .venv -.venv/bin/pip install -e ".[transcribe-mlx,describe-mlx]" # Apple Silicon / Metal - -codec-carver-library /path/to/recordings inventory --threads 4 -# Refresh only already-known paths after Finder materializes them. Rust hashes -# exactly these files and Python atomically merges them into the full manifest, -# avoiding unrelated multi-gigabyte iCloud reads. -codec-carver-library /path/to/recordings inventory \ - --path 'FOLDER01/231102_1840(1).wav' \ - --path 'FOLDER01/231102_1840(1).tmk' -# When the recording root is in iCloud, keep mutable evidence state on local -# storage so File Provider cannot roll back an inventory or mutation journal. -codec-carver-library /path/to/recordings \ - --state-dir "$HOME/Library/Application Support/codec-carver/sony-icd-tx650" \ - inventory --path 'FOLDER01/231102_1840(1).wav' -# Queue only explicitly selected dataless files through native FileManager and -# return immediately. Repeat --path for a deliberately bounded download batch. -codec-carver-library /path/to/recordings materialize \ - --path 'FOLDER01/231113_1524.wav' \ - --path 'FOLDER01/231113_1524(1).wav' -codec-carver-library /path/to/recordings hydrate-tmk --workers 4 -codec-carver-library /path/to/recordings hydrate-tmk \ - --workers 1 --path 'FOLDER01/231101_0917.tmk' -codec-carver-library /path/to/recordings stream-transcribe --accelerator mlx -# If a TMK arrives after a fixed-range fallback, bind its verified SHA and get -# a promote-or-selective-reprocess plan without deleting the old transcript. -codec-carver-library /path/to/recordings reconcile-tmk \ - --path 'FOLDER01/recording.wav' -# Speaker-aware MLX transcription is the default. Each SHA-keyed .txt contains -# one dialogue file with consecutive turns rendered as `[S01] ...`, `[S02] ...`. -# The pinned 0.9B MOSS model transcribes Korean and assigns timestamps and -# anonymous speakers in one Metal pass; Ollama and CPU transcription are unused. -# For a deliberately bounded small batch, pipeline iCloud reads in Rust with -# ordered, single-model GPU transcription. -codec-carver-library /path/to/recordings stream-transcribe --accelerator mlx \ - --prefetch-workers 4 --prefetch-max-bytes 536870912 -# Use legacy Whisper explicitly when word-level audit evidence is required. -codec-carver-library /path/to/recordings stream-transcribe --accelerator mlx \ - --no-speaker-diarization --model mlx-community/whisper-large-v3-turbo-q4 \ - --word-timestamps -# Summarize verified transcripts into filename topics with pinned Gemma 4 on -# Metal. This calls MLX-VLM directly; no Ollama server or transcript upload is -# involved. Repeat --path to keep the description batch bounded. -codec-carver-library /path/to/recordings describe \ - --path "recording-a.m4a" --path "recording-b.wav" -# Bind a reviewer-corrected central-context title to exact one-based MLX -# word-timestamp segments. Repeat --segment-id for direct supporting passages. -codec-carver-library /path/to/recordings review-description \ - --path "recording-b.wav" \ - --title "VOC건수보다-정보질이중요하고-활용공유하며-등록절차가간소화" \ - --central-idea "VOC 포상은 건수 최다 등록자가 합니다. 정보 질이 많이 떨어진 것 같습니다. 활용을 투명하게 공유하고 공감을 많이 받은 정보에 혜택을 연결하고 등록 절차를 간소화해야 합니다." \ - --outcome "활용을 투명하게 공유하고 공감을 많이 받은 정보에 혜택을 연결하고 등록 절차를 간소화해야 합니다." \ - --segment-id 164 --segment-id 263 --segment-id 317 --segment-id 318 \ - --segment-id 359 --segment-id 362 --segment-id 444 --segment-id 467 \ - --segment-id 891 --confidence high -codec-carver-library /path/to/recordings plan -# Bound both planning and later apply-time revalidation to one audio record and -# its linked TMK. Repeat --path for an explicitly selected batch. -codec-carver-library /path/to/recordings plan \ - --path "FOLDER01/231018_1018.wav" -# Every name is compared with the complete SHA-bound name derived from its -# transcript and drift is reported. Changing an existing standard name requires -# one of these explicit refresh authorizations. -codec-carver-library /path/to/recordings plan \ - --refresh-standardized-path "2024-06-24_15-44-11__선유로__old-title__sha256-04d93e2e12fb.m4a" -codec-carver-library /path/to/recordings plan \ - --refresh-description-drift --defer-unready -# When iCloud has not supplied every source, mutate only fully ready recordings -# and preserve the unresolved paths as explicit deferred evidence. -codec-carver-library /path/to/recordings plan --defer-unready -codec-carver-library /path/to/recordings apply # validation only -codec-carver-library /path/to/recordings apply --execute -``` +## Safety model -The library backend is loaded only from the repository's release/debug build or -an explicit `--backend-binary` accompanied by `--backend-sha256`; it is never -selected from ambient `PATH`. The selected binary must be owner-controlled, -non-symlinked, and non-group/world-writable. Python copies the exact bytes read -from a stable, no-follow source descriptor into an independent owner-only -execution inode, seals its directory, and forces every Rust command to that -SHA-256-pinned snapshot. Replacing the configured source path after validation -therefore cannot change the bytes that execute. Duration probing uses only the -approved fixed system `ffprobe` locations; ambient environment variables cannot -change the selected executable. Rust, ffprobe, and ffmpeg children all -receive a minimal allowlisted environment that excludes `LD_*` and `DYLD_*` -loader injection controls. MLX-VLM preflight additionally uses Python isolated -mode, a trusted runtime working directory, and verifies the package origin is -beneath that interpreter's prefix before importing native model code. -The approved absolute `ffmpeg` decodes MLX audio before it is passed to MOSS or -Whisper as an in-memory waveform, so the model libraries never resolve a bare -`ffmpeg` from caller-controlled `PATH`. Transcription repositories are also -immutable inputs: MLX Whisper accepts only -`mlx-community/whisper-large-v3-turbo-q4` at revision -`660c343bbf4e52ac257f0b7d952e5388e6f93bef`, while CUDA resolves -`dropbox-dash/faster-whisper-large-v3-turbo` at revision -`0a363e9161cbc7ed1431c9597a8ceaf0c4f78fcf`. Mutable model names or arbitrary -Hub repositories are rejected before inference. Speaker-aware MLX accepts only -`OpenMOSS-Team/MOSS-Transcribe-Diarize` at revision -`e8681d68e7042738ffca8ac8212bc8fcb1131ab8`. - -`describe` loads the pinned 4-bit -`mlx-community/gemma-4-e2b-it-4bit` revision once per batch, samples up to 48 -GPU transcript segments across the full recording, and first extracts one central idea, -outcome, confidence level, and cited segment IDs. A separate title pass must -express that context instead of listing frequent keywords; low-confidence or -generic-only titles are deferred. The final title and its audit context are -cached together in the SHA-keyed transcript sidecar, and evidence selection is -rescored against both the thesis and outcome. The model identifier and revision -are allowlisted, tokenizer remote code is disabled, transcript prompt data is -control-delimiter escaped JSON, and every title term must be recoverable from -the transcript itself. Segment references count only when they appear as -anchored `[S###]` labels; an `S###` string inside speech is not evidence. -Untrusted CR/LF and other control whitespace inside each Whisper segment are -collapsed before Python assigns its label, and the resulting labels must form -the exact contiguous sequence `S001`, `S002`, and so on. Title grounding -preserves token boundaries, so a cross-token substring cannot impersonate a -source term. Central idea and outcome terms must also occur in the cited -transcript segments, so the model cannot legitimize an invented title through -its own analysis fields. When a speaker explicitly marks a conclusion with -phrases such as `결론`, `종합하면`, or `하고 싶은 말`, at least one such segment -must support the analysis. The same conclusion IDs and their neighboring -context survive every repair prompt; if the small model still fails, a literal -fallback may compose a title only from those exact conclusion clauses and then -run the full grounding checks again. A dense explicit directive may use two -directly related evidence segments without padding a long recording with an -unrelated third segment; it still runs the same literal grounding checks. Old -keyword-only caches are not silently upgraded. Planning consumes this -evidence-backed description when present and retains the deterministic extractor -as a no-model failure-safe. -`review-description` provides the corresponding bounded correction path for a -reviewer who has inspected the full transcript. It accepts only a SHA-verified -MLX transcript with word timestamps or joint speaker-segment timestamps and a -pinned transcription revision, copies the exact selected segment text and time -ranges into an owner-only evidence record, and validates the central idea, -outcome, and title against those passages before replacing an automatic title. -The review never edits raw -transcript text. Review-time compound clauses may add Korean grammatical -particles only when at least three transcript-derived semantic terms remain in -the same filename token. Incomplete connective clauses and pronoun-only -observations are rejected as non-outcomes. The selected original segment IDs, -derived evidence IDs, transcription model/revision, and review timestamp remain -auditable in the SHA-keyed sidecar and `manual-description-review.json`. -Once semantic analysis has explicitly failed, its reason is checkpointed and -the unstandardized recording is deferred instead of being renamed from a -keyword-only fallback. Planning reports an existing standard name when its -entire basename differs from the timestamp, location, transcript-derived -central-context title, and SHA suffix recomputed from current evidence, but a -durable rename still requires explicit refresh authorization. -An evidence-backed title that cannot fit the macOS NFD UTF-8 filename budget is -rejected instead of being silently cut into a different or incomplete claim; -the reviewer must approve a shorter title whose complete meaning fits. - -`materialize` is the nonblocking iCloud request mode. Rust validates each -explicit audio/TMK path beneath the library root, rejects symlinks, calls -Foundation's `startDownloadingUbiquitousItem` only for a dataless placeholder, -and reports whether the request was queued or the file was already local. -Python rechecks the current dataless flag, updates the inventory, and writes an -owner-only `materialization-run.json`; it does not infer that accepted requests -have finished. This keeps download selection bounded while Finder is locked or -unavailable. - -`stream-transcribe` is the low-disk iCloud mode: by default Rust streams one -remote file to system scratch while calculating SHA-256, Metal/CUDA transcribes -that local stage, and Python atomically checkpoints before removing the stage. -The default selection order keeps already-materialized recordings ahead of -remote placeholders for throughput. Add `--oldest-first` when lineage work must -select the globally earliest `recorded_at` across nested directories before -local availability. When timestamps tie, an original-looking path is selected -before numbered copy suffixes; the run checkpoint records the chosen order. -Already-materialized recordings follow the same byte-binding rule: Rust opens -each path component with no-follow descriptors, copies and hashes the opened -file into private scratch, and the GPU reads only that verified copy. A pathname -swap after inspection therefore cannot redirect transcription outside the -library. Python independently opens the backend-reported scratch child relative -to its owner-only directory with `O_NOFOLLOW` and requires the scratch file to -have exactly one link. It confirms that name still identifies the opened inode, -unlinks the name, and only then hashes the anonymous descriptor. The actual byte -count and SHA-256 must match both the backend record and any known inventory -digest before ffmpeg or faster-whisper consumes that same descriptor. A -same-user hardlink, replacement path, or post-check rename therefore cannot -redirect the bytes used for inference. -`--prefetch-workers` keeps a bounded rolling queue of Rust/iCloud staging calls -full; `--prefetch-max-bytes` caps their combined logical size (512 MiB by -default). As soon as the next selected recording is staged, the ordered Python -loop starts its single-model GPU transcription while later Rust staging futures -continue in the same bounded pool. GPU work, durable checkpoints, scratch -removal, and native eviction remain serialized. The run summary records the -number of GPU calls that actually overlapped unfinished prefetch work as -`prefetch_transcription_overlaps`. Native eviction is deferred while any bounded -stage is still running so it cannot contend with FileProvider prefetch. The -no-progress stage timeout defaults to 420 seconds because real iCloud -placeholders can take more than two minutes to -deliver their first byte; override it with `--stage-stall-timeout-seconds` when -the provider has a different latency envelope. A parallel prefetch that reaches -that timeout is retried once through the serial staging path, after bounded -parallel stages finish, because FileProvider can defer every concurrent request -while accepting an immediate single request. If that serial canary also fails, -later timeouts in the same batch skip the otherwise identical long retry; other -failures are not retried. The run summary records fallback attempts, recoveries, -and suppressions. A terminal native-stage stall is checkpointed as -`error_code: stage_source_stalled` with `timeout_seconds`, -`stage_progress_bytes`, and `retryable: true`; its readable error points to an -unhealthy iCloud/FileProvider materialization path instead of exposing only a -generic subprocess command timeout. Already local files stay local. -Run `hydrate-tmk` -first when iCloud holds Sony sidecars: -it reads the tiny TMK files concurrently, checkpoints each SHA-256 and the full -ordered marker vector, and backfills any existing transcript sidecars. The -transcript provenance stores the verified primary `tmk_sha256` alongside its -path and marker vector; unresolved or stale TMK identity is recorded as null. -When File Provider status is unknown, only small TMK sidecars use the bounded -direct-read probe; long audio remains on the coordinated, checkpointed path. -Verified -TMK offsets split long MLX recordings into bounded, one-second-overlap decode -ranges while the same pinned Whisper model remains resident; midpoint ownership -removes overlap duplicates and restores every segment to its recording-global -timestamp. This avoids decoding an hours-long recording into one peak-memory -waveform. When a recording longer than ten minutes has no usable TMK vector, -MLX falls back to deterministic five-minute ranges with the same overlap, -global timestamps, and per-range checkpointing. The transcript distinguishes -`tmk_markers`, `fixed_duration`, and `single_pass` chunking, and the run summary -counts newly completed `automatic_chunked_recordings`. A later dataless flag -does not cause the same TMK to be downloaded again. Four workers and a 60-second -per-file timeout are the defaults because higher iCloud File Provider concurrency -can delay every placeholder; rerunning resumes only unresolved sidecars. Repeat -`--path` to verify only the TMKs paired with the bounded audio batch instead of -waking every iCloud placeholder. Already verified TMKs also repair stale linked -transcript metadata without rehashing; `synced_transcripts` and `sync_failed` -report that idempotent pass separately from new TMK hydration. -`stream-transcribe` never blocks an audio recording on an unresolved TMK: it uses -hydrated markers when present and records `tmk_error` evidence otherwise. -If that primary sidecar is still remote but a same-directory, same-time, -same-size TMK with an equivalent copy-normalized stem has a content-verified SHA -and valid ordered markers, streaming may use it only as a bounded decode hint. -The transcript keeps the unresolved primary `tmk_path` and separately records -the hint path, SHA-256, marker count, last marker, and full vector; it never -presents the sibling as the primary sidecar. `tmk_chunk_hints_used` reports this -performance fallback per run. -Gemma title generation also keeps its two-to-six-token quality gate. If a final -literal-evidence repair still exceeds that bound, codec-carver deterministically -rebuilds a subject-purpose title only from the already validated central idea, -outcome, cited transcript evidence, and transcript-grounded terms instead of -accepting or blindly truncating the model output. -Inventory validation also requires every audio `tmk_path` to reference a record -whose kind is exactly `tmk`; a crafted audio-to-audio link cannot authorize -quarantining canonical audio as if it were a duplicate sidecar. -On macOS, Rust requests every dataless item through Foundation's supported -`FileManager.startDownloadingUbiquitousItem` API, then coordinates the read with -`NSFileCoordinator` and performs the single-pass copy-and-hash inside the -coordinated accessor. The coordinator is required by current File Provider -domains to keep `isDownloadRequested`/`isDownloading` active; already-local -files keep the direct fast path. The implementation does not depend on the -undocumented `brctl download` command. If Finder and the coordinated native -request both remain at zero bytes, inspect File Provider with -`fileproviderctl check` before an operator-approved repair. -After a durable transcript checkpoint, Rust also releases the local source -blocks through `FileManager.evictUbiquitousItem`; no `brctl evict` subprocess is -used. Eviction is optional cleanup, so a native eviction error is recorded in -`eviction_failures` without converting a completed transcription into a failure. -At startup it samples the live macOS dataless flag and drains currently local -audio before remote placeholders, keeping the GPU fed while iCloud catches up. -Rust stage monitoring resets its stall clock whenever the partial grows; the -default 420-second stall limit skips only placeholders making no byte progress, -not large files that are actively copying and hashing. An independent absolute -deadline, four times the configured stall limit, also bounds repeated premature -EOF retries even when a faulty provider reports monotonically increasing byte -counts. File Provider can expose -the logical source size before any bytes are readable; Rust rejects such a -premature short/empty EOF, and Python retries it only until the same bounded -zero-progress deadline instead of accepting the empty-file SHA-256. -Batch commands still print their complete JSON checkpoint summary, but return a -non-zero process status when any selected file is recorded in `failures`. -Planning rejects recordings without SHA-256 or transcript evidence by default. -`--defer-unready` keeps those paths unchanged and lists them in -`deferred_paths`, allowing verified subsets to proceed without inventing a -placeholder description. `plan --path` narrows quarantine and rename operations -to the selected audio paths and their linked TMKs; the same selection is stored -in the private plan and recomputed at apply time, while omitting it preserves the -whole-library batch behavior. -Every rescan archives the previous inventory by its SHA-256. If iCloud evicts a -previously hashed recording, same-path/same-size evidence and transcript -sidecars restore its full hash only as an explicitly unverified identity hint. -It cannot form an exact-duplicate group or a new rename/quarantine operation -until Rust hashes current bytes. Audio and TMK duplicate groups are tracked -separately, so a same-SHA TMK sidecar never collides with an audio record. An -executed mutation journal can restore -identity continuity after a move, but remains unverified until current bytes are -opened and hashed again. Materialized files are rehashed before any transcript -cache hit or new mutation plan, then copied and hashed into private scratch -before a GPU call. -Transcripts are keyed by the full SHA-256 under -`.codec-carver/transcripts/`, use owner-only directory/file permissions, and -accept only canonical 64-hex digest filenames. Every transcript consumer opens -the final sidecar relative to a verified directory descriptor with -`O_NOFOLLOW`; symlinks and non-regular sidecars are unavailable evidence, never -external JSON input. Cache, planning, TMK backfill, and inventory reconciliation -also verify the sidecar's embedded SHA-256 against its inventory record; a -foreign sidecar cannot suppress GPU inference or supply a filename title. Exact -copies are inferred only once. Ultra-short -low-confidence words remain auditable in JSON but do not enter standardized -filenames. For long meetings, the optional Gemma phase records the central idea, -outcome, confidence, and directly supporting segment IDs before it creates the -filename title. Generic keyword bundles are rejected, while the deterministic -corpus-central phrase remains the no-model failure-safe. A structurally valid -timestamp/location/SHA wrapper cannot hide an arbitrary description: the -complete expected name is compared and listed in `description_drift_paths`, -while explicit refresh authorization controls the durable rename. -Duplicate files move to the recoverable -`.codec-carver/quarantine/exact-duplicates/` tree; no irreversible deletion is -performed by default. Inventory, TMK, transcript, and mutation paths are -validated beneath the canonical library root at both the public Python bridge -and Rust boundary. Direct `inspect`, `stage`, and `evict` calls reject absolute, -parent, non-portable, and symlink-component paths before launching Rust. -Symlinked state/staging roots are refused, and scratch cleanup uses a -no-follow directory handle rather than a check-then-unlink pathname. -Private state paths are created and opened from `/` one component at a time with -`mkdirat`/`openat`, `O_DIRECTORY`, and `O_NOFOLLOW`; an intermediate ancestor -swap cannot redirect an atomic state write outside the selected library. -Rust holds an exclusive per-library mutation lock from validation through -execution, walks or creates every source/destination parent relative to the -locked root descriptor with `O_NOFOLLOW`, and performs no-overwrite -descriptor-relative renames (`RENAME_EXCL` on macOS, `RENAME_NOREPLACE` on -Linux). Rollback uses the same primitive, so replacing a destination parent -with a symlink cannot redirect a move outside the library. Python refuses -`apply --execute` for injected or substitute backends; only the concrete, -descriptor-safe `RustBackend` may cross the mutation boundary. -Rust returns inventory and mutation-journal JSON on stdout; Python alone commits -those state files through descriptor-relative atomic replacement. Final-name -symlinks are never followed, and a partial or schema-invalid mutation journal is -moved to `.codec-carver/recovery/malformed-journals/` so a damaged checkpoint -cannot brick later inventories. Both recovery path components are created and -opened from the verified state-directory descriptor with `mkdirat`/`openat` -semantics, so an intermediate symlink cannot redirect quarantine outside the -library. - -The importable API is `audio_library.AudioLibrary`. The architecture, evidence -precedence, filename contract, and primary research/standards sources are in -[`docs/architecture/gpu-transcription-rust-backend.md`](docs/architecture/gpu-transcription-rust-backend.md). - -### Persistent macOS GPU runtime - -On macOS, do not place the MLX environment in an iCloud/File Provider-backed -repository. Loading native packages such as `tokenizers`, `torch`, and -`mlx-vlm` can otherwise block inside `dyld` even when the package files appear -materialized. Create the persistent runtime under the local cache instead. The -bootstrap supports Apple Silicon and installs the complete Python dependency -graph from `requirements-macos-mlx-lock.txt` with package hashes verified; the -checkout itself is run directly rather than installed as an editable package. -The script resets `PATH` before its first helper call, uses fixed system-tool -paths, and copies the reviewed SHA-256-pinned `uv` executable into the validated -runtime inode before executing it. A different reviewed `uv` build requires -both `--uv-bin` and its `--uv-sha256` digest. -The runtime must be a direct child of the owner-controlled -`~/Library/Caches/codec-carver/venvs` directory; bootstrap operations stay bound -to the validated directory inode so a later pathname swap cannot redirect them: +Codec Carver's customer-facing safety contract is simpler than the implementation details behind it: -```bash -./scripts/bootstrap_macos_gpu_runtime.sh -GPU_PY="$HOME/Library/Caches/codec-carver/venvs/gpu-py312/bin/python" -"$GPU_PY" "$PWD/audio_library.py" /path/to/library inventory -"$GPU_PY" "$PWD/audio_library.py" /path/to/library transcribe --accelerator mlx -"$GPU_PY" "$PWD/audio_library.py" /path/to/library describe +- **Originals stay authoritative.** Generated conversion output belongs in a separate destination. +- **Plan before mutation.** Library curation separates inventory/planning from `--execute`. +- **Identity is content-bound.** Exact-duplicate and transcript evidence use full SHA-256 identities rather than filenames alone. +- **Changed evidence fails closed.** Paths, content identities, TMK evidence, and mutation plans are revalidated rather than trusted indefinitely. +- **Deletion is recoverable by default.** Duplicate curation uses quarantine instead of routine irreversible deletion. +- **Local-first processing remains explicit.** Optional GPU/model work is designed around local evidence and pinned profiles; external services are not silently introduced as authority. + +## Architecture + +```text +recordings + │ + ├── inspect / inventory / evidence + │ │ + │ ├── conversion plan + │ ├── transcript / timing evidence (optional) + │ └── library curation plan + │ + └── explicit execution + │ + ├── generated output directory + └── recoverable library mutation + journal ``` -The bootstrap installs the hash-locked dependency sets for `transcribe-mlx` and -`describe-mlx` into one reusable environment outside File Provider storage. The Python API -keeps the Whisper and Gemma models resident for batch work, Apple Metal performs -the model inference without Ollama or CPU fallback, and the Rust backend retains -streaming SHA-256, TMK parsing, inventory, and mutation work. +Python owns the user-facing orchestration and optional model workflows. The Rust backend handles byte-heavy inventory and mutation operations for the advanced audio-library path. External codec/model runtimes remain dependencies behind reviewed boundaries; they do not become part of Codec Carver's own licensing authority. -## Safety notes +## Documentation -- Source files selected by the scan are protected from deletion or overwrite; keep `--output-dir` as a generated-only directory so excluded originals are never mistaken for stale generated outputs. -- Generated output names include the original filename and suffix, for example `clip.wav.flac` and `clip.m4a.flac`, so same-stem inputs cannot collide during parallel conversion. -- For lossy sources, `--flac-all` first creates FLAC to avoid additional loss; if that output exceeds the target size, the generated FLAC is removed and a high-bitrate Opus output is created instead. -- Filesystem metadata preservation is best effort: permissions, nanosecond access/modified times, extended attributes, and macOS creation date are copied when the operating system allows it. -- Video-containing files with supported container extensions are rejected unless - `--allow-video` is set to extract their audio track. -- For real media runs, keep `--output-dir` as a generated-only directory such as `under_2gb` and avoid `--overwrite` unless that directory contains no original source files. +- [Public documentation landing](docs/index.md) — product, architecture, safety, and licensing navigation. +- [Segmentation reconciliation](docs/architecture/segmentation-reconciliation.md) — TMK/VAD evidence precedence and late-evidence reconciliation. +- [GPU transcription + Rust backend](docs/architecture/gpu-transcription-rust-backend.md) — advanced library architecture and evidence boundary. +- [ADR index](docs/adr/README.md) — accepted architecture decisions once the ADR documentation lane integrates. +- [FFmpeg commercial-license blocker #513](https://github.com/ContextualWisdomLab/codec-carver/issues/513) — required codec/probe replacement boundary. +- [GitHub Releases](https://github.com/ContextualWisdomLab/codec-carver/releases) — immutable release evidence when a release exists. -## Verification +## Contributing -```bash -python3 -m unittest discover -s tests -python3 -m py_compile media_shrinker.py -``` +Keep product behavior and evidence claims separate from local operator history. New public behavior should update the relevant tests and architecture documentation, and new dependencies, native binaries, model weights, datasets, or assets must pass commercial-license/provenance review before they are recommended as supported product inputs. + +Avoid adding machine-specific file paths, private recording names, one-off incident commands, or internal automation procedure to the public README. Put detailed operational and implementation evidence in the appropriate architecture/doctoring documentation instead. + +## License + +Codec Carver original source and documentation are licensed under the [MIT License](LICENSE), matching the existing `pyproject.toml` metadata. + +That grant does not relicense FFmpeg/FFprobe, Python dependencies, model weights, model code, container bases, datasets, or other external assets. The current FFmpeg-backed execution path remains commercially blocked under ContextualWisdomLab policy until #513 is resolved. From 5f3090d4224bead3fbb9eb1191b1b52460121c1b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 10:19:55 +0900 Subject: [PATCH 2/6] docs: add MIT source license --- LICENSE | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 LICENSE diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..76ad8046 --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Seongho Bae + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. From bcd0f422c3c7fc0657db40267e7872a9dad0b4c8 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 10:20:12 +0900 Subject: [PATCH 3/6] docs: add Codec Carver public documentation landing --- docs/index.md | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 docs/index.md diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 00000000..e7c618fc --- /dev/null +++ b/docs/index.md @@ -0,0 +1,30 @@ +# Codec Carver + +[![Ask DeepWiki](https://deepwiki.com/badge.svg)](https://deepwiki.com/ContextualWisdomLab/codec-carver) + +Codec Carver is a local-first audio curation toolkit for turning long recordings into reviewable generated assets while preserving originals, metadata, content identity, and mutation evidence. + +## Start here + +- [Repository README](https://github.com/ContextualWisdomLab/codec-carver#readme) — product purpose, source-development quickstart, safety model, status, and licensing. +- [Segmentation reconciliation](architecture/segmentation-reconciliation.md) — TMK/VAD evidence precedence and late-evidence behavior. +- [GPU transcription + Rust backend](architecture/gpu-transcription-rust-backend.md) — advanced audio-library architecture, local model execution, mutation safety, and recovery. +- [GitHub Releases](https://github.com/ContextualWisdomLab/codec-carver/releases) — immutable release evidence when one exists. +- [FFmpeg license replacement #513](https://github.com/ContextualWisdomLab/codec-carver/issues/513) — current commercial-runtime blocker. +- [Ask DeepWiki](https://deepwiki.com/ContextualWisdomLab/codec-carver) — repository-grounded navigation and questions. + +## Product boundary + +The current source owns recording inspection, generated-output planning, metadata/provenance capture, exact-content identity, audio-library inventory and recoverable mutation planning. Optional web, MCP, transcription, diarization, and description adapters remain supporting surfaces; they do not change the authority of the original recording or grant third-party software/model licenses. + +## Commercial status + +Codec Carver original source metadata declares MIT and the public-surface branch adds the matching root MIT license text. The repository currently has no published GitHub release. + +The current conversion/probing implementation requires FFmpeg/FFprobe. Because FFmpeg is LGPL/GPL-family software and ContextualWisdomLab does not accept that family as the supported commercial inbound baseline, the current conversion runtime is not commercially complete. The repository must replace that boundary under #513 rather than hide the dependency, select a particular LGPL build, or move it behind another process/container. + +Optional packages, native runtimes, and model weights retain their own licenses and require profile-specific approval. The Codec Carver MIT grant never relicenses those third-party components. + +## Publication truth + +This page is a source documentation landing only. The repository currently reports GitHub Pages disabled. Source presence is not evidence of a published documentation site; any future Pages claim requires settings reconciliation, successful deployment, and live HTTPS verification. From e99a36c688d4e8d158ed95f5d3309e14e43e62dc Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 10:20:50 +0900 Subject: [PATCH 4/6] docs: keep README links current-branch valid --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 98756080..dbacd96e 100644 --- a/README.md +++ b/README.md @@ -133,7 +133,6 @@ Python owns the user-facing orchestration and optional model workflows. The Rust - [Public documentation landing](docs/index.md) — product, architecture, safety, and licensing navigation. - [Segmentation reconciliation](docs/architecture/segmentation-reconciliation.md) — TMK/VAD evidence precedence and late-evidence reconciliation. - [GPU transcription + Rust backend](docs/architecture/gpu-transcription-rust-backend.md) — advanced library architecture and evidence boundary. -- [ADR index](docs/adr/README.md) — accepted architecture decisions once the ADR documentation lane integrates. - [FFmpeg commercial-license blocker #513](https://github.com/ContextualWisdomLab/codec-carver/issues/513) — required codec/probe replacement boundary. - [GitHub Releases](https://github.com/ContextualWisdomLab/codec-carver/releases) — immutable release evidence when a release exists. From 92661f09c4942876639eedeeb40bfe4a7601155c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 10:21:20 +0900 Subject: [PATCH 5/6] build: include canonical MIT license in packages --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 91884dfe..000e450f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -75,6 +75,7 @@ codec-carver = "media_shrinker:main" codec-carver-library = "audio_library:main" [tool.setuptools] +license-files = ["LICENSE"] py-modules = [ "chapters", "config_file", From 88fb77444c139da6e8c36d5b7366f5d4327f49b6 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Wed, 2 Sep 2026 10:25:27 +0900 Subject: [PATCH 6/6] docs: align quickstart with executable package contracts --- README.md | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index dbacd96e..7c8a1506 100644 --- a/README.md +++ b/README.md @@ -43,11 +43,13 @@ On Windows, activate `.venv\Scripts\activate` instead. This installs the repository's Python source and declared runtime dependencies. It does **not** make an external codec binary or optional model profile commercially approved. -Run the source-level verification suite with: +Run the source-level checks that mirror the Python CI entry points: ```bash -python3 -m unittest discover -s tests -python3 -m py_compile media_shrinker.py +python -m py_compile \ + media_shrinker.py config_file.py presets.py saas_web.py mcp_driver.py job_store.py +python -m unittest discover -s tests -v +codec-carver --help ``` ## CLI workflow @@ -66,20 +68,16 @@ Configuration can be stored in `.codec-carver.json`; explicit CLI options overri ## Web and MCP surfaces -The repository exposes optional integration surfaces through package extras: - -```bash -python -m pip install -e ".[web]" -python -m pip install -e ".[mcp]" -``` +The base package metadata already declares the dependencies needed by the current web and MCP entry points because CI imports and tests those surfaces. The `[web]` and `[mcp]` extras remain compatibility/grouping aliases rather than prerequisites that unlock otherwise-missing dependencies. The web surface provides the upload-oriented application boundary, while the MCP surface lets an authorized host call Codec Carver capabilities without importing private implementation modules. These adapters do not change the underlying source/mutation or third-party license boundaries. ## Audio-library workflow -`codec-carver-library` is the higher-level curation surface for large recording collections. It separates evidence collection from mutation: +`codec-carver-library` is the higher-level curation surface for large recording collections. It requires the repository Rust backend in addition to the Python editable install. Build that backend first, then keep inventory/planning separate from mutation: ```bash +cargo build --release --manifest-path rust-core/Cargo.toml codec-carver-library /path/to/recordings inventory codec-carver-library /path/to/recordings plan codec-carver-library /path/to/recordings apply # validation only