# 1.10.0 — `gc`: garbage-collect orphan materials

Minor release. One new command, zero changes to existing behavior. Where v1.8.0's
`validate` *reports* orphan materials (`orphan_text` / `orphan_media`), `gc`
*removes* them — completing the validate→fixer trio with v1.9.0 `sync-timelines`.

## The problem it solves

Every time `import-captions` (or `restyle`) replaces a caption track, the old text
materials are left behind in `draft_content.json`, referenced by nothing. They're
invisible and harmless, but they accumulate. `validate` flags them as
`materials.orphan_text` / `materials.orphan_media` (info). `gc` deletes them.

## Highlights

- 🧹 **`capcut-david gc <project>`** — removes the segment-orphan text/video/audio
  materials `validate` reports, in place, and re-serialises the draft.
- 🔒 **Scope is the safety proof.** gc deletes from **only** `materials.texts` /
  `videos` / `audios`. For those three slots a cross-material id scan over all 9
  fixtures (1283 materials) found **zero material→material references** — so a
  segment-orphan there is genuinely unreferenced and safe to delete. gc never
  touches any other slot.
- 💾 **JSON-only.** gc never deletes a file under `Resources/` (a media path can be
  shared by a live material) — it only edits `draft_content.json` (+ its `.bak`).
- 🛑 **Refuses on a broken draft.** A dangling reference or a duplicate material id
  → exit 1, no write. (A duplicate id would make "the orphan with id X" ambiguous.)
- 🛟 **No-op never writes.** Nothing orphaned ⇒ no `saveDraft`, no `.bak` churn,
  mtime unchanged (`wrote:false`) — idempotent, and the last edit's rollback is kept.
- 🔁 **Apply-by-default + `--dry-run`**; in `WRITE_COMMANDS` (CapCut-open guard,
  `--force`); a stderr WARNING on real removal names the `.bak` and reminds you to
  run `sync-timelines` afterwards.
- ✅ **363 tests** (+24). `gc.js` 100% lines / 100% functions. Typecheck + lint clean.

## Usage

```
$ capcut-david gc my-draft            # remove orphans, JSON report, exit 0
$ capcut-david gc my-draft --dry-run  # report what WOULD be removed, write nothing
$ capcut-david gc my-draft -q         # exit code only
```

Typical pairing with the v1.8.0 detector:

```
$ capcut-david validate my-draft         # see orphan_text / orphan_media (info)
$ capcut-david gc my-draft               # remove them (CapCut closed)
$ capcut-david sync-timelines my-draft   # if CapCut had opened the draft, push the
                                         # orphan-free root into the Timelines mirrors
```

JSON envelope (`capcut-david/gc@1`):

```json
{
  "schema": "capcut-david/gc@1",
  "ok": true,
  "dry_run": false,
  "project": "C:\\...\\My Draft",
  "draft_file": "C:\\...\\My Draft\\draft_content.json",
  "removed": { "texts": ["<id>"], "videos": [], "audios": [] },
  "skipped_cross_ref": [],
  "summary": { "orphan_text": 1, "orphan_media": 0, "removed_total": 1, "wrote": true }
}
```

**Exit codes:** `0` = success (incl. nothing-to-gc), `1` = tool failure (incl. a
draft with a dangling ref / duplicate id, which gc refuses). No exit 2.

## What gc does NOT do

- It is an **orphan media/text GC, not a full GC**: deleting an orphan text leaves
  its now-doubly-orphan companion bundle (speeds/placeholder/animation) in place.
  Those were already segment-orphans, carry no inbound reference, and `validate`
  ignores them by design — gc doesn't make the draft worse. A future `--deep` flag
  (needing its own safety proof) is the documented escape hatch.
- It never deletes disk assets, never resolves `##_draftpath_…##` tokens, and never
  edits tracks/segments (so positional bindings like a transition at
  `extra_material_refs[2]` survive).
- After a real removal it **cannot** promise byte-identity (the deletions are
  re-serialised). The byte-identity guarantee is the no-op path only.

## Migration

**None.** `gc` is additive; no existing command changes output.

## Compatibility

- CapCut ≥ 5.x desktop (Windows + macOS), JianYing 6+ unsupported — unchanged.
- Node `>= 18` — unchanged.
- Runtime dependencies: zero — unchanged.

## Roadmap (1.x — non-binding)

- `1.x.0` — `init-meta` (generate a missing `draft_meta_info.json`)
- `1.x.0` — `validate --fix` umbrella over the per-finding fixers (gc, sync-timelines, …)
- `1.x.0` — `capcut-david query` (catalogue lookup)
