Phase 1 of the improvement plan, PR 4. Mirrors GitHub PR Twest2/StepForge#9.
Monotonic revision on every guide/step; compare-and-swap saves via { expectedRevision } throwing RevisionConflictError; legacy data upgrades organically.
generateStepPatch saves with the pre-generation revision: stale AI responses can no longer overwrite user edits made mid-generation.
flushStep/flushGuide only clear dirty AFTER a durable save; failures keep dirty state, surface saveError, toast, retry. Navigation away flushes pending saves.
Corrupt guides/steps are quarantined (bytes preserved) and reported via store.getRecoveryReport() instead of silently vanishing.
Verified: 240 unit tests incl. new revision/quarantine/AI-stale suites; startup smoke + sample artifacts E2E pass. No migration needed; rollback-safe.
Phase 1 of the improvement plan, PR 4. Mirrors GitHub PR Twest2/StepForge#9.
- Monotonic revision on every guide/step; compare-and-swap saves via { expectedRevision } throwing RevisionConflictError; legacy data upgrades organically.
- generateStepPatch saves with the pre-generation revision: stale AI responses can no longer overwrite user edits made mid-generation.
- flushStep/flushGuide only clear dirty AFTER a durable save; failures keep dirty state, surface saveError, toast, retry. Navigation away flushes pending saves.
- Corrupt guides/steps are quarantined (bytes preserved) and reported via store.getRecoveryReport() instead of silently vanishing.
Verified: 240 unit tests incl. new revision/quarantine/AI-stale suites; startup smoke + sample artifacts E2E pass. No migration needed; rollback-safe.
Phase 1 of the improvement plan (PR 4 of the sequence): stop concurrent
whole-object saves from losing edits, stop failed saves from reporting clean,
and stop corrupt user data from silently vanishing.
Revisions (core/schema.js, core/store.js):
- Every guide and step carries a monotonic `revision`, bumped on each store
write. Legacy v1 data without the field reads as revision 0 and upgrades on
its next save — no migration pass, no data rewrite.
- saveGuide/saveStep accept { expectedRevision } for compare-and-swap saves;
a mismatch throws RevisionConflictError instead of clobbering. Direct user
edits pass no expectation (the user is the authority); background writers
must pass one.
Stale AI responses (app/text-intel.js):
- generateStepPatch snapshots the step revision before the (slow) model call,
re-reads the step after it, and saves with the original expectedRevision. A
user edit made during generation now surfaces as "the step changed while AI
was generating; nothing was overwritten" — previously the AI response
silently overwrote the newer edit.
Autosave truthfulness (app/renderer/editor.js):
- flushStep/flushGuide cleared the dirty flag BEFORE awaiting the IPC save,
so a rejected save (invoked via a debounce that never handled rejections)
lost the visible dirty state. The flag is now cleared only after a durable
save; failures keep it dirty, surface a persistent saveError in editor
meta, toast the user, and retry on the next edit or explicit save.
- Navigating away from the editor flushes pending debounced saves so the
last edit can never be dropped by a view switch.
Corruption quarantine (core/store.js):
- listGuides/listSteps used to silently skip unreadable entries — a corrupt
guide just vanished from the library. Corrupt guide/step directories are
now moved to library/quarantine (original bytes preserved) and recorded in
a recovery report (store.getRecoveryReport()) for the UI. Empty in-progress
directories are still skipped quietly — absence of guide.json is not
corruption.
Tests: revision increments, stale-CAS rejection with user edit surviving,
CAS success path, guide CAS, v1 no-revision upgrade, guide/step quarantine
with preserved bytes + recovery report, empty-dir non-quarantine, AI
stale-write rejection end-to-end (user edit mid-generation survives) and the
clean-apply path. 240 unit tests pass; startup smoke and sample-artifact
E2E pass.
Co-Authored-By: Claude Fable 5 <[email protected]>
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Phase 1 of the improvement plan, PR 4. Mirrors GitHub PR Twest2/StepForge#9.
Verified: 240 unit tests incl. new revision/quarantine/AI-stale suites; startup smoke + sample artifacts E2E pass. No migration needed; rollback-safe.
Phase 1 of the improvement plan (PR 4 of the sequence): stop concurrent whole-object saves from losing edits, stop failed saves from reporting clean, and stop corrupt user data from silently vanishing. Revisions (core/schema.js, core/store.js): - Every guide and step carries a monotonic `revision`, bumped on each store write. Legacy v1 data without the field reads as revision 0 and upgrades on its next save — no migration pass, no data rewrite. - saveGuide/saveStep accept { expectedRevision } for compare-and-swap saves; a mismatch throws RevisionConflictError instead of clobbering. Direct user edits pass no expectation (the user is the authority); background writers must pass one. Stale AI responses (app/text-intel.js): - generateStepPatch snapshots the step revision before the (slow) model call, re-reads the step after it, and saves with the original expectedRevision. A user edit made during generation now surfaces as "the step changed while AI was generating; nothing was overwritten" — previously the AI response silently overwrote the newer edit. Autosave truthfulness (app/renderer/editor.js): - flushStep/flushGuide cleared the dirty flag BEFORE awaiting the IPC save, so a rejected save (invoked via a debounce that never handled rejections) lost the visible dirty state. The flag is now cleared only after a durable save; failures keep it dirty, surface a persistent saveError in editor meta, toast the user, and retry on the next edit or explicit save. - Navigating away from the editor flushes pending debounced saves so the last edit can never be dropped by a view switch. Corruption quarantine (core/store.js): - listGuides/listSteps used to silently skip unreadable entries — a corrupt guide just vanished from the library. Corrupt guide/step directories are now moved to library/quarantine (original bytes preserved) and recorded in a recovery report (store.getRecoveryReport()) for the UI. Empty in-progress directories are still skipped quietly — absence of guide.json is not corruption. Tests: revision increments, stale-CAS rejection with user edit surviving, CAS success path, guide CAS, v1 no-revision upgrade, guide/step quarantine with preserved bytes + recovery report, empty-dir non-quarantine, AI stale-write rejection end-to-end (user edit mid-generation survives) and the clean-apply path. 240 unit tests pass; startup smoke and sample-artifact E2E pass. Co-Authored-By: Claude Fable 5 <[email protected]>Merged into main as
f31f140(same-SHA merge to both forges; GitHub mirror PR Twest2/StepForge#9 MERGED, CI green ubuntu+windows). Closing.Pull request closed