Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
40 commits
Select commit Hold shift + click to select a range
6688190
tracing: capture client activity and hangs for Perfetto
sandersonstabo Oct 2, 2026
cce1587
sidebar: show cached project icons below thread titles
sandersonstabo Oct 2, 2026
7eedc9d
style: reflow two backend calls on the pinned toolchain
sandersonstabo Oct 3, 2026
a1ed801
models: grey out engines whose CLI is signed out
sandersonstabo Oct 3, 2026
685fd19
approvals: settle answered approval cards from their receipts
sandersonstabo Oct 3, 2026
5634666
titlebar: show the thread's context window as a meter
sandersonstabo Oct 3, 2026
e5cf606
ui: shift-free streaming reveal policy for markdown
sandersonstabo Oct 3, 2026
3a164b9
conversation: keep counting "Working for" while the reply streams
sandersonstabo Oct 3, 2026
dc19f2b
conversation: stream prose in segments and animate live work
sandersonstabo Oct 3, 2026
ee81080
sidebar: show unread threads and fold the rest into Working and History
sandersonstabo Oct 3, 2026
b21660c
inspector: close the context section with a grid rule
sandersonstabo Oct 3, 2026
06be98b
deps: pin gpui with paint-time element transforms
sandersonstabo Oct 3, 2026
aaee7c0
profile: size the menu to its selector and pop it in like a dropdown
sandersonstabo Oct 3, 2026
f725d31
ui: use Founders Grotesk Bold for editor header
sandersonstabo Oct 3, 2026
429f43a
conversation: keep counting labels at a steady width
sandersonstabo Oct 3, 2026
2328682
conversation: finish work prose once anything follows it
sandersonstabo Oct 3, 2026
f7a3412
conversation: sweep segments in and stream prose that lands whole
sandersonstabo Oct 3, 2026
56c10fc
conversation: open entering work rows instead of blurring them in
sandersonstabo Oct 3, 2026
b4a0917
conversation: reveal prose at a steady capped pace
sandersonstabo Oct 3, 2026
01b0913
ui: stack uppercase build badge beneath editor wordmark
sandersonstabo Oct 3, 2026
e17d444
forge: persist explicit user model preferences with source
sandersonstabo Oct 3, 2026
690a7bf
ui: hide last-day sidebar heading while retaining its group
sandersonstabo Oct 3, 2026
dde17d4
forge: answer catalogs from cached discovery while it refreshes
sandersonstabo Oct 3, 2026
02805c1
database: keep dispatch leases renewing across clock steps
sandersonstabo Oct 3, 2026
e26c297
transport: report unreadable credentials as Forge offline
sandersonstabo Oct 3, 2026
c24b57f
tracing: keep incident evidence through storms and WSL restarts
sandersonstabo Oct 3, 2026
6289d26
nix: skip the unused check pass and binary copy in stage builds
sandersonstabo Oct 3, 2026
bf18811
conversation: rest numbers in the columns they roll in
sandersonstabo Oct 3, 2026
dd15bf7
sidebar: clear hover pills whose row did not render
sandersonstabo Oct 3, 2026
c305573
sidebar: keep the open thread's unread outcome until the reader leaves
sandersonstabo Oct 3, 2026
9278979
conversation: fold the work only once the reply is clearly the answer
sandersonstabo Oct 3, 2026
cf397f1
conversation: glide the reveal's fade front through the text
sandersonstabo Oct 3, 2026
709f020
conversation: show what arrived while nobody watched at rest
sandersonstabo Oct 3, 2026
379bc33
conversation: show thinking traces whole and swap them in
sandersonstabo Oct 3, 2026
3ff2db6
conversation: never wait on a provider that has already answered
sandersonstabo Oct 3, 2026
1266e83
inspector: close every section with a grid rule
sandersonstabo Oct 3, 2026
837f72f
usage: fill a context window the engine did not report from the catalog
sandersonstabo Oct 3, 2026
5de5979
composer: track context usage for every engine and keep it when idle
sandersonstabo Oct 3, 2026
b3a9568
claude: report the context window the turn ran in
sandersonstabo Oct 3, 2026
3fdf308
inspector: clear the Agents section once the run settles
sandersonstabo Oct 3, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 27 additions & 17 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ members = [
"modules/native_engine",
"modules/protocol",
"modules/transport",
"modules/tracing",
"modules/ui",
"scripts/capnp_codegen",
"scripts/native_dev",
Expand All @@ -36,8 +37,8 @@ capnpc = "=0.27.0"
command-group = { version = "=5.0.1", default-features = false, features = ["with-tokio"] }
fs2 = "=0.4.3"
getrandom = { version = "=0.4.3", default-features = false }
gpui = { package = "gpui-ce", git = "https://github.com/artisanstreet/gpui-ce", rev = "ee668021b2be6b3af6ff1c37ae19aa2005c3c4b6" }
gpui_platform = { package = "gpui_ce_platform", git = "https://github.com/artisanstreet/gpui-ce", rev = "ee668021b2be6b3af6ff1c37ae19aa2005c3c4b6", features = ["wgpu"] }
gpui = { package = "gpui-ce", git = "https://github.com/artisanstreet/gpui-ce", rev = "8d8c07348532be87463aa666a01c1dbcb28651df" }
gpui_platform = { package = "gpui_ce_platform", git = "https://github.com/artisanstreet/gpui-ce", rev = "8d8c07348532be87463aa666a01c1dbcb28651df", features = ["wgpu"] }
http = { version = "=1.5.0", default-features = false, features = ["std"] }
http-body-util = { version = "=0.1.5", default-features = false }
hyper = { version = "=1.11.0", default-features = false, features = ["client", "http1"] }
Expand Down
14 changes: 9 additions & 5 deletions docs/reference-scene-interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -128,11 +128,15 @@ life, and work and non-promoted prose after the steer stay session details.
session: `superseded = true` iff some other block follows the first
segment. A superseded session never narrates (renderer hides its live
line); the turn-level status row at turn end narrates current work.
- Disclosure. A section cannot be collapsed before its turn settles: while
the turn lifecycle is live the panel is forced open and the header has no
chevron or toggle; the disclosure machine refuses a close for active work
and reopens on re-activation. Once settled, one toggle hides the rows of
every segment while the steer bubbles stay visible.
- Disclosure. A section cannot be collapsed by hand before its turn
settles: while the turn lifecycle is live the panel is forced open and the
header has no chevron or toggle; the disclosure machine refuses a close
for active work and reopens on re-activation. The one exception is the
reply: the moment the promoted reply's first whole word or block is
visibly revealed, the renderer folds the live section (still without a
toggle), and it reopens if that prose is demoted back into the work.
Once settled, one toggle hides the rows of every segment while the steer
bubbles stay visible.
- Backend ordering. The Forge opens an assistant item ahead of its first
text; a steer projected while that item is still empty retires it, so the
acknowledging prose opens a new item after the steer and the seam rule
Expand Down
114 changes: 114 additions & 0 deletions docs/runbooks/flight-recorder.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
# Editor and Forge flight recorder

Debug payloads from `nix run .#dev` include the local flight recorder and start
it automatically. Use the app normally. A stalled UI, stalled operation, busy
admission, failed request, or failed navigation saves a Chrome Trace Event JSON
file that opens directly in [Perfetto](https://ui.perfetto.dev/).

Press **Ctrl+Shift+F11** to save recent Editor activity immediately. Automatic
incidents retain two seconds after the trigger; normal exit saves another
snapshot. This works for detached Windows launches without stdout capture.

Files go to `%TEMP%\artisan-traces` on Windows and `$TMPDIR/artisan-traces`
on Unix, normally `/tmp/artisan-traces`. `ARTISAN_TRACE_DIR` selects another
directory. Editor and Forge write on their respective machines, so a WSL
Comment on lines +12 to +14

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: The writer uses std::env::temp_dir(), which prefers TMP over TEMP on Windows, so %TEMP% is not always the capture directory. Describe the process temp directory instead.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/runbooks/flight-recorder.md, line 12:

<comment>The writer uses `std::env::temp_dir()`, which prefers `TMP` over `TEMP` on Windows, so `%TEMP%` is not always the capture directory. Describe the process temp directory instead.</comment>

<file context>
@@ -0,0 +1,114 @@
+incidents retain two seconds after the trigger; normal exit saves another
+snapshot. This works for detached Windows launches without stdout capture.
+
+Files go to `%TEMP%\artisan-traces` on Windows and `$TMPDIR/artisan-traces`
+on Unix, normally `/tmp/artisan-traces`. `ARTISAN_TRACE_DIR` selects another
+directory. Editor and Forge write on their respective machines, so a WSL
</file context>
Suggested change
Files go to `%TEMP%\artisan-traces` on Windows and `$TMPDIR/artisan-traces`
on Unix, normally `/tmp/artisan-traces`. `ARTISAN_TRACE_DIR` selects another
directory. Editor and Forge write on their respective machines, so a WSL
Files go to Rust's process temp directory with `artisan-traces` appended.
`ARTISAN_TRACE_DIR` selects another directory. Editor and Forge write on their
respective machines, so a WSL Forge's files are on Linux.

Forge's files are on Linux. The newest sixteen JSON files per application are
retained across process restarts. `ARTISAN_TRACE=0` disables recording at launch.
Set these variables in the environment of the actual Editor/Forge process;
an already running Forge service does not inherit a new terminal environment.

## What to inspect

The history covers up to two minutes or 32,768 events, whichever fits first.
High frame rates shorten the retained time window. Traces contain:

- UI and transport heartbeats, with stalled/resumed breadcrumbs;
- GPUI CPU draws and renderer submission durations, dirty-to-draw latency,
animation intervals, and foreground task polls over ten milliseconds;
- UI handlers, route mounting, snapshots, patch processing, and event batches;
- command queue depth, admission/rejection, queue waits, execution spans,
producer-to-consumer flow arrows, and thread identities;
- correlated wire requests, response-family expectations, outcomes, and
reconnect/subscription work;
- recent-thread waits, thread-switch generations/phases, project intake,
navigation gates, pending unsubscribe, and connection holds;
- matching Forge request spans and event-loop stall reports.

Async spans have unique IDs and use `b`/`e`, so concurrent work and executor
migration do not produce falsely nested synchronous slices. Unfinished spans
are closed at the capture boundary with `unfinished=true`. A beginning that
aged out is marked `begin_truncated=true`. `dropped_records` and
`evicted_records` make recording loss explicit. Thread-switch state includes
its owning span ID; request spans include the command span that issued them.

For the delayed busy notification, search for `recent_thread.failed`, then
inspect `navigation.gates` immediately before it. The ten-second recent-thread
wait ends with that failure if navigation is still inadmissible. Follow the
switch phase, outstanding command, and request rather than inferring that the
clicked thread itself was busy.

Use `python3 scripts/trace.py summary TRACE.json` for a text report of the
longest operations and the last navigation gates. To combine local Editor and
Forge exports into one Perfetto timeline:

```sh
python3 scripts/trace.py merge combined.json editor-TRACE.json forge-TRACE.json
```

Matching `request_id` values connect processes. Timestamps use Unix
microseconds anchored to each process's monotonic clock. Different machines
need synchronized wall clocks for visual alignment; IDs remain useful when
their clocks differ. Merge remaps process IDs to avoid Windows/Linux PID
collisions and distinguishes process restarts. Overlapping captures are
deduplicated; real span completions supersede synthetic snapshot boundaries.

## Build-time removal

The shared `artisan-tracing` module has no default features. Without `enabled`,
it contains only macros that erase their arguments. It has no recorder,
serialization dependency, environment handling, filesystem writer, or watchdog.
Each application also gates all tracing fields, startup tasks, and adapters
behind its `flight-recorder` Cargo feature. Production's Nix feature list is
empty, so the tracing implementation is absent from the binaries.

For a Cargo diagnostic build, enable `artisan-frontend/flight-recorder` and
`artisan-backend/flight-recorder`. Build without either for the normal
production behavior. Cargo features are additive; deliberately enabling these
features on a custom release build deliberately includes the recorder.

## Cost and limits

UI and transport producers use bounded, nonblocking sends. History collection
and filesystem output run on separate threads. Saturation drops records rather
than waiting. Arguments accept only bounded scalar values. Instrumentation
records fixed operation names, opaque IDs, enum states, counters, and source
locations; it does not record prompts, thread titles, file targets, credentials,
URLs, snapshots, screenshots, or provider payloads. Files are local, and Unix
exports are created with owner-only permissions.

The UI watchdog triggers after 500 ms without a foreground heartbeat. A long
navigation/project/request span triggers after five seconds. Automatic
captures have a ten-second cooldown. The existing Forge watchdog triggers at
two seconds. Whole-process suspension is reported separately by the watchdogs.
Recording is instrumentation, not native stack sampling: an uninstrumented
blocking function may still need an OS profiler. Completed GPUI task records
can attribute a slow poll to its source location after the UI resumes.

File writing never blocks the UI while the application runs. Normal process
exit joins the recorder and writer to finish the final file; force-killing the
process can lose the latest in-memory history. Export failures go to stderr.
The recorder does not require a GUI viewer to be open and makes no network
requests.

## Verification

```sh
cargo test -p artisan-tracing --features enabled
cargo test -p artisan-tracing --no-default-features
python3 tests/tracing/merge.py
```

Set `ARTISAN_TRACE_KEEP_FIXTURE=1` on the enabled test command and add
`-- --nocapture` to retain its generated stall, manual, and shutdown captures
for an importer check. Handoff arrows use one-microsecond endpoint markers;
their duration represents a marker rather than measured application work.
36 changes: 36 additions & 0 deletions docs/runbooks/project-icons.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# Recent-thread project icons

The icon beside a thread's project subtitle is 14 pixels wide. Disk-only
projects and Git repositories without a network remote show a folder.

For a remote repository, the first available icon wins:

1. A project file, checked in this order: `.artisan/icon.png`,
`.artisan/icon.webp`, `icon.png`, `favicon.png`, `public/favicon.png`.
2. Repository artwork supplied by the Git host.
3. Organization, namespace, or user artwork supplied by the Git host.
4. The hosting service's bundled logo, or the Git logo for unknown hosts.

GitHub resolves the public organization/user avatar. GitLab, Bitbucket,
Codeberg, and Gitea resolve repository artwork before owner artwork. GitHub
has no dedicated repository avatar endpoint, so a local project file provides
its project-specific artwork. Private or inaccessible metadata requires no
login and falls back to the logo. No credentials are sent by this resolver.
Comment on lines +17 to +18

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3: This says private repositories fall back to the logo, but GitHub can return the owner's public avatar even when the repository cannot be read. Clarify that GitHub may still show that avatar.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. When an issue isn't valid or won't be fixed in this PR, reply in its thread with the reason and then resolve the thread. At docs/runbooks/project-icons.md, line 17:

<comment>This says private repositories fall back to the logo, but GitHub can return the owner's public avatar even when the repository cannot be read. Clarify that GitHub may still show that avatar.</comment>

<file context>
@@ -0,0 +1,36 @@
+GitHub resolves the public organization/user avatar. GitLab, Bitbucket,
+Codeberg, and Gitea resolve repository artwork before owner artwork. GitHub
+has no dedicated repository avatar endpoint, so a local project file provides
+its project-specific artwork. Private or inaccessible metadata requires no
+login and falls back to the logo. No credentials are sent by this resolver.
+
</file context>
Suggested change
its project-specific artwork. Private or inaccessible metadata requires no
login and falls back to the logo. No credentials are sent by this resolver.
its project-specific artwork. GitHub may still resolve a public account avatar when a repository cannot be read; inaccessible metadata falls back to the logo. No credentials are
sent by this resolver.


Forge discovers and downloads icons in the background. Subtitles and icons
come from cached repository observations; listing threads and painting the
sidebar perform no Git, file, or network I/O. Successful artwork is refreshed
after an hour, absent artwork after five minutes. Changes are pushed through
the existing recent-thread subscription. A changed remote invalidates the
previous icon. Detached projects leave the cache.

Downloaded input is capped at 512 KiB and decoding runs off the Forge event
loop with image allocation and dimension limits. Artwork is normalized to a
PNG within 48 by 48 pixels and 16 KiB before crossing the protocol. Editor
reuses the decoded image identity between repaints. The added protocol fields
are optional, so an older Forge supplies folder fallback and an older Editor
ignores the artwork.

Metadata field references: [GitHub account avatars](https://docs.github.com/en/rest/users/users#get-a-user),
[GitLab project and namespace avatars](https://docs.gitlab.com/api/projects/),
and [Gitea repository metadata](https://docs.gitea.com/api/operations/repo-get/).
Loading
Loading