Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .specify/feature.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
"feature_directory": "docs/specs/002-proxy-media"
"feature_directory": "docs/specs/005-project-git-versioning"
}
1 change: 1 addition & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,6 @@ For large, parallelizable work that splits into independent units — dead-code/

- **001 — Agent Editing Toolkit** (ACTIVE): plan at [`docs/specs/001-agent-editing-toolkit/plan.md`](docs/specs/001-agent-editing-toolkit/plan.md) (spec/research/data-model/contracts in the same dir). An **MCP server + Skills + Subagents** so external AI agents author/edit Beutl projects **headlessly** (no live-GUI automation). **Declarative-first**: the agent reads an identity-anchored JSON document and submits a desired end-state — a full document or a **JSON Merge Patch (RFC 7396)** — which the toolkit reconciles by **`CoreObject.Id` diff** into Beutl's **undoable `HistoryManager` operations** via the `CoreObjectOperationObserver` recording pipeline (**never raw `PopulateFromJsonObject`/`SetValue` on the live root** — that mints new children, destroying `Id` identity and bypassing undo), behind a **plan/apply** dry-run. Schema/capability discovery comes from `PropertyRegistry` **plus `EngineObject.Properties`** (the modern per-object `IProperty` surface holds most editable params; `PropertyRegistry` alone misses them); `$type` via `JsonHelper.WriteDiscriminator` (`TypeFormat` is internal). Two new **MIT `net10.0`** projects — `Beutl.AgentToolkit` (core lib) + `Beutl.AgentToolkit.Mcp` (stdio console exe, SDK `ModelContextProtocol` 1.4.0, logs→STDERR). Headless still render via `SceneRenderer`/`Renderer.Snapshot` (SKSL runs on CPU; GLSL/3D run on the Vulkan backend — hardware, MoltenVK, or the bundled SwiftShader software fallback — so shader/3D effects are always available, just slower on the software path); **video export in v1** reaches the GPL worker **only via `Beutl.FFmpegIpc`** (no `Beutl.FFmpegWorker` `ProjectReference`). Writes confined to a configured **workspace root** (read anywhere); audio is first-class. **Real-time UI reflection** is delivered by **in-app hosting**: the running editor hosts a loopback HTTP/SSE MCP endpoint (`ModelContextProtocol.AspNetCore`) bound to the active `EditViewModel`'s live `Scene`+`HistoryManager` (**one writer; no new IPC** — Beutl has none into the running app), so agent edits update the preview/timeline/undo stack live; the headless stdio exe handles the no-GUI case via a file-opened session (the `Sessions/` *source* seam swaps file↔live). **Do NOT** add a live-GUI-*automation* (simulated-input) path, a JSON-patch dependency, a universal byte-identity rule, or a GPL `ProjectReference`; in-app live *observation* of the shared model IS in scope and is **not** GUI automation. When building this toolkit, consult that plan + contracts.
- **002 — Proxy Media Workflow** (ACTIVE): plan at [`docs/specs/002-proxy-media/plan.md`](docs/specs/002-proxy-media/plan.md). Spec, research, data-model, contracts, and quickstart live in the same directory.
- **005 — Git Version Control for Editing Projects** (ACTIVE): plan at [`docs/specs/005-project-git-versioning/plan.md`](docs/specs/005-project-git-versioning/plan.md) (spec/research/data-model/contracts/quickstart in the same dir). In-app Git versioning uses the **user's installed `git` CLI only** (≥ 2.36; **no LibGit2Sharp**, no bundled git; graceful degradation when absent). Snapshots fire on **explicit Save/Save All/close only** (never per autosave tick or timer), plus manual commits; automatic messages are stable English with a `Beutl-Snapshot:` trailer. The Avalonia-free public `IProjectVersionControlService` in `src/Beutl.Editor/VersionControl/` is **read/query only**; `VersionControlCoordinator` owns the internal Active→Retiring→Retired backend, all user-level version-control mutations, and one exclusive **durably preserve → close → operation-specific transaction → reopen** cycle. Consented stale-lock removal remains the sole narrow mutation exposed separately through `IRepositoryLockRecoveryService`. `IProjectVersionControlInitializer.InitializeCurrentProjectAsync(Project expectedProject, ...)` requires the exact currently open project as a concurrency guard, forwards the exact operation token to the identity callback, and rejects initialization if another project becomes current; the identity flyout observes that token and closes itself on the UI thread when cancellation is requested. This is a breaking migration with no parameterless shim, so use `feat!:`/`refactor!:` plus a `BREAKING CHANGE:` footer naming the callback change. Dirty restore/switch makes a Safety commit; dirty pull writes `refs/beutl/safety/*` without moving the branch, builds the merged tree and Safety commit off-ref, then applies that exact state. Restore is **restore-as-new-commit**; a post-commit failure appends a compensating Recovery commit. Tree transitions hold the worktree-private `HEAD.lock`, validate scoped worktree/index fingerprints, and compare-and-swap the same attached branch from its exact expected tip as the final durable step; external ref movement yields `OwnershipLost`, unverified recovery yields `RecoveryFailed`, and neither may be overwritten or reopened automatically. `RepositoryDirty` is reserved for actual cleanliness-precondition failures; the coordinator renders either unsafe transition state as exactly the localized uncertain-transition failure without composing inner result text. Enclosing repositories are detected and project-file mutations are **pathspec-scoped to the project directory**; branch/push/pull, whole-repository cleanliness checks, and guarded branch CAS retain disclosed repository-wide semantics. Four serialization prerequisites land first: `{Id:N}.belm` naming (`ElementFileNaming`), appVersion migration semantics (`feat!:`), Include/Exclude `/` normalization, and `JsonHelper` `NewLine="\n"` pinning. **Do NOT** expose merge (beyond fast-forward pull), rebase, destructive reset, force-push, other history rewriting, detached HEAD on the user's project worktree, partial staging, timer checkpoints, or LibGit2Sharp; never parse human-facing Git output (porcelain v2 `-z` only), and always set `GIT_TERMINAL_PROMPT=0` + `GIT_OPTIONAL_LOCKS=0` + `GIT_LITERAL_PATHSPECS=1`, except the validated NUL-delimited `check-ignore --stdin -z` probe sets literal pathspecs to `0` so ignore patterns are evaluated. When building this feature, consult that plan and contracts.
- **003 — Resolution-Independent Rendering Pipeline** (delivered; guardrails still apply): plan at [`docs/specs/003-resolution-independent-pipeline/plan.md`](docs/specs/003-resolution-independent-pipeline/plan.md) (spec + research + data-model + contracts in the same dir). **Supply-driven** scale model (logical properties; output scale `RenderNodeContext.OutputScale` = final target only; per-op `EffectiveScale`, vector = `Unbounded`; computed working scale `w` via `ResolveWorkingScale` — **no per-effect policy** (the `ResolutionPolicy` type was removed; an effect needing a non-supply `w` overrides `Process` in a custom `FilterEffectRenderNode` from `FilterEffect.Resource.CreateRenderNode()`); root surface `ceil(FrameSize × s_out)`); at `s_out = 1.0` the golden content set (vector / text / Skia-filter / unscaled bitmap) stays byte-identical, but byte-identity is **no longer a universal guarantee** — a scaled bitmap feeding an effect renders at its coherent supply density (FR-019; the universal constraint was abolished in `32634977c`). **Do NOT revert to top-down single-scale or output-capped intermediates** — `s_out` never clamps an intermediate (FR-016/FR-036). **Do NOT reintroduce a universal byte-identity-at-`s_out=1` rule, a `ResolutionPolicy` enum, or a `PreserveSource` policy** (all removed); the only **global** working-scale bound is `MaxWorkingScale` (FR-037; preview `2×s_out`, export `+∞` — no quality ceiling; per-buffer allocatability is bounded by `ClampWorkingScaleToBufferBudget`) — additionally the per-buffer dimension clamp (FR-037(b), `ClampWorkingScaleToBufferBudget`, 16384 px per axis) may further reduce `w` at effect boundaries to keep buffers allocatable (two distinct bounds, do not conflate them). Breaking public surface (`refactor!`/`feat!` + `BREAKING CHANGE:`). When touching `Beutl.Engine` graphics rendering / filter effects, consult that plan and the contracts.
<!-- SPECKIT END -->
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@
<PackageVersion Include="NuGet.ProjectModel" Version="7.9.0" />
<PackageVersion Include="NuGet.Protocol" Version="7.9.0" />
<PackageVersion Include="NuGet.Resolver" Version="7.9.0" />
<PackageVersion Include="NuGet.Versioning" Version="7.9.0" />
<PackageVersion Include="Nuke.Common" Version="10.1.0" />
<PackageVersion Include="NUnit" Version="4.6.1" />
<PackageVersion Include="NUnit3TestAdapter" Version="6.3.0" />
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@ Create a Beutl account to acquire extensions or publish your own developed exten
### 🧰 Rich Effects
From basic effects like color filters, blurs, shadows, and LUTs, to minor effects like outlines, inner shadows, and long shadows, Beutl offers a wide range of effects. These can be further expanded through extensions.

