atelier RSS
Systems note โ€” architecture, part two

The View Layer: A Dashboard, a Chat Surface, and a Calendar on Top of a Memory That Didn't Need Any of Them

Part one argued the memory store stands on its own โ€” markdown and git, legible with nothing but a text editor. This half is about what got built on top of it anyway, and why none of it quietly undoes that property.

read time ~9 minยท architecture / dashboards / sync protocols

Two claims, only one of them made

Part one made a point of the memory store not needing a dashboard to stay trustworthy. Still true, still standing by it: a text editor and git log get you all the way there.

But "doesn't need one" and "gets nothing out of one" aren't the same claim, and I only ever made the first one. A directory of markdown files is legible. It's not convenient. Reading commit logs cold every morning just to reconstruct "what's active, what changed, what needs my attention" is a real tax, and it's one a person keeps paying for something a computer could just compute once and hand over. That's the whole job of everything in this piece โ€” dashboard, chat, calendar, all reading and writing the same substrate from part one, none of them a second source of truth. A lens. Not a dependency.

01The brief: the same principle, aimed at a screen

Every session opens with a generated brief โ€” active projects, what changed last time, anything urgent โ€” built the same way as part one's "derive, don't hand-maintain" idea: pulled from frontmatter and git history, not read off a status doc someone kept up by hand. Nothing new here. A dashboard is just a second place that principle gets to pay off, glanced at instead of consumed by a script.

Putting a freshness signal on an actual screen surfaced a mistake the terminal version never had to face. An early "how stale is this" indicator implied real-world recency โ€” how long since something actually happened โ€” when what it measured was documentation recency: how long since someone wrote a sentence about it. Those two diverge in a specific, annoying direction. A project looks frantic because every change touches a tracked file. A project getting real, hands-on attention looks idle because nobody filed a note about a morning spent on it instead of at a keyboard.

The dashboard doesn't know what you did. It only ever knows what you wrote down.

The fix wasn't a better metric โ€” there isn't one, not from inside a text corpus โ€” it was not letting the label claim more than the data actually backs up. "Busy lately" became "written about a lot lately." "Gathering dust" became "no word in a long time." Small wording change, real correction: moved the signal from a false claim about the world to a true claim about the record, which is the only kind of claim a system built entirely on what people bothered to type is ever entitled to make. Worth running any derived indicator through the same check โ€” does the label match what's actually measured, or just what you wish it meant?

02Chat as a second write surface, same boundary, finer grain

A phone-accessible chat interface turns the dashboard from a read-only view into a second write surface โ€” talk to the store, have it write back, from wherever a laptop isn't. Same trust-tiering idea as part one, just one level finer than "which surface." Within a single conversation, on a single surface, the write path branches by what kind of file is being changed.

write policy โ€” enforced outside the model's control
on turn complete:
  changed = diff(before, after)
  for file in changed:
    if file.ext == '.md' and file.path not in code_dirs:
      commit_directly(file)          # memory: reviewed after the fact
    else:
      route_to_pr_branch(file)       # code/config: reviewed before it lands
  # the agent never calls git โ€” it edits files;
  # a supervising process decides what happens next

The chat model has no git tool and no direct write access. It edits files the way any editor session would, and a process outside the conversation does the actual commit once the turn's over โ€” deciding the write path by file type, not by asking the model to police itself. This is part one's "sub-agents return data, only the orchestrator writes" boundary, just literal instead of metaphorical this time: the chat model is the sub-agent, the server process is the orchestrator, and the write policy was never something the model could get talked out of, because applying it was never the model's job to begin with.

03The gate that rejects, not deletes

Part one told the story of a bug where a pre-merge check accidentally destroyed the work it was supposed to be reviewing โ€” a cleanup step ran on every exit path out of a function, including the failure one, so a failing check meant "revert everything, commit nothing, say nothing." That piece drew the lesson about silent failure. The one worth carrying into this piece is about what a gate is actually for.

the rule

