Conversation
🎩 PreviewA preview build has been created at: |
camielvs
marked this pull request as draft
September 21, 2026 17:36
This was referenced Sep 21, 2026
Draft
This was referenced Sep 21, 2026
Collaborator
Author
This was referenced Sep 21, 2026
This was referenced Sep 22, 2026
Projects -> new project -> Tangent is meant to be the way in, but the page it landed on was blank: no session, so the chat said there was none; no tab, so the workarea said nothing was open. Everything had to be started by hand before any work could begin. A project with no sessions now gets one started on arrival, with a pipeline opened beside it to work in — created and attached to the project first if it has none. Both halves ask only whether the project has nothing, so a project someone has worked in comes up as they left it. A session nobody typed into is detached again on unmount, so an untouched project arrives session-less a second time and is set up again; finding the pipeline it attached last time is what stops that adding another. There is no draft pipeline to open — loading a spec throws for anything not in storage — so the starter is a real file. Naming it after the project means asking the stored pipelines whether that name is free: addFile asks only the registry, while the driver it writes through keys off the stored-file list, which also holds pipelines that never got a registry row. Its project row is written for both readers of a browser-held pipeline. A row carrying only Tangent's shape still lists on the project page, but would be offered for deletion as though it held the only copy. This replaces the auto-start that fired only for a project carrying a starting prompt, and keeps that behaviour: the prompt still opens the session and is still cleared afterwards. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A session opens for someone to type in, so the composer should already hold the caret. There was no way to ask for that from here: the embedded chat exposed only a session id, an agent id and an opening prompt. tangent-shell adds an `autoFocus` prop that focuses the composer once it accepts input, and fires once per mounted session rather than taking focus back afterwards. Repointing the three pinned packages at that commit and passing it is the whole change. The sub-agent tabs do not ask for it. Opening one is navigation, not an invitation to type, and the caret may be somewhere it was put on purpose. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
camielvs
force-pushed
the
09-18-feat_connect_the_project_page_and_tangent
branch
from
September 23, 2026 20:57
68939e0 to
5f534f6
Compare
camielvs
force-pushed
the
09-21-feat_open_a_new_project_ready_to_work_in
branch
from
September 23, 2026 20:57
7149314 to
40b5200
Compare
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.

Projects → new project → Tangentis meant to be the way into the app, but the page it landed on was blank: no session, so the chat said there wasn't one; no tab, so the workarea said nothing was open; and nowhere for the caret to be. Everything had to be started by hand before any work could begin.The rule
The first two halves ask only whether the project has nothing in it, so a project someone has already worked in comes up exactly as they left it.
It also survives a second look. A session nobody typed into is detached again on unmount by
discardEmptySession, so an untouched new project arrives session-less next time and is set up again — and finding the pipeline it attached last time is what stops that adding another.This replaces
useStartSessionWithPrompt, which did the same thing for the one case of a project carrying a starting prompt. That behaviour is unchanged: the prompt still opens the session, still names itDebug session, and is still cleared afterwards.There is no such thing as a draft pipeline
The question was whether local storage could carry an empty one. It has to be a real file:
useLoadSpec→resolveSpecDatathrowsPipeline "x" not foundfor anything not in storage, so a tab pointed at a draft renders an error, not an empty canvas. The starter is therefore created through the samecreateNewPipelinethe editor's File → New uses, writing the same default YAML.Two things that needed care:
Naming it after the project means asking the right question.
addFilechecks the registry for uniqueness, butFolderIndexDbStorageDriverinheritswritefrom the root driver and both write the sameuser_pipelineslist — which also holds pipelines that never got a registry row. A name free byaddFile's test can still be occupied, and writing it would overwrite that pipeline.availablePipelineNameasks the stored-name list instead and suffixes2,3, … It also caps the length, since the name is recorded in the project'sextra_data.Its project row is written for both readers. Tangent's "Add a pipeline" records
{type, identity}; the project page records{kind, storage, localName, localId}and reads it withclaimsLocalPipeline. A row carrying only Tangent's shape still lists on the project page, but its removal dialog would say "This is the only copy, so deleting it here deletes it for good" — untrue of a pipeline that lives in the browser. SostarterPipelineResourceInputcomposes both existing builders rather than picking one.browserPipelineTargetreads either shape back, so a pipeline added from the project page is found rather than duplicated.The caret needed the embed to offer it
Focus was impossible from this side: the embedded chat took a session id, an agent id and an opening prompt, and
<tangent-chat>'s internals come from the bundle the server serves.tangent-shellnow has anautoFocusprop that focuses the composer once it accepts input and fires once per mounted session rather than taking focus back afterwards — so the three pinned packages move tof8435e6and the Prime chat passes it.The sub-agent tabs deliberately do not. Opening one is navigation, not an invitation to type, and the caret may be somewhere it was put on purpose.
Testing
pnpm run validateclean; 3045 tests pass, 27 new.Covered against a mocked store: an empty project starts one session, creates one pipeline, attaches and opens it; a project with a session is left alone; a project with a pipeline already attached opens that one and creates nothing; a pointer-shaped row is recognised; the oldest of several is chosen; a failed session start does not go on to a pipeline; and the pipeline opens only after
startSessionresolves, because the workarea is keyed by session and a tab opened before there is one is silently dropped. Plus: the session composer asks for focus and the sub-agent tabs do not.What could not be checked in a browser: all of it runs inside the Tangent page, which is unreachable in my environment and shows the box from #2760 — the same limitation as #2762 — and nothing on the project page changed, so there was no reachable surface to exercise.
One honest gap in the tests: the StrictMode replay that
usePrepareEmptyProject's ref guard exists for cannot be reproduced inrenderHook. I wrapped a test inStrictModeto try, measured that effects still ran once, and took the wrapper back out rather than leave coverage that looks real and isn't.Worth a look on a configured Tangent: that the starter pipeline opens rather than erroring, that revisiting an untouched new project reuses it instead of adding a second, and that the composer actually holds the caret on arrival.
🤖 Generated with Claude Code