### 🕘 Version Control
Track a project's editing history with Git, restore earlier versions, create experimental branches, and back up work to a remote repository from within Beutl.

## 📥 Installation
Refer to the [documentation](https://docs.beutl.beditor.net/get-started/install) here.

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Specification Quality Checklist: Git Version Control for Editing Projects

**Purpose**: Validate specification completeness and quality before proceeding to planning
**Created**: 2026-07-28
**Feature**: [spec.md](../spec.md)

## Content Quality

- [x] No implementation details (languages, frameworks, APIs)
- [x] Focused on user value and business needs
- [x] Written for non-technical stakeholders
- [x] All mandatory sections completed

## Requirement Completeness

- [x] No [NEEDS CLARIFICATION] markers remain
- [x] Requirements are testable and unambiguous
- [x] Success criteria are measurable
- [x] Success criteria are technology-agnostic (no implementation details)
- [x] All acceptance scenarios are defined
- [x] Edge cases are identified
- [x] Scope is clearly bounded
- [x] Dependencies and assumptions identified

## Feature Readiness

- [x] All functional requirements have clear acceptance criteria
- [x] User scenarios cover primary flows
- [x] Feature meets measurable outcomes defined in Success Criteria
- [x] No implementation details leak into specification

## Notes

- "Git" appears throughout as a product-level domain concept (the user-approved scope is Git-based versioning with remotes), not as an implementation choice; engine selection (CLI vs library) is deliberately absent and deferred to plan/research.
- Four assumptions are marked *(to be confirmed in clarification)* — creation-default, timer checkpoints, Save As history, LFS default. They carry informed defaults, so no [NEEDS CLARIFICATION] markers were needed; `/speckit-clarify` will confirm or adjust them.
Loading
Loading