780 lines
16 KiB
Markdown
780 lines
16 KiB
Markdown
# DECISIONS
|
||
|
||
This document records major architectural and product decisions made during the development of Sapling.
|
||
|
||
The goal is to preserve context and reasoning behind decisions so future contributors can understand why a particular approach was chosen.
|
||
|
||
---
|
||
|
||
# D-001 — Sapling Is Local-First
|
||
|
||
Date: 2025-08
|
||
|
||
Status: Accepted
|
||
|
||
## Decision
|
||
|
||
Sapling will be a local-first application.
|
||
|
||
All user content lives on the local filesystem.
|
||
|
||
No Sapling-managed cloud service will be required.
|
||
|
||
## Rationale
|
||
|
||
Users should own their data.
|
||
|
||
Files must remain accessible outside of Sapling.
|
||
|
||
The application should continue functioning without network connectivity.
|
||
|
||
## Consequences
|
||
|
||
Positive:
|
||
|
||
- No vendor lock-in
|
||
- Offline support
|
||
- Simpler privacy story
|
||
- Easier long-term maintenance
|
||
|
||
Negative:
|
||
|
||
- No built-in synchronization
|
||
- Collaboration relies on Git workflows
|
||
|
||
---
|
||
|
||
# D-002 — Git Is Infrastructure, Not the Product
|
||
|
||
Date: 2025-08
|
||
|
||
Status: Accepted
|
||
|
||
## Decision
|
||
|
||
Git will be a first-class capability but not the primary user-facing concept.
|
||
|
||
The editor and workspace experience take precedence over Git terminology.
|
||
|
||
## Rationale
|
||
|
||
Most users want versioning benefits without learning Git internals.
|
||
|
||
Sapling should remain approachable to writers, researchers, and knowledge workers.
|
||
|
||
## Consequences
|
||
|
||
User interfaces should prefer language such as:
|
||
|
||
- Snapshot
|
||
- Changes
|
||
- History
|
||
|
||
over:
|
||
|
||
- Commit
|
||
- Staging
|
||
- Reflog
|
||
|
||
Advanced Git functionality will remain available.
|
||
|
||
---
|
||
|
||
# D-003 — Projects Are Git Repositories
|
||
|
||
Date: 2025-08
|
||
|
||
Status: Accepted
|
||
|
||
## Decision
|
||
|
||
Every Sapling project corresponds directly to a Git repository.
|
||
|
||
No custom repository format will be introduced.
|
||
|
||
## Rationale
|
||
|
||
Git repositories are portable and understood by existing tooling.
|
||
|
||
Users should be able to move between Sapling and other Git tools without migration.
|
||
|
||
## Consequences
|
||
|
||
Projects can be opened in:
|
||
|
||
- Terminal
|
||
- GitHub Desktop
|
||
- SourceTree
|
||
- VS Code
|
||
- Any Git client
|
||
|
||
without conversion.
|
||
|
||
---
|
||
|
||
# D-004 — Workspaces Are Not Versioned
|
||
|
||
Date: 2025-08
|
||
|
||
Status: Accepted
|
||
|
||
## Decision
|
||
|
||
Workspaces are organizational containers and are not Git repositories.
|
||
|
||
Projects are versioned.
|
||
|
||
Workspaces are not.
|
||
|
||
## Rationale
|
||
|
||
Users need a place for temporary notes, drafts, and experimentation.
|
||
|
||
Not everything should require commits.
|
||
|
||
## Consequences
|
||
|
||
Workspace structure exists outside repository history.
|
||
|
||
Projects remain independently portable.
|
||
|
||
---
|
||
|
||
# D-005 — Attachments Belong to Projects
|
||
|
||
Date: 2025-08
|
||
|
||
Status: Accepted
|
||
|
||
## Decision
|
||
|
||
Attachments are stored inside project directories.
|
||
|
||
They are not stored in a database.
|
||
|
||
They are not stored in a global asset store.
|
||
|
||
## Rationale
|
||
|
||
Repositories should remain self-contained.
|
||
|
||
Cloning a repository should retrieve everything required to render its content.
|
||
|
||
## Consequences
|
||
|
||
Projects remain portable.
|
||
|
||
Attachments participate naturally in version control.
|
||
|
||
---
|
||
|
||
# D-006 — Large Assets Use Git LFS
|
||
|
||
Date: 2025-08
|
||
|
||
Status: Accepted
|
||
|
||
## Decision
|
||
|
||
Large binary files should be managed through Git LFS.
|
||
|
||
## Rationale
|
||
|
||
Repositories containing images, PDFs, design assets, and media files can become excessively large.
|
||
|
||
Git LFS is the industry-standard solution.
|
||
|
||
## Consequences
|
||
|
||
Sapling must detect and assist with LFS configuration.
|
||
|
||
---
|
||
|
||
# D-007 — Subprojects Use Git Submodules
|
||
|
||
Date: 2025-08
|
||
|
||
Status: Accepted
|
||
|
||
## Decision
|
||
|
||
Nested projects are implemented using Git submodules.
|
||
|
||
## Rationale
|
||
|
||
Submodules provide an existing, portable, Git-native solution.
|
||
|
||
No custom dependency system is required.
|
||
|
||
## Consequences
|
||
|
||
Sapling must provide a significantly better UX around submodules than traditional Git tools.
|
||
|
||
---
|
||
|
||
# D-008 — Hybrid Markdown Editing Is a Core Feature
|
||
|
||
Date: 2025-08
|
||
|
||
Status: Accepted
|
||
|
||
## Decision
|
||
|
||
Sapling will implement hybrid Markdown editing.
|
||
|
||
The active line displays source.
|
||
|
||
Inactive lines display rendered content.
|
||
|
||
## Rationale
|
||
|
||
This editing model combines the readability of rendered Markdown with the precision of source editing.
|
||
|
||
It is one of the primary differentiators of Sapling.
|
||
|
||
## Consequences
|
||
|
||
The editor becomes a critical architectural component.
|
||
|
||
Prototype and validation work should occur early.
|
||
|
||
---
|
||
|
||
# D-009 — The Editor Is the Highest-Priority System
|
||
|
||
Date: 2025-08
|
||
|
||
Status: Accepted
|
||
|
||
## Decision
|
||
|
||
Editor quality takes precedence over Git features.
|
||
|
||
## Rationale
|
||
|
||
Users tolerate missing Git features.
|
||
|
||
Users do not tolerate poor editing experiences.
|
||
|
||
## Consequences
|
||
|
||
Development milestones should prioritize:
|
||
|
||
1. Editing
|
||
2. Rendering
|
||
3. Workspace management
|
||
4. Git integration
|
||
|
||
in that order.
|
||
|
||
---
|
||
|
||
# D-010 — Git Access Must Be Abstracted
|
||
|
||
Date: 2025-08
|
||
|
||
Status: Accepted
|
||
|
||
## Decision
|
||
|
||
All Git functionality must be accessed through a GitProvider abstraction.
|
||
|
||
Application code should never invoke Git directly.
|
||
|
||
## Rationale
|
||
|
||
macOS and iOS have different implementation requirements.
|
||
|
||
A clean abstraction improves portability and testability.
|
||
|
||
## Initial Implementations
|
||
|
||
MacGitProvider
|
||
|
||
- Uses system Git
|
||
|
||
EmbeddedGitProvider
|
||
|
||
- Future iOS implementation
|
||
|
||
## Consequences
|
||
|
||
All repository operations must remain implementation-agnostic.
|
||
|
||
---
|
||
|
||
# D-011 — Markdown Files Remain Standard Markdown
|
||
|
||
Date: 2025-08
|
||
|
||
Status: Accepted
|
||
|
||
## Decision
|
||
|
||
Sapling documents are standard Markdown files.
|
||
|
||
No proprietary format will be introduced.
|
||
|
||
## Rationale
|
||
|
||
Users should be free to edit documents with any editor.
|
||
|
||
Knowledge should not be trapped inside Sapling.
|
||
|
||
## Consequences
|
||
|
||
Any Sapling-specific features should degrade gracefully in standard Markdown environments.
|
||
|
||
---
|
||
|
||
# D-012 — Platform Focus
|
||
|
||
Date: 2025-08
|
||
|
||
Status: Accepted
|
||
|
||
## Decision
|
||
|
||
macOS is the primary target platform.
|
||
|
||
iOS support is a secondary objective.
|
||
|
||
## Rationale
|
||
|
||
The desktop writing experience is the primary use case.
|
||
|
||
Starting with macOS reduces complexity and accelerates development.
|
||
|
||
## Consequences
|
||
|
||
Architecture should remain cross-platform where practical.
|
||
|
||
Product decisions should optimize for desktop workflows first.
|
||
|
||
---
|
||
|
||
# D-013 — Editor Technology Selection
|
||
|
||
Date: 2026-05
|
||
|
||
Status: Accepted Provisionally
|
||
|
||
Review After: Milestone 2
|
||
|
||
## Decision
|
||
|
||
Sapling will use native platform text systems for the editor prototype:
|
||
|
||
- NSTextView on macOS
|
||
- UITextView on iOS
|
||
|
||
These views will be wrapped behind a Sapling editor abstraction.
|
||
|
||
SwiftUI TextEditor will not be used as the primary editor implementation.
|
||
|
||
## Rationale
|
||
|
||
Sapling's hybrid Markdown editor requires advanced control over cursor movement, selection state, layout, attributed rendering, and editing behavior.
|
||
|
||
SwiftUI TextEditor is useful for simple text entry, but it does not expose enough low-level editing hooks to validate the active-line source and inactive-line rendered model cleanly.
|
||
|
||
NSTextView and UITextView provide direct access to TextKit, attributed text storage, selection ranges, delegates, layout managers, and platform editing behaviors. That makes them better foundations for Milestone 1 validation.
|
||
|
||
## Consequences
|
||
|
||
Positive:
|
||
|
||
- The prototype can inspect and control selection ranges directly.
|
||
- Line-level styling and rendering experiments can be performed in-place.
|
||
- The app can preserve native editing behavior while testing hybrid Markdown concepts.
|
||
- The implementation can remain SwiftUI at the application layer.
|
||
|
||
Negative:
|
||
|
||
- AppKit and UIKit bridging adds platform-specific code.
|
||
- The editor abstraction must prevent the rest of the app from depending directly on NSTextView or UITextView.
|
||
- A future custom editor engine may still be required if line replacement or overlay rendering cannot preserve cursor correctness.
|
||
- Native adapter updates must isolate user-originated selection changes from programmatic text, selection, and attribute changes to avoid SwiftUI/TextKit feedback loops.
|
||
|
||
---
|
||
|
||
# D-014 — Hybrid Editor Architecture Validated
|
||
|
||
## Status
|
||
|
||
Accepted
|
||
|
||
## Date
|
||
|
||
2026-06-02
|
||
|
||
## Context
|
||
|
||
Sapling's core user experience depends on a hybrid editing model:
|
||
|
||
* active content is displayed as Markdown source
|
||
* inactive content is displayed as rendered output
|
||
|
||
This model combines advantages of:
|
||
|
||
* plain-text Markdown editors
|
||
* live preview editors
|
||
* rendered document editors
|
||
|
||
However, early in development there was uncertainty regarding:
|
||
|
||
* editor performance
|
||
* large-document scalability
|
||
* rendering determinism
|
||
* active-line tracking
|
||
* viewport stability
|
||
* TextKit suitability
|
||
* code block rendering
|
||
* interactive rendered elements
|
||
|
||
A significant portion of Milestones 1–3 was dedicated to validating the feasibility of this architecture before proceeding with workspace and Git functionality.
|
||
|
||
## Decision
|
||
|
||
Sapling adopts a hybrid rendered/source editing architecture as its primary editing model.
|
||
|
||
The architecture is considered validated and will remain the foundation of the application.
|
||
|
||
The editor will not be rewritten and no custom text engine is planned at this time.
|
||
|
||
## Validated Properties
|
||
|
||
The following properties have been demonstrated through implementation, profiling, and real-world testing.
|
||
|
||
### Large Document Scalability
|
||
|
||
Documents exceeding:
|
||
|
||
* 50,000 lines
|
||
* 5 MB of Markdown content
|
||
|
||
remain usable.
|
||
|
||
Profiling identified document-wide operations and replaced them with incremental approaches.
|
||
|
||
### Incremental Editing
|
||
|
||
Sapling maintains:
|
||
|
||
* incremental line indexing
|
||
* incremental invalidation
|
||
* incremental rendering updates
|
||
|
||
Editor interactions scale with the edited region rather than total document size.
|
||
|
||
### Rendering Determinism
|
||
|
||
Rendering output is derived from document state rather than interaction history.
|
||
|
||
Rendered content behaves consistently across:
|
||
|
||
* scrolling
|
||
* focus changes
|
||
* selection changes
|
||
* document reloads
|
||
|
||
### Editable Regions
|
||
|
||
The editor supports:
|
||
|
||
* single-line editing
|
||
* multi-line editing
|
||
* block editing
|
||
|
||
Rendered content transitions into source mode when actively edited.
|
||
|
||
### Rendered Elements
|
||
|
||
The editor supports first-class rendered elements including:
|
||
|
||
* headings
|
||
* task lists
|
||
* links
|
||
* code blocks
|
||
|
||
The architecture supports future rendered elements without fundamental redesign.
|
||
|
||
### Code Blocks
|
||
|
||
Code blocks are treated as semantic block elements rather than styled text.
|
||
|
||
They support:
|
||
|
||
* rendered containers
|
||
* syntax highlighting
|
||
* editable source transitions
|
||
|
||
### Viewport Stability
|
||
|
||
Rendered/source transitions preserve user context and do not require disruptive viewport repositioning.
|
||
|
||
## Technology Choice
|
||
|
||
The editor continues to use:
|
||
|
||
* NSTextView
|
||
* NSTextStorage
|
||
* NSLayoutManager
|
||
* TextKit
|
||
|
||
Investigation determined that observed performance bottlenecks originated primarily from Sapling's own document-wide algorithms rather than AppKit itself.
|
||
|
||
After introducing:
|
||
|
||
* incremental line indexing
|
||
* incremental invalidation
|
||
* region-based rendering
|
||
|
||
TextKit remained sufficiently performant for the project's requirements.
|
||
|
||
## Alternatives Considered
|
||
|
||
### Custom Text Engine
|
||
|
||
Rejected.
|
||
|
||
Reasons:
|
||
|
||
* significantly higher complexity
|
||
* increased maintenance burden
|
||
* no demonstrated need
|
||
* current architecture satisfies project requirements
|
||
|
||
### Split Editor / Preview Model
|
||
|
||
Rejected.
|
||
|
||
Reasons:
|
||
|
||
* interrupts writing flow
|
||
* increases cognitive overhead
|
||
* conflicts with Sapling's editing philosophy
|
||
|
||
### Full WYSIWYG Editor
|
||
|
||
Rejected.
|
||
|
||
Reasons:
|
||
|
||
* obscures Markdown source
|
||
* reduces portability
|
||
* conflicts with project goals
|
||
|
||
## Consequences
|
||
|
||
### Positive
|
||
|
||
* Markdown remains the source of truth.
|
||
* Editing remains fast on large documents.
|
||
* Rendered content improves readability.
|
||
* The editor architecture is stable enough for future feature development.
|
||
* Workspace and Git functionality can now be built on top of a proven editor foundation.
|
||
|
||
### Negative
|
||
|
||
* Hybrid editing introduces presentation complexity.
|
||
* Rendered/source transitions require ongoing testing.
|
||
* Some presentation-layer edge cases may continue to require refinement.
|
||
|
||
## Rationale
|
||
|
||
The editor is the core product experience.
|
||
|
||
Milestones 1–3 demonstrated that a hybrid rendered/source architecture can provide:
|
||
|
||
* Markdown transparency
|
||
* pleasant reading experience
|
||
* scalable performance
|
||
* future extensibility
|
||
|
||
without requiring a custom editor implementation.
|
||
|
||
Future development should build upon this foundation rather than revisit the editor architecture unless substantial new evidence emerges.
|
||
|
||
---
|
||
|
||
# D-015 — Filesystem Is The Source Of Truth
|
||
|
||
## Status
|
||
|
||
Accepted
|
||
|
||
## Date
|
||
|
||
2026-06-02
|
||
|
||
## Context
|
||
|
||
Sapling introduces a distinction between:
|
||
|
||
* ordinary folders
|
||
* versioned projects
|
||
* Git subprojects
|
||
|
||
A key architectural decision is determining where workspace state lives.
|
||
|
||
Two approaches were considered:
|
||
|
||
### Option A — Sapling-Owned Workspace Metadata
|
||
|
||
Sapling maintains a manifest or database describing:
|
||
|
||
* folders
|
||
* files
|
||
* projects
|
||
* attachments
|
||
* hierarchy
|
||
|
||
The filesystem becomes an implementation detail.
|
||
|
||
Advantages:
|
||
|
||
* complete control
|
||
* fast metadata access
|
||
* custom workspace structures
|
||
|
||
Disadvantages:
|
||
|
||
* duplicates filesystem state
|
||
* requires synchronization
|
||
* external modifications become difficult
|
||
* introduces risk of workspace corruption
|
||
* reduces interoperability with other tools
|
||
|
||
### Option B — Filesystem-Native Workspace
|
||
|
||
The workspace is a normal directory on disk.
|
||
|
||
Sapling scans and interprets the filesystem directly.
|
||
|
||
Advantages:
|
||
|
||
* simple mental model
|
||
* interoperability with external tools
|
||
* no synchronization layer
|
||
* naturally compatible with Git
|
||
* resilient to external modifications
|
||
|
||
Disadvantages:
|
||
|
||
* requires filesystem scanning
|
||
* metadata must be derived rather than stored
|
||
|
||
## Decision
|
||
|
||
Sapling adopts a filesystem-native architecture.
|
||
|
||
The filesystem is the authoritative source of truth.
|
||
|
||
Sapling does not maintain an authoritative database or manifest describing workspace contents.
|
||
|
||
Workspace contents are derived directly from the filesystem.
|
||
|
||
Projects are discovered through Git metadata.
|
||
|
||
Subprojects are discovered through Git submodules.
|
||
|
||
Files and folders remain ordinary filesystem objects.
|
||
|
||
## Workspace Model
|
||
|
||
Workspace:
|
||
|
||
```text
|
||
Workspace/
|
||
├── Notes/
|
||
├── Research/
|
||
├── Project-A/
|
||
└── Project-B/
|
||
```
|
||
|
||
Sapling scans the workspace root and builds its tree model from the current filesystem state.
|
||
|
||
Changes made through:
|
||
|
||
* Finder
|
||
* Terminal
|
||
* VS Code
|
||
* Cursor
|
||
* Xcode
|
||
* Photoshop
|
||
* external scripts
|
||
|
||
must appear naturally inside Sapling.
|
||
|
||
No import or synchronization step should be required.
|
||
|
||
## Project Detection
|
||
|
||
A directory containing:
|
||
|
||
```text
|
||
.git/
|
||
```
|
||
|
||
is considered a project.
|
||
|
||
Projects receive additional capabilities:
|
||
|
||
* Git status
|
||
* commits
|
||
* branches
|
||
* remotes
|
||
* history
|
||
* Git LFS
|
||
* submodules
|
||
|
||
Folders without Git metadata remain ordinary folders.
|
||
|
||
## Subproject Detection
|
||
|
||
Git submodules are treated as first-class Sapling subprojects.
|
||
|
||
Subproject discovery is derived from Git configuration rather than Sapling metadata.
|
||
|
||
## Persistence
|
||
|
||
Sapling may persist application state separately.
|
||
|
||
Examples:
|
||
|
||
* recent workspaces
|
||
* window state
|
||
* open tabs
|
||
* sidebar width
|
||
* editor preferences
|
||
* UI configuration
|
||
|
||
This data must never become the authoritative representation of workspace contents.
|
||
|
||
## Consequences
|
||
|
||
### Positive
|
||
|
||
* Workspace remains human-readable.
|
||
* Workspace remains tool-agnostic.
|
||
* Users can manipulate files outside Sapling.
|
||
* Git integration remains natural.
|
||
* Cloud synchronization solutions work without special support.
|
||
* Workspaces remain usable even if Sapling is uninstalled.
|
||
|
||
### Negative
|
||
|
||
* Workspace state must be derived from filesystem scans.
|
||
* File watching becomes important.
|
||
* Some metadata may need caching for performance.
|
||
|
||
## Rationale
|
||
|
||
One of Sapling's core values is ownership.
|
||
|
||
Users should own their notes, projects, attachments, and repositories without depending on Sapling-specific storage formats.
|
||
|
||
A workspace should remain a normal directory that can be understood and manipulated using standard operating system tools.
|
||
|
||
Sapling should adapt to the filesystem rather than requiring the filesystem to adapt to Sapling.
|