Prompt override and reconcile (#49) #101

Merged
crowmaki merged 2 commits from issue-49-prompt-override into main 2026-10-08 21:54:45 +00:00
Owner

Closes #49.

A filmmaker can hand-tune a Shot's prompt. The composer never overwrites the edit and never nags (User Stories 105–107, decisions in #9).

What changed

  • Override storage (schema v21). Three new shot columns:

    • prompt_override: the edited text.
    • prompt_fork_base: the composed text the override was forked from, or last reconciled with.
    • prompt_fork_slots: a JSON map from each slot tag to the Slot list key it named at fork time.

    ShotRow carries all three, so deleting a Shot and undoing the delete brings the override back. A split leaves the override on the first part.

  • prompt_override.rs.

    • shot_prompt now returns:
      • text: what a Take is sent.
      • composed: the preview.
      • overridden.
      • composer_changed: true when the fork base ≠ the composed text now.
      • broken_tags.
    • set_shot_prompt_override: the first edit forks the preview and later edits keep the fork base. None resets. Saving the composed text unchanged is not an override.
    • shot_prompt_reconcile: the three-way merge.
    • rebase_shot_prompt_override: ends a reconcile, or keeps reassigned tags as they now point.
    • queueable_prompt: the gate that the queue (#51) will call. It refuses an override with a broken tag. This follows the same pattern as require_queueable_length.
    • All edits are undoable.
  • Broken tags. A <Picture N> / <Audio N> tag is broken in two cases:

    • empty: nothing is sent in that slot.
    • reassigned: the slot now holds a different Slot list item than at fork time.

    #9 says "a tag pointing at an empty or reassigned slot blocks queuing". Detecting reassignment is why the fork-time slot map is stored. This goes one column beyond #24's "plain text plus its fork-base".

  • three_way.rs. A line-by-line three-way merge built on an LCS diff, with no new dependency.

    • Each side's edits form one hunk only where they overlap in the base, or insert at the same point. Edits on neighbouring lines therefore don't turn into conflicts.
    • Changes made only by the user are kept silently. Hunks changed only by the composer default to the composer's lines. Conflicts default to the user's lines.
  • Adapter. New commands set_shot_prompt_override, shot_prompt_reconcile and rebase_shot_prompt_override. shot_prompt now returns ShotPromptDto, and the bindings are regenerated.

  • UI (ShotPrompt.svelte).

    • "Edit a copy" forks the preview. "Reset to composed" drops the override.
    • Quiet badges show on the collapsed row: Edited and Composer changed. A red Broken tag badge appears when queuing is blocked. The panel now loads while collapsed so these badges are visible.
    • "Reconcile…" lets the user pick a side for each changed hunk, or "Keep mine", or reset.
    • "Keep tags as they point now" accepts reassigned slots when the composer hasn't changed.
  • CONTEXT.md defines Prompt override, fork base, reconcile and broken tag.

Screenshots

Editing a copy of the composed preview:

Editing a copy

The saved override after the Scene's Ambience changed. The quiet Composer changed marker shows, and a tag naming an empty slot blocks queuing:

Override with marker and broken tag

The on-demand reconcile, where the composer's new soundscape line is offered:

Reconcile

After applying it, the marker is gone and the edit is kept with the composer's new line:

Reconciled

Tests

  • tests/prompt_override.rs has 16 tests through ProjectSession. They cover:
    • forking and the fork base
    • no override from unchanged text
    • refusing blank text
    • the marker appearing and clearing
    • reconcile hunks and rebasing
    • "keep mine"
    • reset
    • undo and redo of edits and rebases
    • a Shot delete that is undone
    • empty and reassigned tags blocking queueable_prompt
    • keeping reassigned tags
    • CRLF
  • three_way.rs unit tests cover:
    • no change
    • edits only the user made
    • changes only the composer made
    • conflicts, including both sides inserting at the same point
    • the same change on both sides
    • insertions and deletions
  • promptReconcile.test.ts covers the default choices and the resolved text.
  • The adapter smoke test does a round trip: fork, marker, broken tag, reconcile, rebase.
  • cargo test --workspace --all-features, bun test src and bun run check all pass. I also checked the flow in the running app, including undo and redo of a reconcile.
Closes #49. A filmmaker can hand-tune a Shot's prompt. The composer never overwrites the edit and never nags (User Stories 105–107, decisions in #9). ## What changed - **Override storage (schema v21).** Three new `shot` columns: - `prompt_override`: the edited text. - `prompt_fork_base`: the composed text the override was forked from, or last reconciled with. - `prompt_fork_slots`: a JSON map from each slot tag to the Slot list key it named at fork time. `ShotRow` carries all three, so deleting a Shot and undoing the delete brings the override back. A split leaves the override on the first part. - **`prompt_override.rs`.** - `shot_prompt` now returns: - `text`: what a Take is sent. - `composed`: the preview. - `overridden`. - `composer_changed`: true when the fork base ≠ the composed text now. - `broken_tags`. - `set_shot_prompt_override`: the first edit forks the preview and later edits keep the fork base. `None` resets. Saving the composed text unchanged is not an override. - `shot_prompt_reconcile`: the three-way merge. - `rebase_shot_prompt_override`: ends a reconcile, or keeps reassigned tags as they now point. - `queueable_prompt`: the gate that the queue (#51) will call. It refuses an override with a broken tag. This follows the same pattern as `require_queueable_length`. - All edits are undoable. - **Broken tags.** A `<Picture N>` / `<Audio N>` tag is broken in two cases: - **empty**: nothing is sent in that slot. - **reassigned**: the slot now holds a different Slot list item than at fork time. #9 says "a tag pointing at an empty or reassigned slot blocks queuing". Detecting reassignment is why the fork-time slot map is stored. This goes one column beyond #24's "plain text plus its fork-base". - **`three_way.rs`.** A line-by-line three-way merge built on an LCS diff, with no new dependency. - Each side's edits form one hunk only where they overlap in the base, or insert at the same point. Edits on neighbouring lines therefore don't turn into conflicts. - Changes made only by the user are kept silently. Hunks changed only by the composer default to the composer's lines. Conflicts default to the user's lines. - **Adapter.** New commands `set_shot_prompt_override`, `shot_prompt_reconcile` and `rebase_shot_prompt_override`. `shot_prompt` now returns `ShotPromptDto`, and the bindings are regenerated. - **UI (`ShotPrompt.svelte`).** - "Edit a copy" forks the preview. "Reset to composed" drops the override. - Quiet badges show on the collapsed row: `Edited` and `Composer changed`. A red `Broken tag` badge appears when queuing is blocked. The panel now loads while collapsed so these badges are visible. - "Reconcile…" lets the user pick a side for each changed hunk, or "Keep mine", or reset. - "Keep tags as they point now" accepts reassigned slots when the composer hasn't changed. - **CONTEXT.md** defines Prompt override, fork base, reconcile and broken tag. ## Screenshots Editing a copy of the composed preview: ![Editing a copy](https://git.crowmaki.me/attachments/9b0ec075-4234-4c30-81c7-8f4acd2e3eb8) The saved override after the Scene's Ambience changed. The quiet `Composer changed` marker shows, and a tag naming an empty slot blocks queuing: ![Override with marker and broken tag](https://git.crowmaki.me/attachments/6fefdc29-c43b-417b-a46c-a3910e4cdd4d) The on-demand reconcile, where the composer's new soundscape line is offered: ![Reconcile](https://git.crowmaki.me/attachments/110de477-7e63-48ba-8f3a-1b2ac5c04078) After applying it, the marker is gone and the edit is kept with the composer's new line: ![Reconciled](https://git.crowmaki.me/attachments/5c071a8e-1ade-45b1-9b56-e2b46c48904c) ## Tests - `tests/prompt_override.rs` has 16 tests through `ProjectSession`. They cover: - forking and the fork base - no override from unchanged text - refusing blank text - the marker appearing and clearing - reconcile hunks and rebasing - "keep mine" - reset - undo and redo of edits and rebases - a Shot delete that is undone - empty and reassigned tags blocking `queueable_prompt` - keeping reassigned tags - CRLF - `three_way.rs` unit tests cover: - no change - edits only the user made - changes only the composer made - conflicts, including both sides inserting at the same point - the same change on both sides - insertions and deletions - `promptReconcile.test.ts` covers the default choices and the resolved text. - The adapter smoke test does a round trip: fork, marker, broken tag, reconcile, rebase. - `cargo test --workspace --all-features`, `bun test src` and `bun run check` all pass. I also checked the flow in the running app, including undo and redo of a reconcile.
Fork a Shot's composed prompt into a hand-edited override that keeps its
fork base and the slot each tag named. A later composer change shows only
a quiet "Composer changed" marker; a line-by-line three-way reconcile runs
on demand, offering the composer's changes hunk by hunk and keeping the
user's own. A slot tag that names an empty slot, or a slot now holding
another reference, is broken and blocks queuing (`queueable_prompt`).
Override edits, resets and reconciles are undoable.
Let a reassigned slot tag be kept as it now points when the composer has
not changed, normalise CRLF on save, refresh an open reconcile when the
composer moves, and name the merge's side edits and overlap groups.
crowmaki deleted branch issue-49-prompt-override 2026-10-08 21:54:45 +00:00
Sign in to join this conversation.
No description provided.