Scroll is a document application built on relative viewport anchoring: a collaborative editor where remote edits above your viewport never move your screen. It starts as a single-user editor and grows into a multi-user, agent-aware workspace. It exposes its editing surfaces as spawnable endpoints so external systems (PersonalServer, STARfolio) can drive them without reaching into the native app.
The docs/ folder holds the planning record that preceded implementation, so scope and sequencing
survive. The editor, sync server, and agent surfaces are now built; see the retrospective below.
-
Relative viewport anchoring for multi-user documents — a collaborative editor where remote edits above your viewport never move your screen. Google Docs, Confluence, and Overleaf transform the caret correctly through concurrent edits but do not transform the camera. Scroll anchors both. See architecture/relative-viewport-anchoring.md.
-
Attention-anchored collaborative editor — a document where the viewport is a lock. Users write, an agent reorganizes, and nobody's screen ever moves under them. This is the native app value prop, an offshoot of the anchoring primitive. See architecture/attention-anchored-editor.md.
Scroll can spawn single-person editing endpoints on demand. Endpoint spawning is a feature exposed in the app itself.
- doc-es — document endpoint spawner (prose editor).
- ide-es — IDE endpoint spawner (code editor) with schema attachability, spawned by a
create_ide_esfunction or similar spawn logic (referred to as "sl").
Programmatic consumers (starting with PersonalServer) do not run the editor. They seed the schema (goal condition, problem, test-case + TLE budget, hints, and so on) and let Scroll spawn the endpoint.
See architecture/endpoint-spawners.md.
PersonalServer and STARfolio should control endpoints, not Scroll's native app processes. The endpoint-spawner seam exists for Open/Closed adherence: consumers extend Scroll by driving endpoints, not by modifying the app. This is a design bet, not a proven fact. It is recorded as such in open-questions.md.
Scroll used property-based testing (fast-check) from its first stateful component. This measures what that bought, honestly. It splits into two questions with two different answers.
The mutation sweep: ~100% of injected faults caught by 10 cases, with execution-order faults falling slowest.
Weaken the oracle or the generator and worst-class detection drops to 80%. The ceiling is earned by harness design, not luck.
Sensitivity (could PBT catch faults?): near-total. A mutation sweep on TreapOrderIndex (20 hand-built mutants across 5 fault classes, case budgets from 1 to 3000, 10 seeds each) reaches ~100% detection by 10 cases. Execution-order and linkage faults fall slowest. A whole-suite StrykerJS pass kills 86–95% of mutants on covered code.
Realized yield (did PBT fix real wrong code?): about one. The record shows a single organic catch: a >/>= off-by-one that the unit tests passed.
Why realized yield can't be recovered from git, and why that is the finding. The build ran an autonomous loop where an agent fixed defects mid-development, before commit. Each PR then squash-merged to one commit. So the broken intermediate states, exactly what a causal count needs, never entered version control. orderIndex.ts has three commits: create, comment-pass, treap rewrite. Zero fix commits. The one catch on record survived only because a dev transcript logged it. n=1 is a floor the record imposes, not a ceiling on PBT.
What PBT actually bought was preventive coverage of the one high-risk change. The treap rewrite dropped +225 lines converting a simple array into an O(log n) structure, guarded by a differential property that checks the treap against the array/Fenwick oracle on every method after any op sequence. It shipped without a regression. The sweep is sensitivity ~100% on exactly the fault classes that rewrite could introduce.
Method and limitations, stated plainly. The mutation sweep measures sensitivity, not realized bugs: the mutants and the oracle are ours, so it is a capability measure by construction. Squash-merge plus in-loop fixing make realized yield unrecoverable from version control. A project that wants that number should log every property-test failure at the moment it fires, not reconstruct it later.
Harnesses, results, and the figure source live in analysis/pbt/.
- concept.md — the full vision and how the pieces relate.
- roadmap.md — the phased build order.
- architecture/endpoint-spawners.md — doc-es, ide-es, schema attachability, the OCP seam.
- architecture/relative-viewport-anchoring.md — value prop 1, mechanism, hard parts, substrate.
- architecture/attention-anchored-editor.md — value prop 2, residency, guards, agent loops, provenance.
- architecture/boundaries.md — the interface contracts between Scroll and its consumers: dependency direction, peer protocol, spawn schema, programmatic Strategy, grader trust boundary, and a review checklist.
- architecture/distributed-systems.md — the hard core: guarantees, failure modes, single-authority-per-room, GC safety, the agent as a peer, pre-answered reviewer questions.
- architecture/storage-and-persistence.md — how a Y.Doc persists single-user to multi-user, compaction, durability rules.
- architecture/whiteboard.md — build-vs-buy for the STARfolio whiteboard (Konva+Yjs vs Excalidraw vs tldraw).
- integrations/personalserver.md — programmatic schema seeding via ide-es.
- integrations/starfolio.md — STARfolio as a second user and AI interviewer.
- architecture/sample-implementation.md — illustrative diagrams (structure + two flows) with the contracts on the edges.
- open-questions.md — the forks and unproven theses.
- fable-review-brief.md — the adversarial-review prompt to hand to Fable.

