Reams #
A mobile-first, offline-first, local-first web-based markdown editor with Obsidian plugin API compatibility, backed by Automerge CRDTs.
Start here #
Read current architecture and vocabulary. Use the documentation map for additional context as needed.
docs/architecture.md is the maintained architecture entrypoint. Historical
decisions and plans record their dated context; consult them when investigating
why something changed, not as startup instructions or current authority.
For ATProto account lifecycle work, the user-aligned contract is local-first lifecycle. Older offline-fallback, starter-classifier and account-only logout directions are superseded; do not recreate them from historical verification evidence.
Tech Stack #
- Vite + React 18 + TypeScript (strict)
- CodeMirror 6 editor (with @codemirror/lang-markdown, @codemirror/autocomplete, view, state)
- Automerge CRDTs with edition-specific repository runtimes: Classic (IndexedDB + BroadcastChannel + optional WebSocket) and ATProto (
automerge-atproto) - PWA with service worker
- NixOS flake dev environment (Node 22, automerge-repo-sync-server via automerge-repo-nix flake input)
- Chromium and other development tools are available through the Nix development environment; use it when browser or tool availability is needed for verification.
Browser test environment #
Browser-backed tests and the Bombadil lane require the Nix development shell, which provides Chromium and the other browser prerequisites. Run browser tests through Nix when the host environment does not expose them directly:
nix develop --command sh -c 'RUN_BROWSER_TESTS=1 PLAYWRIGHT_CHROMIUM_BIN="$(command -v chromium)" npm run test:bombadil'
The lane still requires RUN_BROWSER_TESTS=1 and PLAYWRIGHT_CHROMIUM_BIN;
the Nix shell supplies the Chromium executable path in the supported setup.
Prefer targeted checks during iteration. Run slow browser/performance campaigns at meaningful checkpoints, and keep browser/fuzzer runs on the main thread. When delegation is requested, use fresh, small, focused subagents with explicit handoffs rather than resuming old sessions.
Development Workflow #
Implement, test, and review locally by default. Deploy only when the user explicitly requests a release; do not deploy each implementation slice automatically.
This project uses three tools that work together: Chainlink for issue tracking, Trycycle for automated planning/building/reviewing, and /handoff for session continuity.
Commit reflection #
Before each model-created commit, briefly consider whether the work exposed unnecessary friction. If so, add or update a short entry in problems.md with the affected path, problem, and smallest useful improvement. Reuse existing entries and remove them when fixed. If nothing useful emerged, leave the file alone.
Planning doctrine: “Why Software Factories Fail” #
Keep Why Software Factories Fail as a standing reference when shaping epics, Chainlinks, and implementation plans.
Its thesis for Reams:
- Harnesses, tests, review agents, and more loops raise the floor, but they cannot reliably judge long-term maintainability. Passing tests is a fast oracle; architectural quality often reveals itself only months later. Human steering and code review remain necessary.
- Front-load alignment for consequential work at four levels: product outcome, system architecture, program design (types, signatures, call stacks, and file layout), then implementation slices. Do not substitute a large technical task list for product and design decisions.
- Prefer vertical slices / tracer bullets over horizontal layer-by-layer plans. Each slice should deliver the thinnest touchable end-to-end behavior across the real boundaries it needs, then be run, inspected, and reviewed before widening it.
- Avoid plans shaped as “build all storage, then all services, then all UI.” Start in the middle with a real contract and observable result; use temporary data only where needed to make the slice runnable, then replace it boundary by boundary.
- Keep slices small enough to review and resteer cheaply—roughly 100–200 meaningful changed lines when practical, or one to three coherent slices at a time. A green slice must be demonstrable through the actual user or operator surface, not only through isolated tests.
- Optimize for sustainable 2–3× leverage, not maximum code throughput. Read the code, preserve conceptual integrity, and stop before stacking more experiments on an unmerged or unverified foundation.
For Chainlink epics, subissues should therefore usually be vertical product increments with their own runnable evidence—not horizontal component buckets. Infrastructure-only work is appropriate when it is itself the smallest independently verifiable risk-reduction slice or is required to unlock the next end-to-end increment.
Chainlink (Issue Tracker) #
Chainlink is a CLI issue tracker stored in .chainlink/. Issues are organized as epics (Chainlinks 1-9) with sub-items. Each sub-item is a single unit of work for one trycycle run.
# See the full issue hierarchy
chainlink tree
# Get the next issue ready to work on (no open blockers)
chainlink next
# Read full issue details including comments
chainlink show <id>
# Read the selected issue's parent epic
chainlink show <epic-id>
# Mark an issue as in-progress
chainlink update <id> --status in-progress
# Close a completed issue
chainlink close <id>
# Add notes about what was done
chainlink comment <id> "description of what was built"
When working on a Chainlink issue, read that issue and its relevant ancestors. Older issues provide history when needed; they do not replace the current architecture.
Trycycle (Automated Build System) #
Trycycle is an OpenCode skill that automates planning, building, and reviewing code. It's installed at ~/.config/opencode/skills/trycycle/.
To use trycycle for a chainlink sub-item:
- Load the trycycle skill
- Say:
Use trycycle to implement: <paste the issue title and description from chainlink show> - Trycycle will:
- Ask clarifying questions if needed
- Propose a testing strategy (requires your approval)
- Create a git worktree
- Write an implementation plan (reviewed up to 5 rounds by @adversary)
- Build a test plan
- Implement the code (reviewed up to 8 rounds by @adversary)
- Present options: merge, PR, keep branch, or discard
Trycycle uses @adversary for both plan review and code review. This is configured in the trycycle SKILL.md. The adversary agent provides harsh, critical review to catch flaws early.
Key files:
~/.config/opencode/skills/trycycle/SKILL.md— main orchestration skill~/.config/opencode/skills/trycycle/subskills/— planning, executing, finishing, worktrees~/.config/opencode/skills/trycycle/subagents/— prompt templates for subagents
Obsidian Contract (Repo-local skill for upstream API tightening) #
obsidian-contract is a repo-local skill at .agents/skills/obsidian-contract/ for tightening Reams against the official upstream Obsidian API one contract failure at a time.
Use obsidian-contract when:
- The work is driven by
npm run obsidian:contract. - You want to fix one upstream contract failure end-to-end in a worktree.
- The task is specifically Obsidian API compatibility tightening, not broader product work.
To use obsidian-contract:
- Load the
obsidian-contractskill from.agents/skills/obsidian-contract/. - Say:
Use obsidian-contract. - The skill will:
- Run the upstream contract report.
- Pick one failing symbol at random.
- Create a worktree from
main. - Fix that one failure.
- Run an adversary review.
- Re-run the contract report and targeted tests.
- Merge back to
main.
Key files:
.agents/skills/obsidian-contract/SKILL.md— the contract-fix loopscripts/obsidian-api-contract-report.mjs— upstream contract report generatorvendor/obsidian-upstream.d.ts— official upstream API snapshot
/handoff (Session Continuity) #
The /handoff command (from the opencode-handoff plugin) creates a continuation prompt for the next session, preserving context and file references.
/handoff <goal for next session>
This generates a prompt in a new session with:
- Summary of what was accomplished
- 8-15 @file references for relevant source files
- Goal and context for continued work
- Reference to the previous session (accessible via read_session tool)
End-to-End Workflow #
The complete flow for building Reams, issue by issue:
1. START SESSION
# Read docs/architecture.md
chainlink next # Get next ready issue
chainlink show <id> # Read full issue details + comments
2. BUILD WITH TRYCYCLE
Use trycycle to implement: <issue description>
# Trycycle handles planning, building, reviewing
# @adversary reviews plans and code
3. ON SUCCESS — IMPORTANT ORDERING
# Chainlink writes to .chainlink/issues.db when you close or comment.
# Close the issue FIRST (in the worktree branch) so the db change is
# included in the same commit as the implementation, BEFORE merging.
# Otherwise the close happens on main as a separate commit, polluting
# history and requiring a follow-up merge.
chainlink close <id> # Close the completed issue (in the worktree)
chainlink comment <id> "Built in worktree at <path>. Ready to merge."
git -C <worktree-path> add .chainlink/issues.db
git -C <worktree-path> commit -m "chore(chainlink): close #<id>"
# NOW merge to main
git checkout main && git merge --no-ff <branch>
4. CONTINUE OR HANDOFF
Option A: Continue in same session
chainlink next # Get next issue
# Repeat from step 2
Option B: Hand off to new session
/handoff Continue building Reams. Next issue: <next issue title>
# New session starts with full context
When a chainlink epic's sub-items are all closed, close the epic too:
chainlink close <epic-id>
Chainlink Status #
Use chainlink tree, chainlink next, and chainlink show <id> for live status.
Keep issue status in the tracker rather than maintaining a second table here.