The studio
The studio is your codebase in the browser, on your machine: what needs you, code to read, edit and run as a notebook, tests, what tcm is running, how the app runs (its traces), data, branches and agents' sessions, history and releases.
$ tcm studio tcm studio /home/ada/board starting (its first start, and the first after tcm changes, builds it: a few seconds) open http://127.0.0.1:4747/#token=3f9c… (signs this browser in)
It opens in your browser and stops when tcm studio does.
Starting it
- The link signs you in. The studio runs the codebase's code, so it takes a token:
.tcm/studio/token, readable only by you, made the first time. The link carries it in its fragment (never sent to a server); the page signs in with it (anHttpOnly,SameSite=Strictcookie) and removes it from the address. Without it, a browser gets only the sign-in page. - It listens on 127.0.0.1 only, port 4747, or a free one if that's taken.
--portasks for another.--no-openprints the link without opening a browser; otherwise$BROWSER, if set, opens it. - One studio per codebase. Run
tcm studioagain and it opens the running one. - It starts on the codebase's own branch (the one
tcm switchmoves), whichever branch you ran it on. - The branch menu in the header reads another branch in this browser: the codebase's own, other branches, and
agents' sessions (active ones marked; those changed in the last day first, the rest under Older sessions). Every
page then reads that branch read-only, as
--branchdoes, with a notice under the header until you go back. A branch named on a page (an agent's, a slot holder's) opens it too. - First start builds it, installing what it uses (React) from npm. It's copied into
.tcm/studio/(again whenever tcm changes), with a.gitignoreso nothing there is committed. It needs a tcm installed from its package.
What's in it
The column on the left has its pages.
Overview
What runs worse than its version before and the change that did it; what's running on the codebase (each tcm dev,
start, serve and the studio, to open); agents' sessions; scratch files holding work; the last day's answers and
errors; what needs fixing; the latest history entries. It refreshes every few seconds.
Code
The codebase's front page, namespaces and definitions, and the finder (⌘K: names, words in docs, types), as the web UI shows them. A definition's page also says how it runs on this machine, from the last day's traces: what runs worse than before and what changed; each version side by side (calls, errors, p50, p95); where its calls came from in the browser; its slowest traces.
- Edit writes it into a scratch file and opens it, as
tcm editdoes (scratch.ts, orscratch.tsxfor JSX, added to what's there; a file kept as written, Markdown say, into its own file). - Rename gives it another name, as
tcm mvdoes (another namespace moves it). What uses it follows. - Delete removes its name, as
tcm rmdoes, after naming the definitions that use it. They keep working (they use it by hash), and history keeps it.
Scratch files
Unsaved work, newest first: the .ts and .tsx files beside the codebase, and files kept as written being edited
(each with where it goes back). New scratch file makes one.
A scratch file's page is an editor. What you write is saved to the file and checked as you type (a pause, or ⌘S), as the editor checks it:
- TypeScript's problems, tcm's and oxlint's warnings are marked in the code and listed under it, each linking to its line (oxlint's never stop an update).
- With no problems, it shows what updating would do to each definition (new, an update, unchanged). An update that changes a type says so, with the definitions using it that are checked again; Pull them in brings them into the file.
- Hovers and completions are TypeScript's. Hovering a codebase name shows its names, hash, signature and doc; completing one the file doesn't import adds the import. A missing import is fixed from a button by the problem.
- A name's hover offers Pull in (its code into this file, as
tcm editadds it), Go to it (⌘-click too), References (where the file and the codebase use it, each to pull in, or all at once), and Rename (in the file, or, for a codebase name, in the codebase too, astcm mv: a dotted name moves it; the import follows). - Pull in… finds any definition by name to pull in. JSX pulled into a
.tsfile makes it.tsx. - Code folds from the gutter; a call's signature shows as you write its arguments.
- ⌘S formats the file as tcm does (oxfmt).
- A file kept as written is edited as what it is (Markdown, JSON, CSS). A Markdown file's first line says where it
goes back (
<!-- tcm: path a.b -->); for others the studio keeps it in.tcm/studio/kept.json. - Update codebase saves it, as
tcm update --cleardoes: it refuses, re-hashes dependents and reports as the command does, deletes the file once saved, and shows the history entry it made. - Discard throws it away, after asking.
A scratch file is a notebook. Once the file checks, each statement shows what it comes to (its watch, as
tcm check shows it):
- a value as Node prints it, or what it threw; what it writes to the console, above that;
- a React element rendered by the server (markup only, in a sandboxed frame with the page's stylesheets), then built for the browser (cached by hash) and run in its own frame, with no access to the studio, so its hooks and events work. An element whose code only runs on the server stays as the server rendered it, saying why;
- values are cached by hash. One that reaches outside (the clock, randomness, the network, files) says what it reaches and waits for ⌘↩ or Run, and runs each time you ask;
- a test shows its outcome: a cached pass, or waiting for ⌘↩ or Run. Run it (or Run it again) runs that one alone.
Select an expression for Evaluate (what it comes to and prints, without writing a statement; it sees the file's top-level names) or Extract to a definition (a new definition above, named as you say, its free locals as typed parameters, and the expression replaced by a call, awaited if it awaits).
Tests
The codebase's tests by namespace, each with its last outcome as tcm test keeps it (passed, failed, not run as it
is now), failures first with what they said, each to Edit or Run it. A package's suites are counted apart.
Run all, a namespace's Run them, or the tests a search finds run tcm test on the studio's branch, one run at
a time, output shown as it comes.
Libraries
Other codebases' code mounted read-only, as tcm lib lists it: each library's release, who
announced it, where it's fetched from, what it reaches outside itself, how much uses it, and what its author and the
people you trust say of it (yanks, advisories, reviews).
- Upgrade (on hover) first says what the newest release changes (
tcm lib upgrade --check), then upgrades. Code using it follows; what doesn't typecheck goes to Needs fixing. - Remove asks, saying what uses it.
- Add a library takes a source (
@alice.dev/geo, a remote, a URL, ortcm) and which, astcm lib add. The search box searches the registry, astcm lib search, each result with Add it.
Packages
The npm packages the codebase runs, as tcm packages lists them: each
range with its locked version and how many definitions use it. A package's page has its README, its API (each
export's signature and doc), what uses it, license, homepage and types, and Upgrade: the newest versions in its
ranges, as tcm upgrade <package> (no hash changes; everything using it is checked again; the lock goes back if
anything breaks).
Processes
What tcm runs on the codebase, whoever started it (the studio, a terminal, an editor, an agent), as
tcm ps lists it: each tcm dev, start, serve and studio, with its address, branch,
process and uptime, to open or stop (as Ctrl-C would; the studio doesn't stop itself). Agents' sessions are here
too, with their background jobs.
The codebase's apps that aren't running each have Start: tcm dev <app> on a free port, on the studio's branch,
opened once it listens. It keeps running after the studio stops. Its Output has a page of its own (kept in
.tcm/studio/logs/<app>.log).
Each app has a page of what it's made of, as tcm build plans it: the server functions its browser code calls, its
browser code, the tables its server code reaches (with rows), and its namespace's recurring jobs and tests (to run).
The page refreshes every few seconds.
Slots
The machine's slots, shared by every tcm on it (testing):
- each slot and what's in it (the suite, when a run says);
- the runs holding them: what each is (suites, tests, an app build, a verify), whose codebase and branch, its process, how many slots and since when;
- the queue, in order, with how many each wants and how long it's waited;
- slots held (this codebase's and others') and waited for, by minute over the last half hour while the studio ran;
- why the machine has that many: cores, performance cores and memory, or
TCM_SLOTS.
Traces
What the app did on this machine in the last hour, day or week, as tcm traces says it: what runs worse than its
version before and the change that did it; time spent by definition as named now, each row (a definition's, a
page's, a statement's) linking to its numbers, versions, callers, and slowest and latest traces; and errors, grouped
by the definition that threw them, with the version they began with.
A trace is a waterfall on one clock, from the browser to the SQL: each span under its parent, coloured by where it ran, linked to its definition, with errors under the span that threw them and the jobs it enqueued.
Production shows a deployed server's traces the same way. Set TCM_TRACES_TOKEN where tcm start runs
(traces), then give the studio the server's address and the token (kept in
.tcm/studio/production.json, readable only by you). It fetches a copy; Fetch again gets a newer one.
Jobs
The app's background jobs on this machine, as tcm jobs lists them: counts of queued, running, done,
failed and cancelled (each a filter), and the newest 200 with function, attempts, when it runs or ran, and error. A
job's page has its arguments, error and trace, with Retry (or Run again), or Cancel while it's queued, as
tcm jobs retry and cancel. Beside them, recurring jobs (every()) with their interval and last run. It refreshes
every few seconds.
Data
The app's data on this machine, as tcm data has it: each table declared or holding rows (a test's
included), with its columns, row count, rows stored as an older row type, last write, and declaring definition.
A table's page shows its rows 50 at a time:
- newest first or sorted by a column; searched anywhere in a row; filtered by fields equal to values (several at
once, as
findtakes them); - each cell as its type reads (dates to the minute in UTC, lists as lists, nothing as a dash), each column with its type and whether it's unique or indexed;
- downloadable as JSON or CSV, as shown.
Click a row to edit it, in a form (each field as its type takes it: text, number, checkbox, date and time, one of
its values, JSON for nested data) or as JSON. Save writes it as the table's own writes do: checked against its row
type (the reason, if it doesn't fit), recorded with its schema, and refused if it'd share a unique field with another
row (which is named). New row starts from what the type needs; Delete asks first. Rows of an older row type are
marked, with Migrate them (tcm data migrate <table>).
Releases
The codebase's packages, those with changes since their latest release first (with a finder when there are many), each with its latest release and whether one is waiting.
A package's page shows its next release as things stand:
- the version and the bump, and what changed since the last release;
- its notes as they'll read: the heading, a summary you write right there (kept as
release-<package>.md, astcm release --draftkeeps it, so a release from anywhere uses it), then what tcm writes from the codebase; - every semver finding behind the version, folded (what changed, the lint, what it means for users);
- what blocks it (work left in todo);
- Release, which asks, then releases as
tcm release <package>does.
Releasing is main's only: on another branch, or reading one, the button is gone and the page says why. Below, the
releases made, newest first, with their notes, and, on main, Announce (as whom, and where it's fetched from),
as tcm release <package>@<version> --announce does (the first time, sign-in
opens in the browser). Beside them, the package's namespace, exports and commands. Up to date means nothing under its
namespace changed since its last release.
History
The branch's entries and what each changed, as the web UI shows them, optionally narrowed to a namespace.
Branches
The codebase's own branch first, then the others, then agents' sessions (those unchanged for a day folded away), each with when it last changed, how many names differ from the codebase's own, and a contribution proposing it.
- New branch makes one from the codebase's own, as
tcm branch, and switches to it. - Merge it in (on
main, Land it) asks what it's for and whether to squash it into one entry (as sessions land), then merges astcm mergedoes, showing output as it runs. Landing runs the branch's tests first; one that fails doesn't land. - Work on it switches to it, as
tcm switch. - Delete asks, then deletes as
tcm branch --delete(notmain, nor a session an agent is working on).
While the branch menu reads another branch, its notice has Merge it in and Work on it too. Under tcm dev, the studio
serves its session's branch and stays on it: there's no Work on it, and a new branch is only read.
Needs fixing
What tcm todo lists (counted in the column), each with what fixes it:
- Conflicted names, each side labelled with where it came from: Edit writes them merged into a file (conflict
markers where they overlap, as
tcm editdoes); Keep ours, Keep theirs (or one by hash) settles it astcm resolvedoes. - Definitions that stopped typechecking, with why: Edit writes each with what replaced what it used.
- Code using a version nothing names (after a change it couldn't follow, or a Delete).
A merge that left conflicts or broke something waits apart, the branch unchanged until it's settled: the page says
so, shows what's left as merged, links the files written to settle it, and has Drop the merge (tcm merge --abort). Once nothing's left, the branch has the merge.
Changing the codebase
Editing, updating, renaming, deleting, settling what needs fixing, release notes, releasing, upgrading packages and
starting apps act on the branch the studio serves (the codebase's own; under tcm dev, the session's). While the
branch menu reads another branch, they're read-only too. Branch actions (making, merging, switching, deleting) act on
the codebase's own branch, whichever is read.
Not the web UI
The studio is built from the web UI's pages, but it's yours alone: only the token's holder uses it, on
your machine. That's why it can run the codebase's code and show its traces, which the web UI, open to anyone a
--public server lets read, never does.
To work on the studio itself: tcm dev tcmts.studio.App in tcm's codebase.
Not yet
- Debugging. The editor's Debug runs code under VS Code's debugger; the studio can't yet.
- Data edits skip migrations. A saved row is checked as the table checks a write, but a
migratestep doesn't run on it: it's written as the current row type, or refused. - Production traces are a copy: the one last fetched (a week at most), not a live view.