A review gate has exactly two acceptable outcomes: pass, or reject-and-keep. The second its failure path can make work disappear, it's stopped being a quality gate and started being a shredder wearing a quality gate's name.

A paper shredder destroying a document.
what the old lint gate was, functionally

The fix was just an ordering change, and the ordering is the part that generalizes. The failing version ran the check first and only assembled a real commit if it passed, so a rejection had nothing durable to land on. The working version assembles a recoverable commit on a local branch first, runs the check against that, and only afterward decides whether it goes further or just gets reported as "failed, here's where it's parked." Same check. Same failure rate. The only thing that moved is which side of it the point of no return sits on.

04The calendar: same instinct, a different domain

The last view is a calendar โ€” a second, bidirectional interface for controlling when scheduled agent work happens, alongside dragging, deleting, and rescheduling by hand. Two decisions here are worth separating, because one is a restraint and the other is a reuse.

Reuse: an existing sync standard, not a bespoke format

Instead of inventing an event format and a bespoke sync protocol, this runs on an existing, widely supported calendar-sync standard โ€” same "don't build what's already solved" instinct that picked git over a bespoke memory format back in part one. Any standard calendar client can subscribe with zero custom software on the reading end. The actual work is entirely in exposing the repo's schedule data through a protocol that's already had decades of interoperable clients built against it.

Restraint: mechanical sync only, no interpretation yet

The tempting next step โ€” let a model read a freeform event description and turn it into a new task โ€” got left out of the first version on purpose. What shipped is strictly mechanical: reschedule, delete, edit a recurrence rule, sync cleanly both ways. No model sits between an event and an action yet. That's a real capability being held back, not a limitation I'm stuck with โ€” the interpretive layer needs an actual pattern of use to design against, and guessing at that pattern now means building a judgment call with zero evidence for what judgment it should make. Ship the plumbing. Live with it. Let real use tell you what the smarter version actually needs to handle.

Getting even the mechanical version live taught me one structural lesson: a client library's "discover the available calendars for you" step can be structurally unable to see a calendar that was deliberately placed outside any single account's own space. A shared collection meant for more than one reader just doesn't live in the path that kind of discovery walks, no matter how correctly the permissions are set. The fix wasn't a workaround inside discovery โ€” it was skipping discovery entirely and pointing the client straight at the known location.

Two astronauts in space, one pointing a gun at the other, the always-has-been meme.
me, after finding out discovery could never see it, structurally, from the start

An abstraction that promises to find things for you is only doing you a favor when the set of things is genuinely unknown. When it's small, closed, and already known, asking the abstraction to rediscover it is pure overhead โ€” and once in a while, just not possible.

memory repo server process sole write path brief read only chat read + gated write calendar read + mechanical write
Three views with three different interaction models, all reaching the repo through exactly one mediating process. Adding a fourth view adds a consumer, not a new place the store can be written to.

05Access grows sideways, not from scratch

Every one of these views eventually raises the same question: what happens when a second person needs to read or write this system? Honest answer โ€” most of that work was already done, by accident, because of how trust got modeled in the first place.

Permission was never attached to an account. It's attached to the content: a flag on a file marking it as something that takes a deliberate act to open, rather than something a reader just drifts into, checked before that content ever reaches a model or a screen. That check doesn't ask who's asking. So when a second reader shows up, the gate that already existed just keeps working, unmodified. What's missing isn't a permission system. It's a thin layer of identity to tell two people's sessions apart, sitting on top of protections that never needed to know the difference.

Cheap extensibility, and I didn't buy it on purpose โ€” it's a side effect of modeling trust as a property of the content instead of the account holding the door. Worth doing on purpose next time instead of noticing it as a lucky accident afterward.

Where this leaves the pair

Part one argues the substrate has to work with nothing on top of it. Part two argues that "nothing on top of it" was never actually the goal โ€” it was the constraint that kept everything built on top of it honest. Delete every view in this piece tomorrow and the store loses exactly nothing except convenience. Which, it turns out, is the whole reason the convenience was worth building in the first place.