- Published on
SAM: I built my own project director because coordinating everything by hand stopped being enough
- Authors

- Name
- Sandy Veliz
- @sandy_veliz
Being full-stack used to be rare. Writing the frontend, the backend, designing the database, understanding deployment, holding an application's entire architecture in your head — few developers could handle all of that at once, and the ones who could were slow. That's why teams existed: someone for frontend, someone for backend, someone for infra.
Today that barrier has moved. With agents writing code, the cost of writing code has dropped to practically zero. What didn't get cheaper is everything else: architecture, system design, database decisions, deployment, and the coordination between all those pieces. That's the work that remains, and it's the work that doesn't scale just because code ships faster.
That's the premise of this post and the reason I built SAM: an orchestrator I use to coordinate everything that happens at Alto Valle Studio and in my clients' projects — tickets, agents writing code in parallel, test environments, branches, pull requests — so that I stop being the bottleneck.
Code became cheap; everything else didn't
When writing code stops being the limit, what slows you down is something else: having to learn and sustain architecture, system design, databases and deployment for every project you carry. And if you carry several projects for several clients at once — which is my case — that load doesn't add up, it multiplies. Every new context (what stack this client uses, how it deploys, what conventions that repo has) is time you won't get back by writing code faster, because code was no longer the problem.
That's where I found myself being the funnel: not because I couldn't write the code — the agents write it — but because every agent working on every project needed me to hold in my head what state everything was in. Which ticket was in which worktree, which branch was real and which invented, which environment was still running and which had already died. Coordinating that by hand, for a handful of clients with several projects each, stopped being humanly sustainable quite a bit sooner than I expected.
The phrase that stuck with me when I sat down to seriously diagnose this was: SAM modeled the work very well, but it didn't model the state of the world. Tickets had a file on disk, a lifecycle, a history. Environments, branches, worktrees and PRs didn't — they were calculations recomputed every time I opened the screen, and lost the moment the process restarted.
A concrete example: a ticket's environment lived in memory. Restart the server — something that happens all the time in development — and that information vanishes, even though the actual process is still running on some port. A ticket's branch wasn't even stored: it was derived from the ticket's id, which worked perfectly until a ticket ran in a mode where that branch had never existed, and the system lied with complete confidence.
Hence the lesson that ended up becoming the design principle for everything else: whatever isn't an entity with its own lifecycle will, sooner or later, lie to you.
From full-stack to technical lead
The way I solved this wasn't writing faster. It was ceasing to be the one who holds each project's state, and handing that load to something that doesn't forget.
In practice, this turned me into something resembling a technical lead, and SAM into something resembling a PM or a PO: I decide what gets done, review the result and give the final verdict; SAM coordinates the agents that write the code, and takes care of remembering what state every branch, every environment, every worktree was left in — the cognitive load that used to be mine. The result, for me, is direct: I can ship more things, faster, and sustain several projects side by side without each one stealing the previous one's context.
I believe that's where we're all headed, not just me. If code is no longer the bottleneck, the developer's role shifts toward coordinating, deciding and reviewing — and that coordination needs its own tool, just as much as code needs its own.
What SAM is, in practice
Architecturally it's simple: a React dashboard, a Node.js engine, and a Python sidecar for voice-to-text transcription — so I can dictate a ticket instead of typing it. Everything running locally on my Windows machine, with git worktrees per ticket and dynamically derived ports so test environments don't step on each other.
But what matters isn't the stack, it's the cycle:
1. You define the ticket → what it's about, what criteria close it
2. An agent picks it up → its own worktree, tools (read, edit, bash, real browser)
3. It reports when done → structured summary: what it did, against which criteria, what's left open
4. SAM spins up the env → a real healthcheck, not a timer assuming that if it didn't blow up, it works
5. You give the verdict → you actually try it, you don't take the agent's word for it
6. One single path to close → merge + delete branch + clean worktree + verify against GitHub
That "one single path to close" is no small detail. There used to be several different ways to call a ticket done — dragging it on the board, approving it from the chat, closing it by hand from GitHub — and only one of them actually cleaned up. The others were silent factories of loose branches and unmerged PRs.
The one I talk to on the other side is always the same SAM, regardless of context: I can talk to it about a specific ticket or about the whole portfolio in the same session, and it's still the same agent, with the same conversation thread. The scope changes — sometimes it's looking at one ticket, sometimes at the whole board — but there are no separate versions per context and no different characters depending on what I bring up.
The name, by the way, is not random. I called it SAM after Samwise Gamgee, the one who, in The Lord of the Rings, carries Frodo through the final stretches, when he can no longer walk on his own. I liked the image of an agent that carries you along. What I didn't calculate, when I named it, was that the joke would become literal: today it is, effectively, the system that carries me — with my own work and my own mess. It's still, to me, the best description of why this tool exists.
How it's working today
Illustrative screenshot with simulated data — this is not any client's real board.The real portfolio today is several clients, ten products, seventeen projects. Inside that there's Alto Valle Studio — my own studio, with Walletfy and Checkear as products —, my biggest client (a distributor with internal and external systems, mobile and web), and Cooperativa del Valle, a fictional project I use on purpose to stress edge cases without risking anything real.
Usage numbers, as of this writing: a hundred tickets, a hundred and fifty-some chat sessions, and over a hundred log entries and recorded agent runs.
And here goes the anecdote that embarrassed me the most in this whole process: for months, the folder where all of that lives — the tickets, the sessions, the log, everything — was not a git repository. SAM's code repo was, but it explicitly ignored that folder. Pushing the code backed up nothing of the workspace. If my disk had died on any given day, it would have taken with it a hundred tickets and the entire memory of what had happened, with no backup of any kind.
I fixed it as soon as I saw it: an initial commit of around 600 files, about 11 MB. It's a silly mistake in retrospect, but it's exactly the kind of thing a system you use every day lets you overlook — I was so busy watching whether tickets moved correctly between columns that I never asked myself where they lived.
Then the model changed again. When SAM became a single-instance tool, with its home fixed at ~/.sam, the workspace git stopped making sense and I replaced it with something dumber — and therefore harder to break: a button that zips the entire home into a folder that Drive already syncs. Less elegant than a commit history, but it's a backup that doesn't depend on me remembering anything.
The idea behind all of it
Three decisions repeat across every part of the system, and they're SAM's real backbone, beyond any specific feature.
Pointers, not content. The central index doesn't store documentation, it stores references to where that documentation lives. If a product has a repo, the source of truth is what's written in that repo — SAM only maintains a derived index that gets regenerated. If it doesn't have a repo (a client, a product that groups several apps), then SAM is the source, because there's no other place that information could live without duplicating itself.
Apps outlive repos, not the other way around. The hierarchy is Client → Product → App → Repo, with Repo as a separate, transversal entity. Why? Because in the middle of a migration, the same app can literally live in two repos at once — the old one shrinking, the new one growing — and it's still the same app: same users, same production URL, same history. If you model App as if it were the same thing as Repo, you lose all of that exactly when you most need not to lose it.
Everything that can get dirty needs a lifecycle and reconciliation, not a calculation recomputed when you look. This is the direct lesson from the underlying problem. An environment is not a timer assuming that if it didn't blow up, it's healthy — it's a real healthcheck against the port, with persisted state, that on startup asks itself "is what this file says still true in the world?" before trusting anything.
And the simplest of the three, though debatable: plain markdown, not a database. A hundred tickets, some six hundred files, eleven megabytes — the scale doesn't call for it. Agents already read markdown natively; putting a database there is extra serialization with no real gain. The backup for that, as I told above, is not git today — it's the zip to Drive. But the archive is still plain text readable by anyone, with or without SAM running, which is the part of the decision that didn't change.
What doesn't work (yet)
Being honest here seems more useful to me than selling a perfect system.
Session number 108. The chat panel became unusable at some point, and not by accident: every time you open a ticket that's already finished, the system fabricates a new session instead of reusing a closed one. The result is a sidebar that grows without end and that I've already had to prune once.
A bug that almost cost me real work. During a ticket's close, in a certain mode, the system committed the worktree's dirty changes locally, but didn't push them. Then it merged on GitHub whatever the remote had — without that commit — and only then deleted the local and remote branches. The commit with the unsaved changes vanished in the process, despite the code carrying a comment that literally said nothing that followed could delete work. I found it reading the code with a magnifying glass, not because it blew up in production — but it's the kind of bug that makes you trust a system less until you've fully closed it out.
Only one case out of three is actually operated. Of the three modes a ticket's environment can be in (local development with its own worktree, single checkout without a worktree, remote preview), only the first one has real buttons. The other two are read-only or outright unorchestrated — and it turns out the two projects I use most in local-without-worktree mode (SAM itself, and my biggest client) fall exactly in the case that doesn't have a single button.
Where it's headed
The redesign in progress can be summarized in three layers, in order of dependency:
INVENTORY layer → reconciles against reality, always, in the background
(is the process alive? does the port respond? what does git actually say?)
STATE layer → Runtime, Branch, Pull Request as persisted entities,
each with its own health and debt
ACTION layer → one click resolves: real terminal, merge, cleanup
One model change I'm particularly excited about: going from one worktree per ticket to one worktree per feature (an epic with several tickets inside). Today, a client with five features in flight has their five tickets serialized against the same checkout. With one worktree per feature, those five run in parallel for real, and I test the whole feature integrated once instead of five loose little pieces.
This is what the mockup of the new screen looks like today, in the lens that solves exactly that — what's ready to test, where it's running and how to test it, feature by feature:

And the same screen, in the lens that shows the full portfolio grouped by client — the view that replaces having to open fifteen projects to know how each one is doing:
Both screenshots are from the navigable mockup, with simulated data.And further ahead, phase 2: going from being just a ticket manager to being a platform that also runs what those tickets produce — its own infrastructure, a real preview per worktree, and tickets that can be born from a signal (a funnel dropping, an error affecting several users) and not only from an idea of mine written by hand.
The strange thing about all this is that SAM is, at the same time, the tool and the object of its own problem: I build it with the same agents it later coordinates, in the same worktrees it will later have to reconcile against reality. Every bug I find in the system that manages my work is, literally, my next work ticket.
I'm still the only developer on this, on a Windows PC, trying to keep the tool I use to avoid being the bottleneck from becoming, itself, something that needs watching by hand. But I'm betting that this way of working — technical lead plus orchestrator, instead of a developer juggling everything — is where most of us who carry several projects at once are going to end up.