Vibecode Dabble
track this build4 phases, 12 steps, beginner friendly0%The core loop is buildable, but a dependable replacement becomes a real weekend or multi-day project. For Dabble, plan chapters, draft a manuscript, track plot threads, and sync a structured writing project. The hard boundary is cloud sync, mobile access, goals, and polished manuscript organization, plus polish, integrations, and proprietary language models.
You are building a lean indie version of Dabble. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== README.md ===== # Dabble · indie build A novel workspace whose hard problems are that the manuscript must survive the application, that plot threads have to stay attached to scenes through reordering and splitting, and that an export has to satisfy a specification before anybody will sell it. Estimated effort: **multi-day**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Runtime | Tauri with a React front end and a small Rust file layer | the words live on your disk and the application is a view onto them, not their container | | Storage | One plain text file per scene with front matter, and SQLite as a rebuildable index | if the application dies tomorrow the manuscript is still a folder of readable files | | Writes | Write to a temporary file, verify it reads back, then replace, snapshotting first | a crash mid-save on a ninety thousand word manuscript is the failure that ends writing projects | | Identity | Stable scene identifiers in the front matter that survive rename, reorder, split and merge | plot threads hang off scenes, and a thread that has lost its scene is worse than no thread | | Export | A compiled manuscript plus a book file carrying the metadata its specification requires | an export a shop rejects is not an export | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **A Rust toolchain and the Tauri prerequisites for your platform** · free - Why: The file layer is where atomic writing and snapshotting live, and it is the part that must not be approximate. - Get it: rustup, then the platform prerequisites listed by the Tauri project. - Verify: A blank application builds and runs on your machine - [ ] **Node.js 22 or newer** · free - Why: Builds the front end and runs the test suite. - Get it: nodejs.org or your version manager. - Verify: node --version prints v22 or higher - [ ] **A validator for the book format you export** · free - Why: Without it the export phase is guesswork, and the file will be rejected by somebody else's upload form instead of by your own build. - Get it: Use the validation tool published for the format and pin its version in the repository. - Verify: A deliberately broken export fails and a good one passes - [ ] **A real manuscript of at least fifty thousand words** · free - Why: Every problem in this build is a problem of scale: reordering, orphaned beats, counting disagreements and export timing all look fine on a ten scene fixture. - Get it: Use your own work in progress, or a public domain novel split into scene files. - Verify: Index rebuild, grid render and export all complete within a stated time - [ ] **Your word counting rule, written down first** · free - Why: Deciding it after implementation means the rule is whatever the code happened to do, and the number will disagree with a submission form for reasons nobody can explain. - Get it: Write down how hyphenated compounds, contractions, numerals and dialogue punctuation are treated, then implement that. - Verify: A fixture counts exactly as the document says it should ## Quick start Follow Phase 1 in `BUILD_PLAN.md`; it creates the project. Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Cloud sync and mobile access. - Collaborative editing. - A style or grammar model of your own. - Browser and office suite extensions. - cloud sync, mobile access, goals, and polished manuscript organization - cross-device sync - large proprietary style models - browser and office-suite extensions - team style guides If one of those is essential to you, that is the reason to keep paying for Dabble, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · Dabble The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a local long-form manuscript workspace that keeps your words in plain files and exports a book file a shop will accept to replace Dabble. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Stack (fixed, do not substitute) - Runtime: Tauri with a React front end and a small Rust file layer · the words live on your disk and the application is a view onto them, not their container - Storage: One plain text file per scene with front matter, and SQLite as a rebuildable index · if the application dies tomorrow the manuscript is still a folder of readable files - Writes: Write to a temporary file, verify it reads back, then replace, snapshotting first · a crash mid-save on a ninety thousand word manuscript is the failure that ends writing projects - Identity: Stable scene identifiers in the front matter that survive rename, reorder, split and merge · plot threads hang off scenes, and a thread that has lost its scene is worse than no thread - Export: A compiled manuscript plus a book file carrying the metadata its specification requires · an export a shop rejects is not an export ### Data model (create this before Phase 1) - Scene files on disk with front matter(id, title, pov, status, updated_at) · the file is the record and everything else is derived from it - scene_index(id, path, ordinal, chapter_id, title, words, hash, indexed_at) · rebuilt by rescanning the folder, and safe to delete at any time - snapshots(id, scene_id, at, path, bytes, reason) · taken before every replace and on a timer, with a stated retention - threads(id, name, colour, notes) and beats(id, thread_id, scene_id, ordinal, note) · the plot grid, keyed on scene identifier and never on position - characters(id, name, notes, first_scene_id) and places(id, name, notes) · the continuity notes that a long manuscript cannot hold in one head - sessions(id, started_at, ended_at, words_start, words_end) · derived from snapshots rather than from a counter that only goes up - export_runs(id, at, target, output_path, validator_output, state) · including the failures, which are the ones worth reading - The folder of scene files is the manuscript. Delete the database and everything but the plot grid rebuilds from the files; delete the files and the database is a list of things that no longer exist. ### Phase 1 · Files first, index second Build: Store one scene per plain text file with front matter carrying a stable identifier. Write to a temporary file, read it back to verify, then replace. Build the index by scanning the folder and ship a rebuild command. Match files by identifier rather than by path. Trap: Keeping the manuscript inside a database with an export button means that a failed migration, or a crash during a write, leaves the whole book inside one binary file that no other program can open, and this is the failure that ends writing projects rather than merely annoying people. Put the words in plain files, verify the replacement reads back before it becomes the scene, and make the index something you can delete and rebuild on a Tuesday. Done when: Deleting the index and rebuilding it reproduces the project except for data only the index holds, tested. A crash injected mid-write leaves the previous scene file intact and readable, tested by killing the process during a save. Every scene file opens in a plain text editor with its identifier visible in the front matter. A scene renamed outside the application is matched by its identifier rather than lost, tested. Do not build yet: No snapshots, plot grid, word counting or export yet. ### Phase 2 · Snapshots, and a way back to Tuesday Build: Snapshot a scene to disk before every replace and on a timer while editing. Keep snapshots with a retention by both count and age that never discards the most recent. Provide a word-level diff between any two snapshots and a restore that itself takes a snapshot first. Trap: An undo stack that lives in memory disappears the moment the application restarts, which is exactly when a writer wants back the paragraph they cut before lunch, and an editor that cannot return it gets replaced by one that can. Snapshot to disk before every replace and on a timer, keep them under a stated retention, and put the restore in the test suite rather than in the documentation. Done when: Restoring a snapshot from a stated number of days ago reproduces that scene byte for byte, exercised in the suite rather than described. Snapshots are taken before every replace and the count is asserted after a scripted editing session. Retention deletes by count and by age and never removes the most recent snapshot of a scene, tested at both boundaries. A restore takes its own snapshot first, so restoring the wrong version is recoverable. Do not build yet: No plot grid, counting or export yet. ### Phase 3 · A plot grid that survives a reorder Build: Model threads and beats keyed on the scene identifier. Define explicitly what splitting a scene does to its beats and what merging two scenes does. Render a grid of threads against scenes. Report beats whose scene has gone and offer to reattach or remove them. Trap: Attaching plot beats to a scene's position in the manuscript means the first structural edit silently invalidates the whole grid, and because it is silent nobody notices until the rewrite, by which point the grid is a picture of a book that no longer exists. Key beats on the scene identifier, decide and document the split and merge rules, and run an orphan report every time the index rebuilds. Done when: Reordering chapters leaves every beat attached to its original scene, tested against a fixture project. Splitting a scene applies a stated rule to its beats and the rule is in the README, tested. The orphan report lists beats whose scene no longer exists and offers reattach or remove, tested by deleting a scene file. The grid renders a project of a hundred and twenty scenes without a visible pause, measured and recorded. Do not build yet: No word counting, goals or export yet. ### Phase 4 · Counting words honestly Build: Write down exactly what the counter counts and implement that. Derive a session total from the difference between snapshots rather than from a counter that only increases. Make targets optional and hideable. Do not build streaks, notifications or any copy that reacts to a missed day. Trap: Every tool counts differently, so a manuscript reading ninety thousand in your editor and eighty seven in a submission form makes the writer distrust both numbers, and a daily total incremented as you type also counts the thousand words you deleted an hour later. Document what the counter counts, derive session figures from the difference between snapshots so a deletion shows as a deletion, and leave goals switched off by default. Done when: The counting rule is documented and a fixture containing hyphenated compounds, contractions, numerals and dialogue punctuation counts exactly as the rule states, tested. A session that ends with fewer words than it started shows a negative figure rather than zero, tested. Goals can be hidden entirely and no interface element reacts to a missed day, verified by review. The manuscript total equals the sum of its scene counts exactly. Do not build yet: No compiling or export yet. ### Phase 5 · An export a shop will accept Build: Compile scenes into a manuscript file and a book file in order. Populate the accessibility metadata the current specification makes mandatory. Run a validator and fail the export rather than writing a file that does not pass. Store the validator output on the export run. Reimport a compiled manuscript and confirm no scene was lost. Trap: The current EPUB accessibility specification, a recommendation dated 17 October 2024, states that all publications 'MUST include accessibility metadata in the package document that exposes their accessible properties', naming access mode, accessibility feature and accessibility hazard as required and accessibility summary and access mode sufficient as recommended. Separately the European accessibility rules have applied since 28 June 2025 and treat e-books and their reading software as a service, with an exemption for microenterprises providing services, defined as fewer than ten people and turnover or balance sheet total not above two million euro. Checked on 15 September 2026. So an export with no accessibility metadata may simply be unsellable in that market, and whether the exemption applies is a fact about the seller rather than about the file. Done when: Every book export contains the mandatory accessibility metadata fields and a test asserts each is present and non-empty. The export runs a validator and fails rather than writing a file that does not pass, tested with a deliberately broken build. A compiled manuscript reimports with every scene present and its identifier intact, tested. The export run stores the validator output, successes and failures alike, and the failures are readable in the interface. Do not build yet: Nothing after this. ### Out of scope (and why) - Cloud sync and mobile access, which need a conflict model for a document two devices edited offline and are a larger project than the editor. - Collaborative editing, which is a different data structure entirely and not something to bolt onto a folder of files. - A style or grammar model of your own, which is a claim about language quality that a weekend build cannot support honestly. - Browser and office suite extensions, which are per-platform review processes and a permanent maintenance commitment. ### README must contain - That the folder of scene files is the manuscript and the database is an index you can delete and rebuild - That writes go to a temporary file, are verified readable, and only then replace the scene, with the crash test named - That plot beats are keyed on scene identifiers, and the documented rules for splitting and merging a scene - Exactly what the word counter counts, and that session totals come from snapshot differences so deletions show as deletions - Which accessibility metadata the book export is required to carry, quoted from the specification with the date checked, and that the validator failing blocks the export ===== AGENTS.md ===== # Agent instructions · Dabble indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Tauri with a React front end and a small Rust file layer, One plain text file per scene with front matter, and SQLite as a rebuildable index, Write to a temporary file, verify it reads back, then replace, snapshotting first, Stable scene identifiers in the front matter that survive rename, reorder, split and merge, A compiled manuscript plus a book file carrying the metadata its specification requires. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Keeping the manuscript inside a database with an export button means that a failed migration, or a crash during a write, leaves the whole book inside one binary file that no other program can open, and this is the failure that ends writing projects rather than merely annoying people. Put the words in plain files, verify the replacement reads back before it becomes the scene, and make the index something you can delete and rebuild on a Tuesday. - An undo stack that lives in memory disappears the moment the application restarts, which is exactly when a writer wants back the paragraph they cut before lunch, and an editor that cannot return it gets replaced by one that can. Snapshot to disk before every replace and on a timer, keep them under a stated retention, and put the restore in the test suite rather than in the documentation. - Attaching plot beats to a scene's position in the manuscript means the first structural edit silently invalidates the whole grid, and because it is silent nobody notices until the rewrite, by which point the grid is a picture of a book that no longer exists. Key beats on the scene identifier, decide and document the split and merge rules, and run an orphan report every time the index rebuilds. - Every tool counts differently, so a manuscript reading ninety thousand in your editor and eighty seven in a submission form makes the writer distrust both numbers, and a daily total incremented as you type also counts the thousand words you deleted an hour later. Document what the counter counts, derive session figures from the difference between snapshots so a deletion shows as a deletion, and leave goals switched off by default. ===== BUILD_PLAN.md ===== # Build plan · Dabble A novel workspace whose hard problems are that the manuscript must survive the application, that plot threads have to stay attached to scenes through reordering and splitting, and that an export has to satisfy a specification before anybody will sell it. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Files first, index second The manuscript is a folder of readable files and the database can be thrown away. ### Steps 1. One scene, one plain file, one stable identifier The identifier is what everything else will hang from. Files: `src-tauri/src/scenes.rs`, `src/scenes/frontmatter.ts` 2. Verify the replacement before it becomes the scene Reading it back is the cheap insurance against a truncated write. Files: `src-tauri/src/atomic.rs`, `tests/crash.test.ts` 3. Make the index rebuildable and prove it An index you are afraid to delete is a second copy of the truth. Files: `src/index/rebuild.ts` ### Done when - [ ] Rebuilding the index reproduces the project - [ ] A crash mid-write leaves the previous file intact - [ ] Scene files open in a plain text editor - [ ] A file renamed outside the app is matched by identifier ### Watch out - Keeping the manuscript inside a database with an export button means that a failed migration, or a crash during a write, leaves the whole book inside one binary file that no other program can open, and this is the failure that ends writing projects rather than merely annoying people. Put the words in plain files, verify the replacement reads back before it becomes the scene, and make the index something you can delete and rebuild on a Tuesday. ## Phase 2 · Snapshots, and a way back to Tuesday Any scene can be restored to what it said several days ago, byte for byte. ### Steps 1. Snapshot before every replace, not after The version you want back is the one that existed before the save. Files: `src/snapshots/take.ts` 2. Retain by count and by age, never dropping the newest Both rules, because either alone has a bad day. Files: `src/snapshots/retain.ts`, `tests/retention.test.ts` 3. Make restore itself snapshot first Restoring the wrong version should not be the end of it. Files: `src/snapshots/restore.ts`, `tests/restore.test.ts` ### Done when - [ ] A days-old snapshot restores byte for byte - [ ] Snapshots are taken before every replace - [ ] Retention never removes the newest snapshot - [ ] Restore takes its own snapshot first ### Watch out - An undo stack that lives in memory disappears the moment the application restarts, which is exactly when a writer wants back the paragraph they cut before lunch, and an editor that cannot return it gets replaced by one that can. Snapshot to disk before every replace and on a timer, keep them under a stated retention, and put the restore in the test suite rather than in the documentation. ## Phase 3 · A plot grid that survives a reorder Moving chapter twelve to chapter four leaves every beat where it belongs. ### Steps 1. Key beats on the identifier, never on the ordinal Position is the one property that changes constantly. Files: `src/plot/beats.ts`, `tests/reorder.test.ts` 2. Decide split and merge behaviour and write it down Doing it by instinct in the moment gives a different answer each time. Files: `src/plot/split.ts`, `README.md` 3. Run the orphan report on every rebuild A silently detached beat is worse than a missing one. Files: `src/plot/orphans.ts` ### Done when - [ ] A reorder leaves beats on their scenes - [ ] Split follows a documented rule - [ ] Orphaned beats are reported and fixable - [ ] The grid renders a large project without a pause ### Watch out - Attaching plot beats to a scene's position in the manuscript means the first structural edit silently invalidates the whole grid, and because it is silent nobody notices until the rewrite, by which point the grid is a picture of a book that no longer exists. Key beats on the scene identifier, decide and document the split and merge rules, and run an orphan report every time the index rebuilds. ## Phase 4 · Counting words honestly One documented counting rule, session totals derived from snapshots, and nothing that shames. ### Steps 1. Document the counting rule before implementing it Otherwise the rule is whatever the code happened to do. Files: `docs/word-counting.md`, `src/count.ts` 2. Derive sessions from snapshot differences So a day spent cutting reads as a day spent cutting. Files: `src/sessions.ts`, `tests/sessions.test.ts` 3. Ship goals off by default and no streaks at all A writing tool that nags is a writing tool people avoid opening. Files: `src/goals.ts` ### Done when - [ ] The fixture counts exactly as documented - [ ] A net deletion shows as a negative session - [ ] Goals hide completely and nothing nags - [ ] The manuscript total equals the sum of its scenes ### Watch out - Every tool counts differently, so a manuscript reading ninety thousand in your editor and eighty seven in a submission form makes the writer distrust both numbers, and a daily total incremented as you type also counts the thousand words you deleted an hour later. Document what the counter counts, derive session figures from the difference between snapshots so a deletion shows as a deletion, and leave goals switched off by default. ## Not in this build - Cloud sync and mobile access. - Collaborative editing. - A style or grammar model of your own. - Browser and office suite extensions. ## After v1, if you want it - A read-only reading mode that renders the compiled manuscript, which is how continuity errors actually surface - Scene status tracking through a revision pass, which is a column in the grid rather than a new screen - An export target for a submission format with its own layout rules, built behind the same validate-then-write path ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. The folder of scene files. This is the manuscript; everything else is derived. PROJECT_DIR=~/Writing/novel # Required. The rebuildable index. Safe to delete at any time. INDEX_PATH=~/Writing/novel/.index.db # Required. Versions before every replace. Back this up with the manuscript. SNAPSHOT_DIR=~/Writing/novel/.snapshots # Required. Per scene, alongside the age rule. Neither ever removes the newest. SNAPSHOT_KEEP=200 # Required. The age half of the retention rule. SNAPSHOT_MAX_AGE_DAYS=180 # Required. How much typing an unexpected shutdown can cost. AUTOSAVE_SECONDS=20 # Optional · secret. Optional rewrite of a selected passage only. The application works entirely without it. MODEL_API_KEY=sk-...redacted # Required. Compiled manuscripts and book files, with the validator output beside each. EXPORT_DIR=~/Writing/exports
You are building a lean indie version of Dabble. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== README.md ===== # Dabble · indie build A novel workspace whose hard problems are that the manuscript must survive the application, that plot threads have to stay attached to scenes through reordering and splitting, and that an export has to satisfy a specification before anybody will sell it. Estimated effort: **multi-day**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Runtime | Tauri with a React front end and a small Rust file layer | the words live on your disk and the application is a view onto them, not their container | | Storage | One plain text file per scene with front matter, and SQLite as a rebuildable index | if the application dies tomorrow the manuscript is still a folder of readable files | | Writes | Write to a temporary file, verify it reads back, then replace, snapshotting first | a crash mid-save on a ninety thousand word manuscript is the failure that ends writing projects | | Identity | Stable scene identifiers in the front matter that survive rename, reorder, split and merge | plot threads hang off scenes, and a thread that has lost its scene is worse than no thread | | Export | A compiled manuscript plus a book file carrying the metadata its specification requires | an export a shop rejects is not an export | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **A Rust toolchain and the Tauri prerequisites for your platform** · free - Why: The file layer is where atomic writing and snapshotting live, and it is the part that must not be approximate. - Get it: rustup, then the platform prerequisites listed by the Tauri project. - Verify: A blank application builds and runs on your machine - [ ] **Node.js 22 or newer** · free - Why: Builds the front end and runs the test suite. - Get it: nodejs.org or your version manager. - Verify: node --version prints v22 or higher - [ ] **A validator for the book format you export** · free - Why: Without it the export phase is guesswork, and the file will be rejected by somebody else's upload form instead of by your own build. - Get it: Use the validation tool published for the format and pin its version in the repository. - Verify: A deliberately broken export fails and a good one passes - [ ] **A real manuscript of at least fifty thousand words** · free - Why: Every problem in this build is a problem of scale: reordering, orphaned beats, counting disagreements and export timing all look fine on a ten scene fixture. - Get it: Use your own work in progress, or a public domain novel split into scene files. - Verify: Index rebuild, grid render and export all complete within a stated time - [ ] **Your word counting rule, written down first** · free - Why: Deciding it after implementation means the rule is whatever the code happened to do, and the number will disagree with a submission form for reasons nobody can explain. - Get it: Write down how hyphenated compounds, contractions, numerals and dialogue punctuation are treated, then implement that. - Verify: A fixture counts exactly as the document says it should ## Quick start Follow Phase 1 in `BUILD_PLAN.md`; it creates the project. Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Cloud sync and mobile access. - Collaborative editing. - A style or grammar model of your own. - Browser and office suite extensions. - cloud sync, mobile access, goals, and polished manuscript organization - cross-device sync - large proprietary style models - browser and office-suite extensions - team style guides If one of those is essential to you, that is the reason to keep paying for Dabble, and the README should say so rather than pretend. ===== BRIEF.md ===== # Build brief · Dabble The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a local long-form manuscript workspace that keeps your words in plain files and exports a book file a shop will accept to replace Dabble. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Stack (fixed, do not substitute) - Runtime: Tauri with a React front end and a small Rust file layer · the words live on your disk and the application is a view onto them, not their container - Storage: One plain text file per scene with front matter, and SQLite as a rebuildable index · if the application dies tomorrow the manuscript is still a folder of readable files - Writes: Write to a temporary file, verify it reads back, then replace, snapshotting first · a crash mid-save on a ninety thousand word manuscript is the failure that ends writing projects - Identity: Stable scene identifiers in the front matter that survive rename, reorder, split and merge · plot threads hang off scenes, and a thread that has lost its scene is worse than no thread - Export: A compiled manuscript plus a book file carrying the metadata its specification requires · an export a shop rejects is not an export ### Data model (create this before Phase 1) - Scene files on disk with front matter(id, title, pov, status, updated_at) · the file is the record and everything else is derived from it - scene_index(id, path, ordinal, chapter_id, title, words, hash, indexed_at) · rebuilt by rescanning the folder, and safe to delete at any time - snapshots(id, scene_id, at, path, bytes, reason) · taken before every replace and on a timer, with a stated retention - threads(id, name, colour, notes) and beats(id, thread_id, scene_id, ordinal, note) · the plot grid, keyed on scene identifier and never on position - characters(id, name, notes, first_scene_id) and places(id, name, notes) · the continuity notes that a long manuscript cannot hold in one head - sessions(id, started_at, ended_at, words_start, words_end) · derived from snapshots rather than from a counter that only goes up - export_runs(id, at, target, output_path, validator_output, state) · including the failures, which are the ones worth reading - The folder of scene files is the manuscript. Delete the database and everything but the plot grid rebuilds from the files; delete the files and the database is a list of things that no longer exist. ### Phase 1 · Files first, index second Build: Store one scene per plain text file with front matter carrying a stable identifier. Write to a temporary file, read it back to verify, then replace. Build the index by scanning the folder and ship a rebuild command. Match files by identifier rather than by path. Trap: Keeping the manuscript inside a database with an export button means that a failed migration, or a crash during a write, leaves the whole book inside one binary file that no other program can open, and this is the failure that ends writing projects rather than merely annoying people. Put the words in plain files, verify the replacement reads back before it becomes the scene, and make the index something you can delete and rebuild on a Tuesday. Done when: Deleting the index and rebuilding it reproduces the project except for data only the index holds, tested. A crash injected mid-write leaves the previous scene file intact and readable, tested by killing the process during a save. Every scene file opens in a plain text editor with its identifier visible in the front matter. A scene renamed outside the application is matched by its identifier rather than lost, tested. Do not build yet: No snapshots, plot grid, word counting or export yet. ### Phase 2 · Snapshots, and a way back to Tuesday Build: Snapshot a scene to disk before every replace and on a timer while editing. Keep snapshots with a retention by both count and age that never discards the most recent. Provide a word-level diff between any two snapshots and a restore that itself takes a snapshot first. Trap: An undo stack that lives in memory disappears the moment the application restarts, which is exactly when a writer wants back the paragraph they cut before lunch, and an editor that cannot return it gets replaced by one that can. Snapshot to disk before every replace and on a timer, keep them under a stated retention, and put the restore in the test suite rather than in the documentation. Done when: Restoring a snapshot from a stated number of days ago reproduces that scene byte for byte, exercised in the suite rather than described. Snapshots are taken before every replace and the count is asserted after a scripted editing session. Retention deletes by count and by age and never removes the most recent snapshot of a scene, tested at both boundaries. A restore takes its own snapshot first, so restoring the wrong version is recoverable. Do not build yet: No plot grid, counting or export yet. ### Phase 3 · A plot grid that survives a reorder Build: Model threads and beats keyed on the scene identifier. Define explicitly what splitting a scene does to its beats and what merging two scenes does. Render a grid of threads against scenes. Report beats whose scene has gone and offer to reattach or remove them. Trap: Attaching plot beats to a scene's position in the manuscript means the first structural edit silently invalidates the whole grid, and because it is silent nobody notices until the rewrite, by which point the grid is a picture of a book that no longer exists. Key beats on the scene identifier, decide and document the split and merge rules, and run an orphan report every time the index rebuilds. Done when: Reordering chapters leaves every beat attached to its original scene, tested against a fixture project. Splitting a scene applies a stated rule to its beats and the rule is in the README, tested. The orphan report lists beats whose scene no longer exists and offers reattach or remove, tested by deleting a scene file. The grid renders a project of a hundred and twenty scenes without a visible pause, measured and recorded. Do not build yet: No word counting, goals or export yet. ### Phase 4 · Counting words honestly Build: Write down exactly what the counter counts and implement that. Derive a session total from the difference between snapshots rather than from a counter that only increases. Make targets optional and hideable. Do not build streaks, notifications or any copy that reacts to a missed day. Trap: Every tool counts differently, so a manuscript reading ninety thousand in your editor and eighty seven in a submission form makes the writer distrust both numbers, and a daily total incremented as you type also counts the thousand words you deleted an hour later. Document what the counter counts, derive session figures from the difference between snapshots so a deletion shows as a deletion, and leave goals switched off by default. Done when: The counting rule is documented and a fixture containing hyphenated compounds, contractions, numerals and dialogue punctuation counts exactly as the rule states, tested. A session that ends with fewer words than it started shows a negative figure rather than zero, tested. Goals can be hidden entirely and no interface element reacts to a missed day, verified by review. The manuscript total equals the sum of its scene counts exactly. Do not build yet: No compiling or export yet. ### Phase 5 · An export a shop will accept Build: Compile scenes into a manuscript file and a book file in order. Populate the accessibility metadata the current specification makes mandatory. Run a validator and fail the export rather than writing a file that does not pass. Store the validator output on the export run. Reimport a compiled manuscript and confirm no scene was lost. Trap: The current EPUB accessibility specification, a recommendation dated 17 October 2024, states that all publications 'MUST include accessibility metadata in the package document that exposes their accessible properties', naming access mode, accessibility feature and accessibility hazard as required and accessibility summary and access mode sufficient as recommended. Separately the European accessibility rules have applied since 28 June 2025 and treat e-books and their reading software as a service, with an exemption for microenterprises providing services, defined as fewer than ten people and turnover or balance sheet total not above two million euro. Checked on 15 September 2026. So an export with no accessibility metadata may simply be unsellable in that market, and whether the exemption applies is a fact about the seller rather than about the file. Done when: Every book export contains the mandatory accessibility metadata fields and a test asserts each is present and non-empty. The export runs a validator and fails rather than writing a file that does not pass, tested with a deliberately broken build. A compiled manuscript reimports with every scene present and its identifier intact, tested. The export run stores the validator output, successes and failures alike, and the failures are readable in the interface. Do not build yet: Nothing after this. ### Out of scope (and why) - Cloud sync and mobile access, which need a conflict model for a document two devices edited offline and are a larger project than the editor. - Collaborative editing, which is a different data structure entirely and not something to bolt onto a folder of files. - A style or grammar model of your own, which is a claim about language quality that a weekend build cannot support honestly. - Browser and office suite extensions, which are per-platform review processes and a permanent maintenance commitment. ### README must contain - That the folder of scene files is the manuscript and the database is an index you can delete and rebuild - That writes go to a temporary file, are verified readable, and only then replace the scene, with the crash test named - That plot beats are keyed on scene identifiers, and the documented rules for splitting and merging a scene - Exactly what the word counter counts, and that session totals come from snapshot differences so deletions show as deletions - Which accessibility metadata the book export is required to carry, quoted from the specification with the date checked, and that the validator failing blocks the export ===== AGENTS.md ===== # Agent instructions · Dabble indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Tauri with a React front end and a small Rust file layer, One plain text file per scene with front matter, and SQLite as a rebuildable index, Write to a temporary file, verify it reads back, then replace, snapshotting first, Stable scene identifiers in the front matter that survive rename, reorder, split and merge, A compiled manuscript plus a book file carrying the metadata its specification requires. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Keeping the manuscript inside a database with an export button means that a failed migration, or a crash during a write, leaves the whole book inside one binary file that no other program can open, and this is the failure that ends writing projects rather than merely annoying people. Put the words in plain files, verify the replacement reads back before it becomes the scene, and make the index something you can delete and rebuild on a Tuesday. - An undo stack that lives in memory disappears the moment the application restarts, which is exactly when a writer wants back the paragraph they cut before lunch, and an editor that cannot return it gets replaced by one that can. Snapshot to disk before every replace and on a timer, keep them under a stated retention, and put the restore in the test suite rather than in the documentation. - Attaching plot beats to a scene's position in the manuscript means the first structural edit silently invalidates the whole grid, and because it is silent nobody notices until the rewrite, by which point the grid is a picture of a book that no longer exists. Key beats on the scene identifier, decide and document the split and merge rules, and run an orphan report every time the index rebuilds. - Every tool counts differently, so a manuscript reading ninety thousand in your editor and eighty seven in a submission form makes the writer distrust both numbers, and a daily total incremented as you type also counts the thousand words you deleted an hour later. Document what the counter counts, derive session figures from the difference between snapshots so a deletion shows as a deletion, and leave goals switched off by default. ===== BUILD_PLAN.md ===== # Build plan · Dabble A novel workspace whose hard problems are that the manuscript must survive the application, that plot threads have to stay attached to scenes through reordering and splitting, and that an export has to satisfy a specification before anybody will sell it. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Files first, index second The manuscript is a folder of readable files and the database can be thrown away. ### Steps 1. One scene, one plain file, one stable identifier The identifier is what everything else will hang from. Files: `src-tauri/src/scenes.rs`, `src/scenes/frontmatter.ts` 2. Verify the replacement before it becomes the scene Reading it back is the cheap insurance against a truncated write. Files: `src-tauri/src/atomic.rs`, `tests/crash.test.ts` 3. Make the index rebuildable and prove it An index you are afraid to delete is a second copy of the truth. Files: `src/index/rebuild.ts` ### Done when - [ ] Rebuilding the index reproduces the project - [ ] A crash mid-write leaves the previous file intact - [ ] Scene files open in a plain text editor - [ ] A file renamed outside the app is matched by identifier ### Watch out - Keeping the manuscript inside a database with an export button means that a failed migration, or a crash during a write, leaves the whole book inside one binary file that no other program can open, and this is the failure that ends writing projects rather than merely annoying people. Put the words in plain files, verify the replacement reads back before it becomes the scene, and make the index something you can delete and rebuild on a Tuesday. ## Phase 2 · Snapshots, and a way back to Tuesday Any scene can be restored to what it said several days ago, byte for byte. ### Steps 1. Snapshot before every replace, not after The version you want back is the one that existed before the save. Files: `src/snapshots/take.ts` 2. Retain by count and by age, never dropping the newest Both rules, because either alone has a bad day. Files: `src/snapshots/retain.ts`, `tests/retention.test.ts` 3. Make restore itself snapshot first Restoring the wrong version should not be the end of it. Files: `src/snapshots/restore.ts`, `tests/restore.test.ts` ### Done when - [ ] A days-old snapshot restores byte for byte - [ ] Snapshots are taken before every replace - [ ] Retention never removes the newest snapshot - [ ] Restore takes its own snapshot first ### Watch out - An undo stack that lives in memory disappears the moment the application restarts, which is exactly when a writer wants back the paragraph they cut before lunch, and an editor that cannot return it gets replaced by one that can. Snapshot to disk before every replace and on a timer, keep them under a stated retention, and put the restore in the test suite rather than in the documentation. ## Phase 3 · A plot grid that survives a reorder Moving chapter twelve to chapter four leaves every beat where it belongs. ### Steps 1. Key beats on the identifier, never on the ordinal Position is the one property that changes constantly. Files: `src/plot/beats.ts`, `tests/reorder.test.ts` 2. Decide split and merge behaviour and write it down Doing it by instinct in the moment gives a different answer each time. Files: `src/plot/split.ts`, `README.md` 3. Run the orphan report on every rebuild A silently detached beat is worse than a missing one. Files: `src/plot/orphans.ts` ### Done when - [ ] A reorder leaves beats on their scenes - [ ] Split follows a documented rule - [ ] Orphaned beats are reported and fixable - [ ] The grid renders a large project without a pause ### Watch out - Attaching plot beats to a scene's position in the manuscript means the first structural edit silently invalidates the whole grid, and because it is silent nobody notices until the rewrite, by which point the grid is a picture of a book that no longer exists. Key beats on the scene identifier, decide and document the split and merge rules, and run an orphan report every time the index rebuilds. ## Phase 4 · Counting words honestly One documented counting rule, session totals derived from snapshots, and nothing that shames. ### Steps 1. Document the counting rule before implementing it Otherwise the rule is whatever the code happened to do. Files: `docs/word-counting.md`, `src/count.ts` 2. Derive sessions from snapshot differences So a day spent cutting reads as a day spent cutting. Files: `src/sessions.ts`, `tests/sessions.test.ts` 3. Ship goals off by default and no streaks at all A writing tool that nags is a writing tool people avoid opening. Files: `src/goals.ts` ### Done when - [ ] The fixture counts exactly as documented - [ ] A net deletion shows as a negative session - [ ] Goals hide completely and nothing nags - [ ] The manuscript total equals the sum of its scenes ### Watch out - Every tool counts differently, so a manuscript reading ninety thousand in your editor and eighty seven in a submission form makes the writer distrust both numbers, and a daily total incremented as you type also counts the thousand words you deleted an hour later. Document what the counter counts, derive session figures from the difference between snapshots so a deletion shows as a deletion, and leave goals switched off by default. ## Not in this build - Cloud sync and mobile access. - Collaborative editing. - A style or grammar model of your own. - Browser and office suite extensions. ## After v1, if you want it - A read-only reading mode that renders the compiled manuscript, which is how continuity errors actually surface - Scene status tracking through a revision pass, which is a column in the grid rather than a new screen - An export target for a submission format with its own layout rules, built behind the same validate-then-write path ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. The folder of scene files. This is the manuscript; everything else is derived. PROJECT_DIR=~/Writing/novel # Required. The rebuildable index. Safe to delete at any time. INDEX_PATH=~/Writing/novel/.index.db # Required. Versions before every replace. Back this up with the manuscript. SNAPSHOT_DIR=~/Writing/novel/.snapshots # Required. Per scene, alongside the age rule. Neither ever removes the newest. SNAPSHOT_KEEP=200 # Required. The age half of the retention rule. SNAPSHOT_MAX_AGE_DAYS=180 # Required. How much typing an unexpected shutdown can cost. AUTOSAVE_SECONDS=20 # Optional · secret. Optional rewrite of a selected passage only. The application works entirely without it. MODEL_API_KEY=sk-...redacted # Required. Compiled manuscripts and book files, with the validator output beside each. EXPORT_DIR=~/Writing/exports
You are building a production product version of Dabble. Create the following project files first, then implement the application by following them. Keep the files updated as decisions change. Do not collapse this into a single README or prompt. ===== PRODUCT.md ===== # Dabble · product brief ## Problem The core loop is buildable, but a dependable replacement becomes a real weekend or multi-day project. For Dabble, plan chapters, draft a manuscript, track plot threads, and sync a structured writing project. The hard boundary is cloud sync, mobile access, goals, and polished manuscript organization, plus polish, integrations, and proprietary language models. ## Product outcome A workspace where the manuscript is a folder of plain files that outlives the application, any scene can be restored to what it said last week, the plot grid survives a structural edit, and the book export is blocked rather than shipped when it would be rejected. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - macOS, Windows, or Linux - local files - optional OpenAI or Anthropic API key for semantic rewrites ## Explicit non-goals for v1 - Cloud sync and mobile access. - Collaborative editing. - A style or grammar model of your own. - Browser and office suite extensions. - cloud sync, mobile access, goals, and polished manuscript organization - cross-device sync - large proprietary style models - browser and office-suite extensions - team style guides ## Success criteria - Rebuilding the index from files alone is confirmed to reproduce the project - A crash mid-write is confirmed to leave the previous scene intact - A chapter reorder is confirmed to leave every beat attached - The word counting fixture is confirmed to match the documented rule - An export failing validation is confirmed to write no file ===== BRIEF.md ===== # Build brief · Dabble The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a local long-form manuscript workspace that keeps your words in plain files and exports a book file a shop will accept to replace Dabble. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Stack (fixed, do not substitute) - Runtime: Tauri with a React front end and a small Rust file layer · the words live on your disk and the application is a view onto them, not their container - Storage: One plain text file per scene with front matter, and SQLite as a rebuildable index · if the application dies tomorrow the manuscript is still a folder of readable files - Writes: Write to a temporary file, verify it reads back, then replace, snapshotting first · a crash mid-save on a ninety thousand word manuscript is the failure that ends writing projects - Identity: Stable scene identifiers in the front matter that survive rename, reorder, split and merge · plot threads hang off scenes, and a thread that has lost its scene is worse than no thread - Export: A compiled manuscript plus a book file carrying the metadata its specification requires · an export a shop rejects is not an export ### Data model (create this before Phase 1) - Scene files on disk with front matter(id, title, pov, status, updated_at) · the file is the record and everything else is derived from it - scene_index(id, path, ordinal, chapter_id, title, words, hash, indexed_at) · rebuilt by rescanning the folder, and safe to delete at any time - snapshots(id, scene_id, at, path, bytes, reason) · taken before every replace and on a timer, with a stated retention - threads(id, name, colour, notes) and beats(id, thread_id, scene_id, ordinal, note) · the plot grid, keyed on scene identifier and never on position - characters(id, name, notes, first_scene_id) and places(id, name, notes) · the continuity notes that a long manuscript cannot hold in one head - sessions(id, started_at, ended_at, words_start, words_end) · derived from snapshots rather than from a counter that only goes up - export_runs(id, at, target, output_path, validator_output, state) · including the failures, which are the ones worth reading - The folder of scene files is the manuscript. Delete the database and everything but the plot grid rebuilds from the files; delete the files and the database is a list of things that no longer exist. ### Phase 1 · Files first, index second Build: Store one scene per plain text file with front matter carrying a stable identifier. Write to a temporary file, read it back to verify, then replace. Build the index by scanning the folder and ship a rebuild command. Match files by identifier rather than by path. Trap: Keeping the manuscript inside a database with an export button means that a failed migration, or a crash during a write, leaves the whole book inside one binary file that no other program can open, and this is the failure that ends writing projects rather than merely annoying people. Put the words in plain files, verify the replacement reads back before it becomes the scene, and make the index something you can delete and rebuild on a Tuesday. Done when: Deleting the index and rebuilding it reproduces the project except for data only the index holds, tested. A crash injected mid-write leaves the previous scene file intact and readable, tested by killing the process during a save. Every scene file opens in a plain text editor with its identifier visible in the front matter. A scene renamed outside the application is matched by its identifier rather than lost, tested. Do not build yet: No snapshots, plot grid, word counting or export yet. ### Phase 2 · Snapshots, and a way back to Tuesday Build: Snapshot a scene to disk before every replace and on a timer while editing. Keep snapshots with a retention by both count and age that never discards the most recent. Provide a word-level diff between any two snapshots and a restore that itself takes a snapshot first. Trap: An undo stack that lives in memory disappears the moment the application restarts, which is exactly when a writer wants back the paragraph they cut before lunch, and an editor that cannot return it gets replaced by one that can. Snapshot to disk before every replace and on a timer, keep them under a stated retention, and put the restore in the test suite rather than in the documentation. Done when: Restoring a snapshot from a stated number of days ago reproduces that scene byte for byte, exercised in the suite rather than described. Snapshots are taken before every replace and the count is asserted after a scripted editing session. Retention deletes by count and by age and never removes the most recent snapshot of a scene, tested at both boundaries. A restore takes its own snapshot first, so restoring the wrong version is recoverable. Do not build yet: No plot grid, counting or export yet. ### Phase 3 · A plot grid that survives a reorder Build: Model threads and beats keyed on the scene identifier. Define explicitly what splitting a scene does to its beats and what merging two scenes does. Render a grid of threads against scenes. Report beats whose scene has gone and offer to reattach or remove them. Trap: Attaching plot beats to a scene's position in the manuscript means the first structural edit silently invalidates the whole grid, and because it is silent nobody notices until the rewrite, by which point the grid is a picture of a book that no longer exists. Key beats on the scene identifier, decide and document the split and merge rules, and run an orphan report every time the index rebuilds. Done when: Reordering chapters leaves every beat attached to its original scene, tested against a fixture project. Splitting a scene applies a stated rule to its beats and the rule is in the README, tested. The orphan report lists beats whose scene no longer exists and offers reattach or remove, tested by deleting a scene file. The grid renders a project of a hundred and twenty scenes without a visible pause, measured and recorded. Do not build yet: No word counting, goals or export yet. ### Phase 4 · Counting words honestly Build: Write down exactly what the counter counts and implement that. Derive a session total from the difference between snapshots rather than from a counter that only increases. Make targets optional and hideable. Do not build streaks, notifications or any copy that reacts to a missed day. Trap: Every tool counts differently, so a manuscript reading ninety thousand in your editor and eighty seven in a submission form makes the writer distrust both numbers, and a daily total incremented as you type also counts the thousand words you deleted an hour later. Document what the counter counts, derive session figures from the difference between snapshots so a deletion shows as a deletion, and leave goals switched off by default. Done when: The counting rule is documented and a fixture containing hyphenated compounds, contractions, numerals and dialogue punctuation counts exactly as the rule states, tested. A session that ends with fewer words than it started shows a negative figure rather than zero, tested. Goals can be hidden entirely and no interface element reacts to a missed day, verified by review. The manuscript total equals the sum of its scene counts exactly. Do not build yet: No compiling or export yet. ### Phase 5 · An export a shop will accept Build: Compile scenes into a manuscript file and a book file in order. Populate the accessibility metadata the current specification makes mandatory. Run a validator and fail the export rather than writing a file that does not pass. Store the validator output on the export run. Reimport a compiled manuscript and confirm no scene was lost. Trap: The current EPUB accessibility specification, a recommendation dated 17 October 2024, states that all publications 'MUST include accessibility metadata in the package document that exposes their accessible properties', naming access mode, accessibility feature and accessibility hazard as required and accessibility summary and access mode sufficient as recommended. Separately the European accessibility rules have applied since 28 June 2025 and treat e-books and their reading software as a service, with an exemption for microenterprises providing services, defined as fewer than ten people and turnover or balance sheet total not above two million euro. Checked on 15 September 2026. So an export with no accessibility metadata may simply be unsellable in that market, and whether the exemption applies is a fact about the seller rather than about the file. Done when: Every book export contains the mandatory accessibility metadata fields and a test asserts each is present and non-empty. The export runs a validator and fails rather than writing a file that does not pass, tested with a deliberately broken build. A compiled manuscript reimports with every scene present and its identifier intact, tested. The export run stores the validator output, successes and failures alike, and the failures are readable in the interface. Do not build yet: Nothing after this. ### Out of scope (and why) - Cloud sync and mobile access, which need a conflict model for a document two devices edited offline and are a larger project than the editor. - Collaborative editing, which is a different data structure entirely and not something to bolt onto a folder of files. - A style or grammar model of your own, which is a claim about language quality that a weekend build cannot support honestly. - Browser and office suite extensions, which are per-platform review processes and a permanent maintenance commitment. ### README must contain - That the folder of scene files is the manuscript and the database is an index you can delete and rebuild - That writes go to a temporary file, are verified readable, and only then replace the scene, with the crash test named - That plot beats are keyed on scene identifiers, and the documented rules for splitting and merging a scene - Exactly what the word counter counts, and that session totals come from snapshot differences so deletions show as deletions - Which accessibility metadata the book export is required to carry, quoted from the specification with the date checked, and that the validator failing blocks the export ===== ARCHITECTURE.md ===== # Architecture · Dabble ## Stack | Part | Choice | Why | | --- | --- | --- | | Runtime | Tauri with a React front end and a small Rust file layer | the words live on your disk and the application is a view onto them, not their container | | Storage | One plain text file per scene with front matter, and SQLite as a rebuildable index | if the application dies tomorrow the manuscript is still a folder of readable files | | Writes | Write to a temporary file, verify it reads back, then replace, snapshotting first | a crash mid-save on a ninety thousand word manuscript is the failure that ends writing projects | | Identity | Stable scene identifiers in the front matter that survive rename, reorder, split and merge | plot threads hang off scenes, and a thread that has lost its scene is worse than no thread | | Export | A compiled manuscript plus a book file carrying the metadata its specification requires | an export a shop rejects is not an export | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Files | Scene files, front matter and atomic replacement. | Not swappable for a database of paragraphs, which is the failure this design avoids. | | Index | Ordinals, chapters and search. | Any store, provided it can be deleted and rebuilt from the files. | | Snapshots | Versions, retention and restore. | Any mechanism, provided the restore is exercised in the suite. | | Plot | Threads, beats and orphan reporting. | Any model keyed on scene identifiers rather than positions. | | Export | Compilation, metadata and validation. | Any format, provided an invalid file is never written. | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `PROJECT_DIR` · required · The folder of scene files. This is the manuscript; everything else is derived. - `INDEX_PATH` · required · The rebuildable index. Safe to delete at any time. - `SNAPSHOT_DIR` · required · Versions before every replace. Back this up with the manuscript. - `SNAPSHOT_KEEP` · required · Per scene, alongside the age rule. Neither ever removes the newest. - `SNAPSHOT_MAX_AGE_DAYS` · required · The age half of the retention rule. - `AUTOSAVE_SECONDS` · required · How much typing an unexpected shutdown can cost. - `MODEL_API_KEY` · optional, secret · Optional rewrite of a selected passage only. The application works entirely without it. - `EXPORT_DIR` · required · Compiled manuscripts and book files, with the validator output beside each. ## Production baseline - Security: least privilege, input validation at every boundary, secret redaction in logs, rate limits on abuse-prone paths, no invented security primitives. - Data: explicit schema and migrations, transactional writes where integrity matters, backup and restore procedures that have been exercised. - Integrations: adapters around third-party providers, idempotent webhook or job processing, bounded retries, timeouts. - Observability: structured logs with request or operation ids, an error-tracking hook, and health and readiness checks where a server exists. - Quality: unit tests for domain rules, integration tests at module boundaries, one end-to-end test of the critical path. ## Decision records For each dependency in the stack table, keep a short note: why it was chosen, its failure mode, and how it is replaced. Do not add infrastructure until a requirement in `PRODUCT.md` justifies it. ===== AGENTS.md ===== # Agent instructions · Dabble product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: Tauri with a React front end and a small Rust file layer, One plain text file per scene with front matter, and SQLite as a rebuildable index, Write to a temporary file, verify it reads back, then replace, snapshotting first, Stable scene identifiers in the front matter that survive rename, reorder, split and merge, A compiled manuscript plus a book file carrying the metadata its specification requires. - Implement milestone by milestone from `MILESTONES.md`; keep each change reviewable and leave the application runnable at every commit. - Treat authentication, payments, encryption, imports, webhooks and destructive actions as high-risk boundaries when present. - Never invent cryptography or silently weaken a requirement to make a check pass. - Put every external service behind an interface with a deterministic fake for tests. - Add migrations and rollback or recovery notes for every persistent data change. - Log useful operational context without credentials, tokens, passwords or personal data. - Update documentation and run every check before completing a milestone. ## Known traps - Keeping the manuscript inside a database with an export button means that a failed migration, or a crash during a write, leaves the whole book inside one binary file that no other program can open, and this is the failure that ends writing projects rather than merely annoying people. Put the words in plain files, verify the replacement reads back before it becomes the scene, and make the index something you can delete and rebuild on a Tuesday. - An undo stack that lives in memory disappears the moment the application restarts, which is exactly when a writer wants back the paragraph they cut before lunch, and an editor that cannot return it gets replaced by one that can. Snapshot to disk before every replace and on a timer, keep them under a stated retention, and put the restore in the test suite rather than in the documentation. - Attaching plot beats to a scene's position in the manuscript means the first structural edit silently invalidates the whole grid, and because it is silent nobody notices until the rewrite, by which point the grid is a picture of a book that no longer exists. Key beats on the scene identifier, decide and document the split and merge rules, and run an orphan report every time the index rebuilds. - Every tool counts differently, so a manuscript reading ninety thousand in your editor and eighty seven in a submission form makes the writer distrust both numbers, and a daily total incremented as you type also counts the thousand words you deleted an hour later. Document what the counter counts, derive session figures from the difference between snapshots so a deletion shows as a deletion, and leave goals switched off by default. - The current EPUB accessibility specification, a recommendation dated 17 October 2024, states that all publications 'MUST include accessibility metadata in the package document that exposes their accessible properties', naming access mode, accessibility feature and accessibility hazard as required and accessibility summary and access mode sufficient as recommended. Separately the European accessibility rules have applied since 28 June 2025 and treat e-books and their reading software as a service, with an exemption for microenterprises providing services, defined as fewer than ten people and turnover or balance sheet total not above two million euro. Checked on 15 September 2026. So an export with no accessibility metadata may simply be unsellable in that market, and whether the exemption applies is a fact about the seller rather than about the file. ===== MILESTONES.md ===== # Delivery milestones · Dabble Estimated effort: **multi-day** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · Files first, index second The manuscript is a folder of readable files and the database can be thrown away. ### Steps 1. One scene, one plain file, one stable identifier The identifier is what everything else will hang from. Files: `src-tauri/src/scenes.rs`, `src/scenes/frontmatter.ts` 2. Verify the replacement before it becomes the scene Reading it back is the cheap insurance against a truncated write. Files: `src-tauri/src/atomic.rs`, `tests/crash.test.ts` 3. Make the index rebuildable and prove it An index you are afraid to delete is a second copy of the truth. Files: `src/index/rebuild.ts` ### Done when - [ ] Rebuilding the index reproduces the project - [ ] A crash mid-write leaves the previous file intact - [ ] Scene files open in a plain text editor - [ ] A file renamed outside the app is matched by identifier ### Watch out - Keeping the manuscript inside a database with an export button means that a failed migration, or a crash during a write, leaves the whole book inside one binary file that no other program can open, and this is the failure that ends writing projects rather than merely annoying people. Put the words in plain files, verify the replacement reads back before it becomes the scene, and make the index something you can delete and rebuild on a Tuesday. ## M2 · Snapshots, and a way back to Tuesday Any scene can be restored to what it said several days ago, byte for byte. ### Steps 1. Snapshot before every replace, not after The version you want back is the one that existed before the save. Files: `src/snapshots/take.ts` 2. Retain by count and by age, never dropping the newest Both rules, because either alone has a bad day. Files: `src/snapshots/retain.ts`, `tests/retention.test.ts` 3. Make restore itself snapshot first Restoring the wrong version should not be the end of it. Files: `src/snapshots/restore.ts`, `tests/restore.test.ts` ### Done when - [ ] A days-old snapshot restores byte for byte - [ ] Snapshots are taken before every replace - [ ] Retention never removes the newest snapshot - [ ] Restore takes its own snapshot first ### Watch out - An undo stack that lives in memory disappears the moment the application restarts, which is exactly when a writer wants back the paragraph they cut before lunch, and an editor that cannot return it gets replaced by one that can. Snapshot to disk before every replace and on a timer, keep them under a stated retention, and put the restore in the test suite rather than in the documentation. ## M3 · A plot grid that survives a reorder Moving chapter twelve to chapter four leaves every beat where it belongs. ### Steps 1. Key beats on the identifier, never on the ordinal Position is the one property that changes constantly. Files: `src/plot/beats.ts`, `tests/reorder.test.ts` 2. Decide split and merge behaviour and write it down Doing it by instinct in the moment gives a different answer each time. Files: `src/plot/split.ts`, `README.md` 3. Run the orphan report on every rebuild A silently detached beat is worse than a missing one. Files: `src/plot/orphans.ts` ### Done when - [ ] A reorder leaves beats on their scenes - [ ] Split follows a documented rule - [ ] Orphaned beats are reported and fixable - [ ] The grid renders a large project without a pause ### Watch out - Attaching plot beats to a scene's position in the manuscript means the first structural edit silently invalidates the whole grid, and because it is silent nobody notices until the rewrite, by which point the grid is a picture of a book that no longer exists. Key beats on the scene identifier, decide and document the split and merge rules, and run an orphan report every time the index rebuilds. ## M4 · Counting words honestly One documented counting rule, session totals derived from snapshots, and nothing that shames. ### Steps 1. Document the counting rule before implementing it Otherwise the rule is whatever the code happened to do. Files: `docs/word-counting.md`, `src/count.ts` 2. Derive sessions from snapshot differences So a day spent cutting reads as a day spent cutting. Files: `src/sessions.ts`, `tests/sessions.test.ts` 3. Ship goals off by default and no streaks at all A writing tool that nags is a writing tool people avoid opening. Files: `src/goals.ts` ### Done when - [ ] The fixture counts exactly as documented - [ ] A net deletion shows as a negative session - [ ] Goals hide completely and nothing nags - [ ] The manuscript total equals the sum of its scenes ### Watch out - Every tool counts differently, so a manuscript reading ninety thousand in your editor and eighty seven in a submission form makes the writer distrust both numbers, and a daily total incremented as you type also counts the thousand words you deleted an hour later. Document what the counter counts, derive session figures from the difference between snapshots so a deletion shows as a deletion, and leave goals switched off by default. ## M5 · An export a shop will accept (production only) The book file carries the metadata its specification requires and is validated before it is written. ### Steps 1. Populate the mandatory metadata, not just the pretty parts The specification is explicit about which fields are required. Files: `src/export/metadata.ts`, `tests/metadata.test.ts` 2. Validate and fail rather than write A file that exists and is rejected costs more than no file. Files: `src/export/validate.ts` 3. Prove the compile round-trips A lost scene in a hundred and twenty is not visible by eye. Files: `src/export/compile.ts`, `tests/roundtrip.test.ts` ### Done when - [ ] Mandatory accessibility metadata is present and non-empty - [ ] A failing validation blocks the export - [ ] A compiled manuscript reimports with every scene - [ ] Validator output is stored and readable ### Watch out - The current EPUB accessibility specification, a recommendation dated 17 October 2024, states that all publications 'MUST include accessibility metadata in the package document that exposes their accessible properties', naming access mode, accessibility feature and accessibility hazard as required and accessibility summary and access mode sufficient as recommended. Separately the European accessibility rules have applied since 28 June 2025 and treat e-books and their reading software as a service, with an exemption for microenterprises providing services, defined as fewer than ten people and turnover or balance sheet total not above two million euro. Checked on 15 September 2026. So an export with no accessibility metadata may simply be unsellable in that market, and whether the exemption applies is a fact about the seller rather than about the file. ===== OPERATIONS.md ===== # Operations · Dabble ## Backup The project folder and the snapshot folder together, and offsite, because this is the one category where the data is the work itself. The index does not need backing up and should not be restored in preference to a rescan. ## Restore Restore the files, delete the index, rebuild it and run the orphan report. If beats are orphaned the snapshot folder is where the missing scene will be. A restore that brings back an old index over new files will look wrong in ways that are hard to diagnose. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring For a local tool these live in the suite: the crash test, the snapshot retention boundaries, the reorder test, the counting fixture and the export validation. Run them on every release rather than watching a dashboard. ## Incident checklist The incident is lost words, and it happens through a crash mid-write, an over-eager retention rule, or a sync tool resolving a conflict on your behalf. Atomic writes, snapshots that never discard the newest, and plain files an external tool can also read are the three things that make recovery a matter of looking rather than hoping. 1. Contain the issue without destroying evidence or user data. 2. Record the timeline and affected scope. 3. Rotate exposed secrets and revoke compromised sessions or credentials. 4. Restore from a verified backup when needed. 5. Document the root cause, the remediation and the regression test. ## Release gate - [ ] Rebuilding the index from files alone is confirmed to reproduce the project - [ ] A crash mid-write is confirmed to leave the previous scene intact - [ ] A chapter reorder is confirmed to leave every beat attached - [ ] The word counting fixture is confirmed to match the documented rule - [ ] An export failing validation is confirmed to write no file ## Launch constraint Do not market omitted Dabble capabilities as implemented. The non-goals in `PRODUCT.md` remain user-visible limitations until they are deliberately delivered. ===== .env.example ===== # Copy to .env and fill in. Never commit .env; this file documents it. # Required. The folder of scene files. This is the manuscript; everything else is derived. PROJECT_DIR=~/Writing/novel # Required. The rebuildable index. Safe to delete at any time. INDEX_PATH=~/Writing/novel/.index.db # Required. Versions before every replace. Back this up with the manuscript. SNAPSHOT_DIR=~/Writing/novel/.snapshots # Required. Per scene, alongside the age rule. Neither ever removes the newest. SNAPSHOT_KEEP=200 # Required. The age half of the retention rule. SNAPSHOT_MAX_AGE_DAYS=180 # Required. How much typing an unexpected shutdown can cost. AUTOSAVE_SECONDS=20 # Optional · secret. Optional rewrite of a selected passage only. The application works entirely without it. MODEL_API_KEY=sk-...redacted # Required. Compiled manuscripts and book files, with the validator output beside each. EXPORT_DIR=~/Writing/exports
# Dabble · indie build A novel workspace whose hard problems are that the manuscript must survive the application, that plot threads have to stay attached to scenes through reordering and splitting, and that an export has to satisfy a specification before anybody will sell it. Estimated effort: **multi-day**. Work `BUILD_PLAN.md` top to bottom · every phase ends in a check that has to pass before the next one starts. ## Stack | Part | Choice | Why | | --- | --- | --- | | Runtime | Tauri with a React front end and a small Rust file layer | the words live on your disk and the application is a view onto them, not their container | | Storage | One plain text file per scene with front matter, and SQLite as a rebuildable index | if the application dies tomorrow the manuscript is still a folder of readable files | | Writes | Write to a temporary file, verify it reads back, then replace, snapshotting first | a crash mid-save on a ninety thousand word manuscript is the failure that ends writing projects | | Identity | Stable scene identifiers in the front matter that survive rename, reorder, split and merge | plot threads hang off scenes, and a thread that has lost its scene is worse than no thread | | Export | A compiled manuscript plus a book file carrying the metadata its specification requires | an export a shop rejects is not an export | ## Before you start Have every one of these ready. The plan assumes them from step one. - [ ] **A Rust toolchain and the Tauri prerequisites for your platform** · free - Why: The file layer is where atomic writing and snapshotting live, and it is the part that must not be approximate. - Get it: rustup, then the platform prerequisites listed by the Tauri project. - Verify: A blank application builds and runs on your machine - [ ] **Node.js 22 or newer** · free - Why: Builds the front end and runs the test suite. - Get it: nodejs.org or your version manager. - Verify: node --version prints v22 or higher - [ ] **A validator for the book format you export** · free - Why: Without it the export phase is guesswork, and the file will be rejected by somebody else's upload form instead of by your own build. - Get it: Use the validation tool published for the format and pin its version in the repository. - Verify: A deliberately broken export fails and a good one passes - [ ] **A real manuscript of at least fifty thousand words** · free - Why: Every problem in this build is a problem of scale: reordering, orphaned beats, counting disagreements and export timing all look fine on a ten scene fixture. - Get it: Use your own work in progress, or a public domain novel split into scene files. - Verify: Index rebuild, grid render and export all complete within a stated time - [ ] **Your word counting rule, written down first** · free - Why: Deciding it after implementation means the rule is whatever the code happened to do, and the number will disagree with a submission form for reasons nobody can explain. - Get it: Write down how hyphenated compounds, contractions, numerals and dialogue punctuation are treated, then implement that. - Verify: A fixture counts exactly as the document says it should ## Quick start Follow Phase 1 in `BUILD_PLAN.md`; it creates the project. Then copy `.env.example` to `.env` and fill in the values it documents. ## Honest limits This build deliberately does not replace: - Cloud sync and mobile access. - Collaborative editing. - A style or grammar model of your own. - Browser and office suite extensions. - cloud sync, mobile access, goals, and polished manuscript organization - cross-device sync - large proprietary style models - browser and office-suite extensions - team style guides If one of those is essential to you, that is the reason to keep paying for Dabble, and the README should say so rather than pretend.
# Build brief · Dabble The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a local long-form manuscript workspace that keeps your words in plain files and exports a book file a shop will accept to replace Dabble. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Stack (fixed, do not substitute) - Runtime: Tauri with a React front end and a small Rust file layer · the words live on your disk and the application is a view onto them, not their container - Storage: One plain text file per scene with front matter, and SQLite as a rebuildable index · if the application dies tomorrow the manuscript is still a folder of readable files - Writes: Write to a temporary file, verify it reads back, then replace, snapshotting first · a crash mid-save on a ninety thousand word manuscript is the failure that ends writing projects - Identity: Stable scene identifiers in the front matter that survive rename, reorder, split and merge · plot threads hang off scenes, and a thread that has lost its scene is worse than no thread - Export: A compiled manuscript plus a book file carrying the metadata its specification requires · an export a shop rejects is not an export ### Data model (create this before Phase 1) - Scene files on disk with front matter(id, title, pov, status, updated_at) · the file is the record and everything else is derived from it - scene_index(id, path, ordinal, chapter_id, title, words, hash, indexed_at) · rebuilt by rescanning the folder, and safe to delete at any time - snapshots(id, scene_id, at, path, bytes, reason) · taken before every replace and on a timer, with a stated retention - threads(id, name, colour, notes) and beats(id, thread_id, scene_id, ordinal, note) · the plot grid, keyed on scene identifier and never on position - characters(id, name, notes, first_scene_id) and places(id, name, notes) · the continuity notes that a long manuscript cannot hold in one head - sessions(id, started_at, ended_at, words_start, words_end) · derived from snapshots rather than from a counter that only goes up - export_runs(id, at, target, output_path, validator_output, state) · including the failures, which are the ones worth reading - The folder of scene files is the manuscript. Delete the database and everything but the plot grid rebuilds from the files; delete the files and the database is a list of things that no longer exist. ### Phase 1 · Files first, index second Build: Store one scene per plain text file with front matter carrying a stable identifier. Write to a temporary file, read it back to verify, then replace. Build the index by scanning the folder and ship a rebuild command. Match files by identifier rather than by path. Trap: Keeping the manuscript inside a database with an export button means that a failed migration, or a crash during a write, leaves the whole book inside one binary file that no other program can open, and this is the failure that ends writing projects rather than merely annoying people. Put the words in plain files, verify the replacement reads back before it becomes the scene, and make the index something you can delete and rebuild on a Tuesday. Done when: Deleting the index and rebuilding it reproduces the project except for data only the index holds, tested. A crash injected mid-write leaves the previous scene file intact and readable, tested by killing the process during a save. Every scene file opens in a plain text editor with its identifier visible in the front matter. A scene renamed outside the application is matched by its identifier rather than lost, tested. Do not build yet: No snapshots, plot grid, word counting or export yet. ### Phase 2 · Snapshots, and a way back to Tuesday Build: Snapshot a scene to disk before every replace and on a timer while editing. Keep snapshots with a retention by both count and age that never discards the most recent. Provide a word-level diff between any two snapshots and a restore that itself takes a snapshot first. Trap: An undo stack that lives in memory disappears the moment the application restarts, which is exactly when a writer wants back the paragraph they cut before lunch, and an editor that cannot return it gets replaced by one that can. Snapshot to disk before every replace and on a timer, keep them under a stated retention, and put the restore in the test suite rather than in the documentation. Done when: Restoring a snapshot from a stated number of days ago reproduces that scene byte for byte, exercised in the suite rather than described. Snapshots are taken before every replace and the count is asserted after a scripted editing session. Retention deletes by count and by age and never removes the most recent snapshot of a scene, tested at both boundaries. A restore takes its own snapshot first, so restoring the wrong version is recoverable. Do not build yet: No plot grid, counting or export yet. ### Phase 3 · A plot grid that survives a reorder Build: Model threads and beats keyed on the scene identifier. Define explicitly what splitting a scene does to its beats and what merging two scenes does. Render a grid of threads against scenes. Report beats whose scene has gone and offer to reattach or remove them. Trap: Attaching plot beats to a scene's position in the manuscript means the first structural edit silently invalidates the whole grid, and because it is silent nobody notices until the rewrite, by which point the grid is a picture of a book that no longer exists. Key beats on the scene identifier, decide and document the split and merge rules, and run an orphan report every time the index rebuilds. Done when: Reordering chapters leaves every beat attached to its original scene, tested against a fixture project. Splitting a scene applies a stated rule to its beats and the rule is in the README, tested. The orphan report lists beats whose scene no longer exists and offers reattach or remove, tested by deleting a scene file. The grid renders a project of a hundred and twenty scenes without a visible pause, measured and recorded. Do not build yet: No word counting, goals or export yet. ### Phase 4 · Counting words honestly Build: Write down exactly what the counter counts and implement that. Derive a session total from the difference between snapshots rather than from a counter that only increases. Make targets optional and hideable. Do not build streaks, notifications or any copy that reacts to a missed day. Trap: Every tool counts differently, so a manuscript reading ninety thousand in your editor and eighty seven in a submission form makes the writer distrust both numbers, and a daily total incremented as you type also counts the thousand words you deleted an hour later. Document what the counter counts, derive session figures from the difference between snapshots so a deletion shows as a deletion, and leave goals switched off by default. Done when: The counting rule is documented and a fixture containing hyphenated compounds, contractions, numerals and dialogue punctuation counts exactly as the rule states, tested. A session that ends with fewer words than it started shows a negative figure rather than zero, tested. Goals can be hidden entirely and no interface element reacts to a missed day, verified by review. The manuscript total equals the sum of its scene counts exactly. Do not build yet: No compiling or export yet. ### Phase 5 · An export a shop will accept Build: Compile scenes into a manuscript file and a book file in order. Populate the accessibility metadata the current specification makes mandatory. Run a validator and fail the export rather than writing a file that does not pass. Store the validator output on the export run. Reimport a compiled manuscript and confirm no scene was lost. Trap: The current EPUB accessibility specification, a recommendation dated 17 October 2024, states that all publications 'MUST include accessibility metadata in the package document that exposes their accessible properties', naming access mode, accessibility feature and accessibility hazard as required and accessibility summary and access mode sufficient as recommended. Separately the European accessibility rules have applied since 28 June 2025 and treat e-books and their reading software as a service, with an exemption for microenterprises providing services, defined as fewer than ten people and turnover or balance sheet total not above two million euro. Checked on 15 September 2026. So an export with no accessibility metadata may simply be unsellable in that market, and whether the exemption applies is a fact about the seller rather than about the file. Done when: Every book export contains the mandatory accessibility metadata fields and a test asserts each is present and non-empty. The export runs a validator and fails rather than writing a file that does not pass, tested with a deliberately broken build. A compiled manuscript reimports with every scene present and its identifier intact, tested. The export run stores the validator output, successes and failures alike, and the failures are readable in the interface. Do not build yet: Nothing after this. ### Out of scope (and why) - Cloud sync and mobile access, which need a conflict model for a document two devices edited offline and are a larger project than the editor. - Collaborative editing, which is a different data structure entirely and not something to bolt onto a folder of files. - A style or grammar model of your own, which is a claim about language quality that a weekend build cannot support honestly. - Browser and office suite extensions, which are per-platform review processes and a permanent maintenance commitment. ### README must contain - That the folder of scene files is the manuscript and the database is an index you can delete and rebuild - That writes go to a temporary file, are verified readable, and only then replace the scene, with the crash test named - That plot beats are keyed on scene identifiers, and the documented rules for splitting and merging a scene - Exactly what the word counter counts, and that session totals come from snapshot differences so deletions show as deletions - Which accessibility metadata the book export is required to carry, quoted from the specification with the date checked, and that the validator failing blocks the export
# Agent instructions · Dabble indie build - Read `README.md` and `BUILD_PLAN.md` before writing code. The stack is fixed: Tauri with a React front end and a small Rust file layer, One plain text file per scene with front matter, and SQLite as a rebuildable index, Write to a temporary file, verify it reads back, then replace, snapshotting first, Stable scene identifiers in the front matter that survive rename, reorder, split and merge, A compiled manuscript plus a book file carrying the metadata its specification requires. Do not substitute. - Work one phase at a time, in order. Do not start a phase until every "Done when" item of the previous one passes. - Prefer the fewest moving parts that satisfy the step. No frameworks, services or dependencies the plan does not name. - Secrets live in `.env`, never in source or logs. Keep `.env.example` current when a variable is introduced. - Do not invent cryptography, security guarantees, APIs or compliance claims. - Add a focused test for every destructive, security-sensitive or data-loss path the plan names. - Run the project checks before declaring a phase complete, and record any deliberate shortcut in the README under "Tradeoffs". ## Known traps - Keeping the manuscript inside a database with an export button means that a failed migration, or a crash during a write, leaves the whole book inside one binary file that no other program can open, and this is the failure that ends writing projects rather than merely annoying people. Put the words in plain files, verify the replacement reads back before it becomes the scene, and make the index something you can delete and rebuild on a Tuesday. - An undo stack that lives in memory disappears the moment the application restarts, which is exactly when a writer wants back the paragraph they cut before lunch, and an editor that cannot return it gets replaced by one that can. Snapshot to disk before every replace and on a timer, keep them under a stated retention, and put the restore in the test suite rather than in the documentation. - Attaching plot beats to a scene's position in the manuscript means the first structural edit silently invalidates the whole grid, and because it is silent nobody notices until the rewrite, by which point the grid is a picture of a book that no longer exists. Key beats on the scene identifier, decide and document the split and merge rules, and run an orphan report every time the index rebuilds. - Every tool counts differently, so a manuscript reading ninety thousand in your editor and eighty seven in a submission form makes the writer distrust both numbers, and a daily total incremented as you type also counts the thousand words you deleted an hour later. Document what the counter counts, derive session figures from the difference between snapshots so a deletion shows as a deletion, and leave goals switched off by default.
# Build plan · Dabble A novel workspace whose hard problems are that the manuscript must survive the application, that plot threads have to stay attached to scenes through reordering and splitting, and that an export has to satisfy a specification before anybody will sell it. Phases are in dependency order. Each ends in a "Done when" list; treat an unticked item as a blocker, not a note. ## Phase 1 · Files first, index second The manuscript is a folder of readable files and the database can be thrown away. ### Steps 1. One scene, one plain file, one stable identifier The identifier is what everything else will hang from. Files: `src-tauri/src/scenes.rs`, `src/scenes/frontmatter.ts` 2. Verify the replacement before it becomes the scene Reading it back is the cheap insurance against a truncated write. Files: `src-tauri/src/atomic.rs`, `tests/crash.test.ts` 3. Make the index rebuildable and prove it An index you are afraid to delete is a second copy of the truth. Files: `src/index/rebuild.ts` ### Done when - [ ] Rebuilding the index reproduces the project - [ ] A crash mid-write leaves the previous file intact - [ ] Scene files open in a plain text editor - [ ] A file renamed outside the app is matched by identifier ### Watch out - Keeping the manuscript inside a database with an export button means that a failed migration, or a crash during a write, leaves the whole book inside one binary file that no other program can open, and this is the failure that ends writing projects rather than merely annoying people. Put the words in plain files, verify the replacement reads back before it becomes the scene, and make the index something you can delete and rebuild on a Tuesday. ## Phase 2 · Snapshots, and a way back to Tuesday Any scene can be restored to what it said several days ago, byte for byte. ### Steps 1. Snapshot before every replace, not after The version you want back is the one that existed before the save. Files: `src/snapshots/take.ts` 2. Retain by count and by age, never dropping the newest Both rules, because either alone has a bad day. Files: `src/snapshots/retain.ts`, `tests/retention.test.ts` 3. Make restore itself snapshot first Restoring the wrong version should not be the end of it. Files: `src/snapshots/restore.ts`, `tests/restore.test.ts` ### Done when - [ ] A days-old snapshot restores byte for byte - [ ] Snapshots are taken before every replace - [ ] Retention never removes the newest snapshot - [ ] Restore takes its own snapshot first ### Watch out - An undo stack that lives in memory disappears the moment the application restarts, which is exactly when a writer wants back the paragraph they cut before lunch, and an editor that cannot return it gets replaced by one that can. Snapshot to disk before every replace and on a timer, keep them under a stated retention, and put the restore in the test suite rather than in the documentation. ## Phase 3 · A plot grid that survives a reorder Moving chapter twelve to chapter four leaves every beat where it belongs. ### Steps 1. Key beats on the identifier, never on the ordinal Position is the one property that changes constantly. Files: `src/plot/beats.ts`, `tests/reorder.test.ts` 2. Decide split and merge behaviour and write it down Doing it by instinct in the moment gives a different answer each time. Files: `src/plot/split.ts`, `README.md` 3. Run the orphan report on every rebuild A silently detached beat is worse than a missing one. Files: `src/plot/orphans.ts` ### Done when - [ ] A reorder leaves beats on their scenes - [ ] Split follows a documented rule - [ ] Orphaned beats are reported and fixable - [ ] The grid renders a large project without a pause ### Watch out - Attaching plot beats to a scene's position in the manuscript means the first structural edit silently invalidates the whole grid, and because it is silent nobody notices until the rewrite, by which point the grid is a picture of a book that no longer exists. Key beats on the scene identifier, decide and document the split and merge rules, and run an orphan report every time the index rebuilds. ## Phase 4 · Counting words honestly One documented counting rule, session totals derived from snapshots, and nothing that shames. ### Steps 1. Document the counting rule before implementing it Otherwise the rule is whatever the code happened to do. Files: `docs/word-counting.md`, `src/count.ts` 2. Derive sessions from snapshot differences So a day spent cutting reads as a day spent cutting. Files: `src/sessions.ts`, `tests/sessions.test.ts` 3. Ship goals off by default and no streaks at all A writing tool that nags is a writing tool people avoid opening. Files: `src/goals.ts` ### Done when - [ ] The fixture counts exactly as documented - [ ] A net deletion shows as a negative session - [ ] Goals hide completely and nothing nags - [ ] The manuscript total equals the sum of its scenes ### Watch out - Every tool counts differently, so a manuscript reading ninety thousand in your editor and eighty seven in a submission form makes the writer distrust both numbers, and a daily total incremented as you type also counts the thousand words you deleted an hour later. Document what the counter counts, derive session figures from the difference between snapshots so a deletion shows as a deletion, and leave goals switched off by default. ## Not in this build - Cloud sync and mobile access. - Collaborative editing. - A style or grammar model of your own. - Browser and office suite extensions. ## After v1, if you want it - A read-only reading mode that renders the compiled manuscript, which is how continuity errors actually surface - Scene status tracking through a revision pass, which is a column in the grid rather than a new screen - An export target for a submission format with its own layout rules, built behind the same validate-then-write path
# Copy to .env and fill in. Never commit .env; this file documents it. # Required. The folder of scene files. This is the manuscript; everything else is derived. PROJECT_DIR=~/Writing/novel # Required. The rebuildable index. Safe to delete at any time. INDEX_PATH=~/Writing/novel/.index.db # Required. Versions before every replace. Back this up with the manuscript. SNAPSHOT_DIR=~/Writing/novel/.snapshots # Required. Per scene, alongside the age rule. Neither ever removes the newest. SNAPSHOT_KEEP=200 # Required. The age half of the retention rule. SNAPSHOT_MAX_AGE_DAYS=180 # Required. How much typing an unexpected shutdown can cost. AUTOSAVE_SECONDS=20 # Optional · secret. Optional rewrite of a selected passage only. The application works entirely without it. MODEL_API_KEY=sk-...redacted # Required. Compiled manuscripts and book files, with the validator output beside each. EXPORT_DIR=~/Writing/exports
# Dabble · product brief ## Problem The core loop is buildable, but a dependable replacement becomes a real weekend or multi-day project. For Dabble, plan chapters, draft a manuscript, track plot threads, and sync a structured writing project. The hard boundary is cloud sync, mobile access, goals, and polished manuscript organization, plus polish, integrations, and proprietary language models. ## Product outcome A workspace where the manuscript is a folder of plain files that outlives the application, any scene can be restored to what it said last week, the plot grid survives a structural edit, and the book export is blocked rather than shipped when it would be rejected. ## Target user A builder who needs a maintainable product foundation, not a one-off demo. ## Required capabilities - macOS, Windows, or Linux - local files - optional OpenAI or Anthropic API key for semantic rewrites ## Explicit non-goals for v1 - Cloud sync and mobile access. - Collaborative editing. - A style or grammar model of your own. - Browser and office suite extensions. - cloud sync, mobile access, goals, and polished manuscript organization - cross-device sync - large proprietary style models - browser and office-suite extensions - team style guides ## Success criteria - Rebuilding the index from files alone is confirmed to reproduce the project - A crash mid-write is confirmed to leave the previous scene intact - A chapter reorder is confirmed to leave every beat attached - The word counting fixture is confirmed to match the documented rule - An export failing validation is confirmed to write no file
# Build brief · Dabble The one-shot brief this plan expands. `BUILD_PLAN.md` (or `MILESTONES.md`) is the same sequence broken into steps and checks; where the two disagree, the plan wins. Build me a local long-form manuscript workspace that keeps your words in plain files and exports a book file a shop will accept to replace Dabble. Build it in phases, in the order below. Do not write the whole thing in one pass. Finish a phase, run its "Done when" check, fix what fails, and only then start the next phase. ### Stack (fixed, do not substitute) - Runtime: Tauri with a React front end and a small Rust file layer · the words live on your disk and the application is a view onto them, not their container - Storage: One plain text file per scene with front matter, and SQLite as a rebuildable index · if the application dies tomorrow the manuscript is still a folder of readable files - Writes: Write to a temporary file, verify it reads back, then replace, snapshotting first · a crash mid-save on a ninety thousand word manuscript is the failure that ends writing projects - Identity: Stable scene identifiers in the front matter that survive rename, reorder, split and merge · plot threads hang off scenes, and a thread that has lost its scene is worse than no thread - Export: A compiled manuscript plus a book file carrying the metadata its specification requires · an export a shop rejects is not an export ### Data model (create this before Phase 1) - Scene files on disk with front matter(id, title, pov, status, updated_at) · the file is the record and everything else is derived from it - scene_index(id, path, ordinal, chapter_id, title, words, hash, indexed_at) · rebuilt by rescanning the folder, and safe to delete at any time - snapshots(id, scene_id, at, path, bytes, reason) · taken before every replace and on a timer, with a stated retention - threads(id, name, colour, notes) and beats(id, thread_id, scene_id, ordinal, note) · the plot grid, keyed on scene identifier and never on position - characters(id, name, notes, first_scene_id) and places(id, name, notes) · the continuity notes that a long manuscript cannot hold in one head - sessions(id, started_at, ended_at, words_start, words_end) · derived from snapshots rather than from a counter that only goes up - export_runs(id, at, target, output_path, validator_output, state) · including the failures, which are the ones worth reading - The folder of scene files is the manuscript. Delete the database and everything but the plot grid rebuilds from the files; delete the files and the database is a list of things that no longer exist. ### Phase 1 · Files first, index second Build: Store one scene per plain text file with front matter carrying a stable identifier. Write to a temporary file, read it back to verify, then replace. Build the index by scanning the folder and ship a rebuild command. Match files by identifier rather than by path. Trap: Keeping the manuscript inside a database with an export button means that a failed migration, or a crash during a write, leaves the whole book inside one binary file that no other program can open, and this is the failure that ends writing projects rather than merely annoying people. Put the words in plain files, verify the replacement reads back before it becomes the scene, and make the index something you can delete and rebuild on a Tuesday. Done when: Deleting the index and rebuilding it reproduces the project except for data only the index holds, tested. A crash injected mid-write leaves the previous scene file intact and readable, tested by killing the process during a save. Every scene file opens in a plain text editor with its identifier visible in the front matter. A scene renamed outside the application is matched by its identifier rather than lost, tested. Do not build yet: No snapshots, plot grid, word counting or export yet. ### Phase 2 · Snapshots, and a way back to Tuesday Build: Snapshot a scene to disk before every replace and on a timer while editing. Keep snapshots with a retention by both count and age that never discards the most recent. Provide a word-level diff between any two snapshots and a restore that itself takes a snapshot first. Trap: An undo stack that lives in memory disappears the moment the application restarts, which is exactly when a writer wants back the paragraph they cut before lunch, and an editor that cannot return it gets replaced by one that can. Snapshot to disk before every replace and on a timer, keep them under a stated retention, and put the restore in the test suite rather than in the documentation. Done when: Restoring a snapshot from a stated number of days ago reproduces that scene byte for byte, exercised in the suite rather than described. Snapshots are taken before every replace and the count is asserted after a scripted editing session. Retention deletes by count and by age and never removes the most recent snapshot of a scene, tested at both boundaries. A restore takes its own snapshot first, so restoring the wrong version is recoverable. Do not build yet: No plot grid, counting or export yet. ### Phase 3 · A plot grid that survives a reorder Build: Model threads and beats keyed on the scene identifier. Define explicitly what splitting a scene does to its beats and what merging two scenes does. Render a grid of threads against scenes. Report beats whose scene has gone and offer to reattach or remove them. Trap: Attaching plot beats to a scene's position in the manuscript means the first structural edit silently invalidates the whole grid, and because it is silent nobody notices until the rewrite, by which point the grid is a picture of a book that no longer exists. Key beats on the scene identifier, decide and document the split and merge rules, and run an orphan report every time the index rebuilds. Done when: Reordering chapters leaves every beat attached to its original scene, tested against a fixture project. Splitting a scene applies a stated rule to its beats and the rule is in the README, tested. The orphan report lists beats whose scene no longer exists and offers reattach or remove, tested by deleting a scene file. The grid renders a project of a hundred and twenty scenes without a visible pause, measured and recorded. Do not build yet: No word counting, goals or export yet. ### Phase 4 · Counting words honestly Build: Write down exactly what the counter counts and implement that. Derive a session total from the difference between snapshots rather than from a counter that only increases. Make targets optional and hideable. Do not build streaks, notifications or any copy that reacts to a missed day. Trap: Every tool counts differently, so a manuscript reading ninety thousand in your editor and eighty seven in a submission form makes the writer distrust both numbers, and a daily total incremented as you type also counts the thousand words you deleted an hour later. Document what the counter counts, derive session figures from the difference between snapshots so a deletion shows as a deletion, and leave goals switched off by default. Done when: The counting rule is documented and a fixture containing hyphenated compounds, contractions, numerals and dialogue punctuation counts exactly as the rule states, tested. A session that ends with fewer words than it started shows a negative figure rather than zero, tested. Goals can be hidden entirely and no interface element reacts to a missed day, verified by review. The manuscript total equals the sum of its scene counts exactly. Do not build yet: No compiling or export yet. ### Phase 5 · An export a shop will accept Build: Compile scenes into a manuscript file and a book file in order. Populate the accessibility metadata the current specification makes mandatory. Run a validator and fail the export rather than writing a file that does not pass. Store the validator output on the export run. Reimport a compiled manuscript and confirm no scene was lost. Trap: The current EPUB accessibility specification, a recommendation dated 17 October 2024, states that all publications 'MUST include accessibility metadata in the package document that exposes their accessible properties', naming access mode, accessibility feature and accessibility hazard as required and accessibility summary and access mode sufficient as recommended. Separately the European accessibility rules have applied since 28 June 2025 and treat e-books and their reading software as a service, with an exemption for microenterprises providing services, defined as fewer than ten people and turnover or balance sheet total not above two million euro. Checked on 15 September 2026. So an export with no accessibility metadata may simply be unsellable in that market, and whether the exemption applies is a fact about the seller rather than about the file. Done when: Every book export contains the mandatory accessibility metadata fields and a test asserts each is present and non-empty. The export runs a validator and fails rather than writing a file that does not pass, tested with a deliberately broken build. A compiled manuscript reimports with every scene present and its identifier intact, tested. The export run stores the validator output, successes and failures alike, and the failures are readable in the interface. Do not build yet: Nothing after this. ### Out of scope (and why) - Cloud sync and mobile access, which need a conflict model for a document two devices edited offline and are a larger project than the editor. - Collaborative editing, which is a different data structure entirely and not something to bolt onto a folder of files. - A style or grammar model of your own, which is a claim about language quality that a weekend build cannot support honestly. - Browser and office suite extensions, which are per-platform review processes and a permanent maintenance commitment. ### README must contain - That the folder of scene files is the manuscript and the database is an index you can delete and rebuild - That writes go to a temporary file, are verified readable, and only then replace the scene, with the crash test named - That plot beats are keyed on scene identifiers, and the documented rules for splitting and merging a scene - Exactly what the word counter counts, and that session totals come from snapshot differences so deletions show as deletions - Which accessibility metadata the book export is required to carry, quoted from the specification with the date checked, and that the validator failing blocks the export
# Architecture · Dabble ## Stack | Part | Choice | Why | | --- | --- | --- | | Runtime | Tauri with a React front end and a small Rust file layer | the words live on your disk and the application is a view onto them, not their container | | Storage | One plain text file per scene with front matter, and SQLite as a rebuildable index | if the application dies tomorrow the manuscript is still a folder of readable files | | Writes | Write to a temporary file, verify it reads back, then replace, snapshotting first | a crash mid-save on a ninety thousand word manuscript is the failure that ends writing projects | | Identity | Stable scene identifiers in the front matter that survive rename, reorder, split and merge | plot threads hang off scenes, and a thread that has lost its scene is worse than no thread | | Export | A compiled manuscript plus a book file carrying the metadata its specification requires | an export a shop rejects is not an export | ## Modules Each module has one owner concern and a documented way to replace it. | Module | Owns | How to replace it | | --- | --- | --- | | Files | Scene files, front matter and atomic replacement. | Not swappable for a database of paragraphs, which is the failure this design avoids. | | Index | Ordinals, chapters and search. | Any store, provided it can be deleted and rebuilt from the files. | | Snapshots | Versions, retention and restore. | Any mechanism, provided the restore is exercised in the suite. | | Plot | Threads, beats and orphan reporting. | Any model keyed on scene identifiers rather than positions. | | Export | Compilation, metadata and validation. | Any format, provided an invalid file is never written. | ## Configuration Every runtime setting is an environment variable documented in `.env.example`, validated at startup, with a safe local default wherever one exists. - `PROJECT_DIR` · required · The folder of scene files. This is the manuscript; everything else is derived. - `INDEX_PATH` · required · The rebuildable index. Safe to delete at any time. - `SNAPSHOT_DIR` · required · Versions before every replace. Back this up with the manuscript. - `SNAPSHOT_KEEP` · required · Per scene, alongside the age rule. Neither ever removes the newest. - `SNAPSHOT_MAX_AGE_DAYS` · required · The age half of the retention rule. - `AUTOSAVE_SECONDS` · required · How much typing an unexpected shutdown can cost. - `MODEL_API_KEY` · optional, secret · Optional rewrite of a selected passage only. The application works entirely without it. - `EXPORT_DIR` · required · Compiled manuscripts and book files, with the validator output beside each. ## Production baseline - Security: least privilege, input validation at every boundary, secret redaction in logs, rate limits on abuse-prone paths, no invented security primitives. - Data: explicit schema and migrations, transactional writes where integrity matters, backup and restore procedures that have been exercised. - Integrations: adapters around third-party providers, idempotent webhook or job processing, bounded retries, timeouts. - Observability: structured logs with request or operation ids, an error-tracking hook, and health and readiness checks where a server exists. - Quality: unit tests for domain rules, integration tests at module boundaries, one end-to-end test of the critical path. ## Decision records For each dependency in the stack table, keep a short note: why it was chosen, its failure mode, and how it is replaced. Do not add infrastructure until a requirement in `PRODUCT.md` justifies it.
# Agent instructions · Dabble product build - Read `PRODUCT.md` and `ARCHITECTURE.md` before changing code. The stack is fixed: Tauri with a React front end and a small Rust file layer, One plain text file per scene with front matter, and SQLite as a rebuildable index, Write to a temporary file, verify it reads back, then replace, snapshotting first, Stable scene identifiers in the front matter that survive rename, reorder, split and merge, A compiled manuscript plus a book file carrying the metadata its specification requires. - Implement milestone by milestone from `MILESTONES.md`; keep each change reviewable and leave the application runnable at every commit. - Treat authentication, payments, encryption, imports, webhooks and destructive actions as high-risk boundaries when present. - Never invent cryptography or silently weaken a requirement to make a check pass. - Put every external service behind an interface with a deterministic fake for tests. - Add migrations and rollback or recovery notes for every persistent data change. - Log useful operational context without credentials, tokens, passwords or personal data. - Update documentation and run every check before completing a milestone. ## Known traps - Keeping the manuscript inside a database with an export button means that a failed migration, or a crash during a write, leaves the whole book inside one binary file that no other program can open, and this is the failure that ends writing projects rather than merely annoying people. Put the words in plain files, verify the replacement reads back before it becomes the scene, and make the index something you can delete and rebuild on a Tuesday. - An undo stack that lives in memory disappears the moment the application restarts, which is exactly when a writer wants back the paragraph they cut before lunch, and an editor that cannot return it gets replaced by one that can. Snapshot to disk before every replace and on a timer, keep them under a stated retention, and put the restore in the test suite rather than in the documentation. - Attaching plot beats to a scene's position in the manuscript means the first structural edit silently invalidates the whole grid, and because it is silent nobody notices until the rewrite, by which point the grid is a picture of a book that no longer exists. Key beats on the scene identifier, decide and document the split and merge rules, and run an orphan report every time the index rebuilds. - Every tool counts differently, so a manuscript reading ninety thousand in your editor and eighty seven in a submission form makes the writer distrust both numbers, and a daily total incremented as you type also counts the thousand words you deleted an hour later. Document what the counter counts, derive session figures from the difference between snapshots so a deletion shows as a deletion, and leave goals switched off by default. - The current EPUB accessibility specification, a recommendation dated 17 October 2024, states that all publications 'MUST include accessibility metadata in the package document that exposes their accessible properties', naming access mode, accessibility feature and accessibility hazard as required and accessibility summary and access mode sufficient as recommended. Separately the European accessibility rules have applied since 28 June 2025 and treat e-books and their reading software as a service, with an exemption for microenterprises providing services, defined as fewer than ten people and turnover or balance sheet total not above two million euro. Checked on 15 September 2026. So an export with no accessibility metadata may simply be unsellable in that market, and whether the exemption applies is a fact about the seller rather than about the file.
# Delivery milestones · Dabble Estimated effort: **multi-day** for the indie phases; the production-only milestones add the trust and operability layer. ## M1 · Files first, index second The manuscript is a folder of readable files and the database can be thrown away. ### Steps 1. One scene, one plain file, one stable identifier The identifier is what everything else will hang from. Files: `src-tauri/src/scenes.rs`, `src/scenes/frontmatter.ts` 2. Verify the replacement before it becomes the scene Reading it back is the cheap insurance against a truncated write. Files: `src-tauri/src/atomic.rs`, `tests/crash.test.ts` 3. Make the index rebuildable and prove it An index you are afraid to delete is a second copy of the truth. Files: `src/index/rebuild.ts` ### Done when - [ ] Rebuilding the index reproduces the project - [ ] A crash mid-write leaves the previous file intact - [ ] Scene files open in a plain text editor - [ ] A file renamed outside the app is matched by identifier ### Watch out - Keeping the manuscript inside a database with an export button means that a failed migration, or a crash during a write, leaves the whole book inside one binary file that no other program can open, and this is the failure that ends writing projects rather than merely annoying people. Put the words in plain files, verify the replacement reads back before it becomes the scene, and make the index something you can delete and rebuild on a Tuesday. ## M2 · Snapshots, and a way back to Tuesday Any scene can be restored to what it said several days ago, byte for byte. ### Steps 1. Snapshot before every replace, not after The version you want back is the one that existed before the save. Files: `src/snapshots/take.ts` 2. Retain by count and by age, never dropping the newest Both rules, because either alone has a bad day. Files: `src/snapshots/retain.ts`, `tests/retention.test.ts` 3. Make restore itself snapshot first Restoring the wrong version should not be the end of it. Files: `src/snapshots/restore.ts`, `tests/restore.test.ts` ### Done when - [ ] A days-old snapshot restores byte for byte - [ ] Snapshots are taken before every replace - [ ] Retention never removes the newest snapshot - [ ] Restore takes its own snapshot first ### Watch out - An undo stack that lives in memory disappears the moment the application restarts, which is exactly when a writer wants back the paragraph they cut before lunch, and an editor that cannot return it gets replaced by one that can. Snapshot to disk before every replace and on a timer, keep them under a stated retention, and put the restore in the test suite rather than in the documentation. ## M3 · A plot grid that survives a reorder Moving chapter twelve to chapter four leaves every beat where it belongs. ### Steps 1. Key beats on the identifier, never on the ordinal Position is the one property that changes constantly. Files: `src/plot/beats.ts`, `tests/reorder.test.ts` 2. Decide split and merge behaviour and write it down Doing it by instinct in the moment gives a different answer each time. Files: `src/plot/split.ts`, `README.md` 3. Run the orphan report on every rebuild A silently detached beat is worse than a missing one. Files: `src/plot/orphans.ts` ### Done when - [ ] A reorder leaves beats on their scenes - [ ] Split follows a documented rule - [ ] Orphaned beats are reported and fixable - [ ] The grid renders a large project without a pause ### Watch out - Attaching plot beats to a scene's position in the manuscript means the first structural edit silently invalidates the whole grid, and because it is silent nobody notices until the rewrite, by which point the grid is a picture of a book that no longer exists. Key beats on the scene identifier, decide and document the split and merge rules, and run an orphan report every time the index rebuilds. ## M4 · Counting words honestly One documented counting rule, session totals derived from snapshots, and nothing that shames. ### Steps 1. Document the counting rule before implementing it Otherwise the rule is whatever the code happened to do. Files: `docs/word-counting.md`, `src/count.ts` 2. Derive sessions from snapshot differences So a day spent cutting reads as a day spent cutting. Files: `src/sessions.ts`, `tests/sessions.test.ts` 3. Ship goals off by default and no streaks at all A writing tool that nags is a writing tool people avoid opening. Files: `src/goals.ts` ### Done when - [ ] The fixture counts exactly as documented - [ ] A net deletion shows as a negative session - [ ] Goals hide completely and nothing nags - [ ] The manuscript total equals the sum of its scenes ### Watch out - Every tool counts differently, so a manuscript reading ninety thousand in your editor and eighty seven in a submission form makes the writer distrust both numbers, and a daily total incremented as you type also counts the thousand words you deleted an hour later. Document what the counter counts, derive session figures from the difference between snapshots so a deletion shows as a deletion, and leave goals switched off by default. ## M5 · An export a shop will accept (production only) The book file carries the metadata its specification requires and is validated before it is written. ### Steps 1. Populate the mandatory metadata, not just the pretty parts The specification is explicit about which fields are required. Files: `src/export/metadata.ts`, `tests/metadata.test.ts` 2. Validate and fail rather than write A file that exists and is rejected costs more than no file. Files: `src/export/validate.ts` 3. Prove the compile round-trips A lost scene in a hundred and twenty is not visible by eye. Files: `src/export/compile.ts`, `tests/roundtrip.test.ts` ### Done when - [ ] Mandatory accessibility metadata is present and non-empty - [ ] A failing validation blocks the export - [ ] A compiled manuscript reimports with every scene - [ ] Validator output is stored and readable ### Watch out - The current EPUB accessibility specification, a recommendation dated 17 October 2024, states that all publications 'MUST include accessibility metadata in the package document that exposes their accessible properties', naming access mode, accessibility feature and accessibility hazard as required and accessibility summary and access mode sufficient as recommended. Separately the European accessibility rules have applied since 28 June 2025 and treat e-books and their reading software as a service, with an exemption for microenterprises providing services, defined as fewer than ten people and turnover or balance sheet total not above two million euro. Checked on 15 September 2026. So an export with no accessibility metadata may simply be unsellable in that market, and whether the exemption applies is a fact about the seller rather than about the file.
# Operations · Dabble ## Backup The project folder and the snapshot folder together, and offsite, because this is the one category where the data is the work itself. The index does not need backing up and should not be restored in preference to a rescan. ## Restore Restore the files, delete the index, rebuild it and run the orphan report. If beats are orphaned the snapshot folder is where the missing scene will be. A restore that brings back an old index over new files will look wrong in ways that are hard to diagnose. Do a restore drill before the first real user, and write the date here when it passes. ## Monitoring For a local tool these live in the suite: the crash test, the snapshot retention boundaries, the reorder test, the counting fixture and the export validation. Run them on every release rather than watching a dashboard. ## Incident checklist The incident is lost words, and it happens through a crash mid-write, an over-eager retention rule, or a sync tool resolving a conflict on your behalf. Atomic writes, snapshots that never discard the newest, and plain files an external tool can also read are the three things that make recovery a matter of looking rather than hoping. 1. Contain the issue without destroying evidence or user data. 2. Record the timeline and affected scope. 3. Rotate exposed secrets and revoke compromised sessions or credentials. 4. Restore from a verified backup when needed. 5. Document the root cause, the remediation and the regression test. ## Release gate - [ ] Rebuilding the index from files alone is confirmed to reproduce the project - [ ] A crash mid-write is confirmed to leave the previous scene intact - [ ] A chapter reorder is confirmed to leave every beat attached - [ ] The word counting fixture is confirmed to match the documented rule - [ ] An export failing validation is confirmed to write no file ## Launch constraint Do not market omitted Dabble capabilities as implemented. The non-goals in `PRODUCT.md` remain user-visible limitations until they are deliberately delivered.
# Copy to .env and fill in. Never commit .env; this file documents it. # Required. The folder of scene files. This is the manuscript; everything else is derived. PROJECT_DIR=~/Writing/novel # Required. The rebuildable index. Safe to delete at any time. INDEX_PATH=~/Writing/novel/.index.db # Required. Versions before every replace. Back this up with the manuscript. SNAPSHOT_DIR=~/Writing/novel/.snapshots # Required. Per scene, alongside the age rule. Neither ever removes the newest. SNAPSHOT_KEEP=200 # Required. The age half of the retention rule. SNAPSHOT_MAX_AGE_DAYS=180 # Required. How much typing an unexpected shutdown can cost. AUTOSAVE_SECONDS=20 # Optional · secret. Optional rewrite of a selected passage only. The application works entirely without it. MODEL_API_KEY=sk-...redacted # Required. Compiled manuscripts and book files, with the validator output beside each. EXPORT_DIR=~/Writing/exports
$ choose a build depth, inspect the files, then open the complete pack in your agent
People still pay for Dabble because the paid tool is already present everywhere they write and its suggestions have been tuned across millions of documents. The recurring cost buys model quality, extension compatibility, dictionaries, releases, and sync, not just the visible interface.
xcloud sync, mobile access, goals, and polished manuscript organization
xcross-device sync
xlarge proprietary style models
xbrowser and office-suite extensions
xteam style guides
Don't feel like building it? These folks already made it free.
all 6 free alternatives to Dabble →· no votes, no pay-to-list · just what's real
Dabble pricing
| plan | monthly | annual (per mo) | what you get |
|---|---|---|---|
| writer | $19/user | $15.20/user | Unlimited manuscripts, chapters, and words; cloud sync; co-authoring and writing-group sharing |
| author | $29/user | $23.20/user | Everything in Writer plus unlimited editing/grammar features, Read-to-Me, beta-reader and editor support |
| bestseller | $49/user | $39.20/user | Everything in Author plus 70+ hours of workshop recordings, live monthly workshops, courses and community |
| lifetime author access | custom | — | All Author features and future Author updates forever |
free tierno free tier
billingmonthly + annual (-20%) + $699 one-time Lifetime Author option; 14-day trial is not a free tier
hidden costsThe one-time license covers Author, not Bestseller; the public page states annual plans are 20% off but does not expose exact annual checkout totals.
verified 2026-08-12 · source ↗
Vibecode Dabble
Kinda. The core of Dabble is buildable in a weekend with the prompt on this page, but there are real gaps: cloud sync, mobile access, goals, and polished manuscript organization, cross-device sync. Read the honest list above before committing.
How much does Dabble cost?
Dabble costs about $19/month (Writer, checked 2026-08-12), which is $228 per year.
What do I lose by replacing Dabble?
Honestly: cloud sync, mobile access, goals, and polished manuscript organization; cross-device sync; large proprietary style models; browser and office-suite extensions; team style guides. If any of those are load-bearing for you, keep paying.
Is there an open-source alternative to Dabble?
Yes: novelWriter (A plain-text novel room with scenes, synopses, cross-references, and no cloud account between you and chapter twelve.) Manuskript (A Scrivener-shaped box of scenes, characters, plots, index cards, and rough edges that cost exactly nothing.) Kindling (A tiny local fiction studio where the outline stays beside the draft; no AI, no cloud, and barely any GitHub fame yet.) All 6 curated free alternatives are at vibecodeit.com/dabble/alternatives. The prompt is for when you want it exactly your way.