Skip to content

Proposal: make project save state explicit and consistent #637

Description

@hcoona

Goal

The common project save flow works well today, but hcoona#17 describes several edge cases where different parts of the app appear to interpret "saved" slightly differently.

The goal of this proposal is not to redesign the project file format or introduce a large new persistence system. The goal is smaller: make the save-state decisions explicit, shared, and tested so that the window title, Save/Ctrl+S, close/switch confirmation, autosave, and actual file persistence all agree.

Observed problem

Today, the project's undo version and last saved version work as a reasonable dirty-state proxy for the normal path:

  1. Open an existing project.
  2. Edit it.
  3. Save it back to the same file.

The edge cases are different. A project can have no new undo edits but still not be safely represented by the main project file. For example:

  • the project has no file name yet;
  • the attached project file was deleted or is missing;
  • the current contents came from a newer autosave and have not been written back to the main file;
  • autosave paths can accidentally become part of later save/autosave decisions.

Those cases are not all the same UX problem. "Can the user save?", "Should the title show a dirty marker?", and "Should closing warn the user?" are related, but they should not necessarily be the same boolean.

Proposed direction

I suggest centralizing the save-state logic into a small set of derived decisions on Project, or into a small value object if that is cleaner. The exact shape is less important than making the decisions explicit and tested.

A lightweight model could track:

  • whether the project has a main save target;
  • whether the current content differs from the last explicit save;
  • whether the main save target is known to be missing;
  • whether the project was restored from a newer autosave;
  • whether an untitled project is still the initial blank project or has user edits.

From that, the UI can derive separate decisions:

Decision Suggested meaning
CanSave Save/Ctrl+S should do something useful: Save As for untitled projects, write dirty content, recreate a missing file when possible, or write recovered autosave content back to the main file.
ShowsDirtyMarker The current content is not safely represented by the main project file.
ShouldWarnBeforeDiscard Continuing could lose meaningful project content or recovered autosave data.
ShouldAutosave Autosave may protect dirty content, but autosave alone should not count as an explicit save to the main project file.

Proposed user-visible behavior

Scenario Proposed behavior
Untitled blank project Save may open Save As, but the title does not need a dirty marker and closing does not need a warning.
Untitled project with user edits Save opens Save As, the title shows dirty state, and close/switch prompts before discarding.
Existing clean project Save can be disabled, no dirty marker, no close/switch prompt.
Existing dirty project Save is enabled, the title shows dirty state, and close/switch prompts.
Attached file is missing Save should try to write back to the original path when possible. If that fails, the app should keep the unsaved state and fall back to Save As or an error path.
Newer autosave was restored The project should remain in a "needs explicit save" state until the recovered content is saved to the main file or saved elsewhere.
Save As is canceled The project state should remain unchanged; dirty/recovered/missing-file state should not be cleared.

Important details

A few details seem important for correctness:

  • Autosave should not clear the explicit-save state. It protects the user from data loss, but it should not make the main project file count as saved.
  • Restoring from *-autosave-N.yafc should keep the main project file as the save target, rather than turning the autosave file into the project's normal path.
  • Saving should not short-circuit only because the undo version matches. If the target file is missing, Save still needs to write the file.
  • Existing behavior such as flushing pending undo/edit batches before persistence should be preserved.
  • File-system checks should be treated carefully. Whether a target can be written is ultimately known at save time, so failed saves should leave the project in a state where the user can retry or use Save As.

Owner decisions

I think the main product decisions for the project owners are:

  1. Should a brand-new untitled blank project have Save enabled, or should Save only become enabled after edits?
  2. Should the title * mean "content has user edits" or the broader "main project file does not safely represent the current content"?
  3. When an attached file is missing, should Ctrl+S first try to recreate the original path, immediately prompt Save As, or show an error first?
  4. After autosave recovery, should Save overwrite the main file directly, or should the UI make the recovery state more explicit before overwriting?
  5. What counts as a meaningful edit for an untitled project: only user-created project changes, or also automatic/default project initialization?

My preference is:

  • untitled blank projects should be saveable but not dirty;
  • untitled edited projects should behave like normal unsaved work;
  • recovered autosave content should require an explicit save;
  • missing attached files should not be treated as clean.

Practical implementation path

This can be kept incremental:

  1. Add model-level tests for the save-state edge cases in Review save-state edge-case branch for upstream PR hcoona/yafc-ce#17.
  2. Add a small set of derived save-state properties, or a small value object, without changing the project file format.
  3. Update MainScreen to use those derived decisions for Save/Ctrl+S, the title marker, close/switch confirmation, and autosave.
  4. Add tests or manual verification for Save As cancelation, missing-file save, autosave recovery, and current-project autosave paths.
  5. Keep the PR focused on behavior consistency rather than a broad persistence rewrite.

This could be one PR if the change stays small, or split into a model/test PR followed by UI wiring if that is easier to review.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions