Add a README with a high-level roadmap - #7
Conversation
|
|
||
| The "minimal" milestones were about getting Zed to a point where the Zed team could use Zed productively to build Zed. What features are required for someone outside the company to use Zed to productively work on another project that is also written in Rust? | ||
|
|
||
| This includes infrastructure like auto-updates, error reporting, and metrics collection. It also includes some amount of polish to make the tool more discoverable for someone that didn't write it, such as a UI for updating settings and key bindings. |
There was a problem hiding this comment.
This includes infrastructure like auto-updates, error reporting, and metrics collection
It might be good to mention some other basic server-side concerns like signup, a permissions model, etc. Would those things also fall under this "Private alpha for Rust teams" phase? I think that for the internal collaborative phase, we might be able to punt on some of them.
There was a problem hiding this comment.
Good point, @maxbrunsfeld.
One related thought is: is there any non-code work that we would need by this phase, e.g. a logo, branding for the website/app, etc.?
Also, in terms of how the app looks, I'd be more than happy for internal use to have the editor look like Atom. That said, people are very excited about trying something that looks different (see yesterday's conversation about Dark Mode driving signups on StackOverflow) and sticking to it because of how it looks (in addition to the great performance we will offer). We should probably only provide one theme, but do we need to diverge from Atom's palette and, if so, when is the right time to do that? Alpha or private beta?
as-cii
left a comment
There was a problem hiding this comment.
Absolutely love this, nice work capturing it all so clearly! 💯
|
|
||
| The "minimal" milestones were about getting Zed to a point where the Zed team could use Zed productively to build Zed. What features are required for someone outside the company to use Zed to productively work on another project that is also written in Rust? | ||
|
|
||
| This includes infrastructure like auto-updates, error reporting, and metrics collection. It also includes some amount of polish to make the tool more discoverable for someone that didn't write it, such as a UI for updating settings and key bindings. |
There was a problem hiding this comment.
Good point, @maxbrunsfeld.
One related thought is: is there any non-code work that we would need by this phase, e.g. a logo, branding for the website/app, etc.?
Also, in terms of how the app looks, I'd be more than happy for internal use to have the editor look like Atom. That said, people are very excited about trying something that looks different (see yesterday's conversation about Dark Mode driving signups on StackOverflow) and sticking to it because of how it looks (in addition to the great performance we will offer). We should probably only provide one theme, but do we need to diverge from Atom's palette and, if so, when is the right time to do that? Alpha or private beta?
This pull request enables users to set breakpoints by clicking to the left of a line number within editor. It also anchor's breakpoints to the original line they were placed on, which allows breakpoints to stay in their relative position when a line before a breakpoint is removed/added.
- Fix #4: ACP write_text_file now checks tool permissions for edit_file tool before writing. Deny patterns and default mode are respected. - Fix #5: ACP request_permission now checks always_deny/always_allow/always_confirm patterns using the tool call title as best-effort input for matching, instead of only checking the default mode. - Fix #7: Settings UI find_matched_patterns now tracks whether shell parsing succeeded and marks allow patterns as overridden when parsing fails, matching the real engine's behavior.
Replace all occurrences of 'default_mode' with the canonical 'default' field name. The old name only exists as a serde alias for backward compat.
- Remove retain block that wiped branch names for closed workspaces, contradicting the stated purpose of persisting them across worktree deletion (#1) - Replace single group_fallback_branch with per-path fallback map so multi-repo threads don't get the wrong branch applied to unmatched paths (#2) - Gate backfill behind needs_branch_backfill flag so it only runs in response to git HeadChanged/GitWorktreeListChanged events, not on every sidebar rebuild (#3) - Use .context("deserialize branch_names")? instead of .ok() to propagate deserialization errors instead of silently swallowing them (#4) - Clear branch name entries when repos enter detached HEAD state instead of leaving stale values (#5) - Use to_string_lossy() instead of display() for PathBuf serialization (#6) - Group all Column::column reads together at the top of the Column impl to prevent future column-order bugs (#7)
…d-do-not-offer-to Add suggest_dev_container setting to disable dev container suggestions
…ession" The navigator was scaffolded earlier but never finished — "the navigator's set_active_solution is intentionally left UNWIRED in v1" was the comment and the rows had no on_click. Result: opening the dock showed an empty "Sessions" panel with no way out. This wires it end-to-end: - Navigator stores WeakEntity<Project> (not just WeakEntity<Workspace>) so derive_active_solution can read worktrees without taking a Workspace borrow — calling workspace.read(cx) from the project-event subscription callback double-leases and panics with "cannot read Workspace while it is already being updated". - Initial active_solution derivation runs via cx.defer so it fires *after* the surrounding observe_new<Workspace> closes, for the same reason. - SolutionStore::Changed and project::Event both retrigger refresh_active_solution, so add_member / open / close all retarget the panel without the user having to close-and-reopen the workspace. - FocusNavigator action now has a workspace.register_action handler — the Panel::toggle_action box was returned but no one was listening, so the sidebar icon click did nothing. - Session rows are ButtonLikes that open SolutionSessionView as a pane Item (per FORK.md decision zed-industries#7). - Footer renders one "+ New <Name> Session" button per registered SolutionAgentAdapter, sourcing the label and icon from the adapter itself. The previous draft hard-coded "New Claude Session" and IconName::AiClaude in three places; if a second adapter ever lands the navigator just shows two buttons with no extra wiring. The dock sidebar icon is also IconName::Sparkle now (was AiClaude) for the same reason. - script/run-mcp --skip-onboarding stopped touching the migrations table. Without a UNIQUE constraint INSERT OR IGNORE was a no-op-by-name and stacked duplicate rows on every run, so sqlez eventually saw three step-0 migrations for KeyValueStore, refused to apply the real step 1, and fell back to an in-memory DB (which dropped first_open and put the editor right back into Onboarding). Letting sqlez own the migrations table is the simpler and correct thing.
Three problems with the previous take, all visible in one screenshot:
1. Header showed JSON-ish goo: 'claude-acp · Running { started_at:
Instant { tv_sec: 148873, tv_nsec: ... }, notified: false }'. That was
format!('{:?}', state) leaking the Debug repr of SessionState::Running.
2. Sending 'привет' produced an Assistant reply but no visible user
message — the claude-acp wrapper does not echo user prompts back as
UserMessageChunk updates, and we relied on the agent's stream alone
to populate thread.entries().
3. The right Sessions navigator listed the same session uuid that was
already a tab in the main editor, and the chat ended up in the editor
area competing with code. Cursor / Cody / Copilot Chat all keep chat
in a dedicated docked panel — users expected the same here.
Fixes:
- SessionState::short_label() + Display impl. Header now reads
'claude-acp · Running' / Idle / Awaiting input / Error.
- store::send_message_blocks pushes the user message into the AcpThread
optimistically (with the same UserMessageId we ship to the agent, so a
future echoing agent coalesces into the same entry instead of dupes).
- SolutionSessionsNavigator becomes the chat surface: own tab strip,
status row, body = active SolutionSessionView, '+ New <Adapter>
Session' buttons in footer. Each tab shows the session title with an
'×' close button. Tabs are panel-local (not persisted yet); switching
active solution wipes the tab list (sessions themselves stay alive in
the store and reappear if reopened).
- SolutionSessionView no longer implements workspace::Item and no
longer renders its own header — just conversation + compose box,
ready to be hosted as a child by the panel.
- Default panel width 280→420 (chat needs reading room).
FORK.md decision zed-industries#7 reversed accordingly: 'sessions live inside the
right-dock chat panel, not as workspace pane Items'.
…p + refresh call-site tracers Three new cfg-gated log emits under texture-cache-debug feature, all in crates/gpui/src/window.rs: - entity_taffy_input (Tracer A, primary): per-entity style+children hash on every Window::request_layout call; tracks frame-to-frame stability to inform zed-industries#1 Path B viability + zed-industries#7 Taffy memoization hit rate. Hashes via seahash (already a workspace dep) over Debug repr (Style has no Hash derive). Per-EntityId keying — last-wins intra-frame semantics; cross-frame aggregation gives the useful signal. - ancestor_walk_step (Tracer B): per-iteration log inside mark_view_dirty's view_path_reversed walk; captures leaf id + step + walking ancestor + already_dirty bool. Tells us how many ancestors are spurious vs necessary. Inputs zed-industries#9 walk-skip viability decision. - window_refresh_called (Tracer C): logs every Window::refresh() call site via #[track_caller] + Location::caller(). #[track_caller] is unconditional (matches existing pattern in same file); emit cfg-gated. Counts which of the 12 div.rs refresh sites duo identified actually fire under real workloads. Inputs zed-industries#6 P5.2.8 extend viability. Hot-path overhead estimated 3-5% FPS measurement skew during smoke; acceptable trade-off for the empirical attribution. Zero release-build cost (cfg-gated). Refs: S538 synthesis §"Bundled Logging Proposal"; bolt S538 Hop 2 Step 1 implementation brief.
Follow-ups from code review on #skills-announcement. Items #5 (the doc-comment removal was intentional), #7 (illustration skill names), and #9 (the TODO on the version match) are intentionally left as-is per discussion. ### Changes **`Try Now` no longer un-focuses an already-focused agent panel.** `ToggleFocus` dispatches `workspace.toggle_panel_focus` which un-focuses when already focused. Swapped for `FocusAgent`, which calls `focus_panel` unconditionally. **Migration bullet is omitted for users who never had Rules.** The deleted `RulesToSkillsModal` had two flavors (generic intro vs migration summary) gated on `MigrationResult::is_empty()`. The toast now reads `migration_result()` and only includes the "Default Rules are converted into your global AGENTS.md\u2026" bullet when the migration actually moved something. New users (and existing users without Rules) see a cleaner two-bullet message that doesn't reference rules they don't have. **Telemetry events prefixed with `Skills`.** Previously `Announcement Main Click` etc., which would collide with the past parallel-agent announcement data and any future announcement reusing this code path. Now `Skills Announcement Main Click` / `Skills Announcement Secondary Click` / `Skills Announcement Dismiss`. **Dropped `Option<SharedString>` around `secondary_action_label`.** The underlying `AnnouncementToast` field is a plain `SharedString` with a default of `"Learn More"`, so `None` didn't actually hide the button \u2014 it just kept the default label. Made the field a plain `SharedString` to match. **Renamed dismiss key** from `skills_migration_announcement_banner_dismissed_at` to `skills_announcement_dismissed`. "banner" was leftover from when this was a title-bar banner; `_at` suggested a timestamp value but it's a bool. Safe to rename because this PR hasn't shipped yet. **Refreshed stale doc comments** in `prompt_store/src/rules_to_skills_migration.rs` that still referenced the deleted modal and title-bar banner. ### Files - `crates/auto_update_ui/Cargo.toml` \u2014 add `prompt_store` dep - `crates/auto_update_ui/src/auto_update_ui.rs` \u2014 all the toast-side changes above - `crates/prompt_store/src/rules_to_skills_migration.rs` \u2014 doc-comment refresh only, no behavior change - `Cargo.lock` \u2014 `prompt_store` added to `auto_update_ui`'s deps `cargo check -p auto_update_ui` is clean. Release Notes: - N/A
…ave arm, multi-conv momentum cap, wrong-conv wave spawns zed-industries#2 unquote slices only after confirming ASCII quote bytes -- a multi-byte first/last char (the em-dashes the writer fix now emits) panicked the indexer; surrogate pairs decode (zed-industries#6). zed-industries#1 orchestrator_target grows the missing "wave" arm (the native dispatch path was dead code; the bridge WAVE_RE fallback masked it). zed-industries#3 momentum_only_since moves onto ConvSim -- the Sim-level timer was reset every frame by any cold sibling conv, disabling the limit-cycle cap on multi-conversation boards. zed-industries#4+zed-industries#7+zed-industries#8 /wave + Run-wave: strict arg parse (bare vendor word refuses instead of spawning everything), in-flight guards, plan_conv (never the most-recent-conv fallback -- real spend on the wrong plan), refusals render verbatim (composer note + under-score line). zed-industries#9 project spokes prefer type:project docs over the generic first-wins resolve. 788 green single-threaded. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Audited every CHANGELOG claim against the code: all phase and bug entries are genuinely implemented (symbol checks for each), and every commit hash inside this shallow clone's history resolves to the work it claims. Nothing needed pulling out for review. Fixes found along the way: - Bugs zed-industries#47 and zed-industries#54 were archived as user-confirmed by c199237 but that commit never deleted their bugs.md entries, so both were double-tracked. Removed. - Bug zed-industries#63 was still "open — needs a decision" although phase 59 implemented the decided behaviour. Moved to fix attempted - untested with the fix recorded. - Bugs zed-industries#7, zed-industries#15 and zed-industries#20 sat at fix attempted - untested with no pointer in the testing ledger, so they were invisible there. Added with test recipes. - Bug zed-industries#56's changelog hash was a transposition (c199273 → c199237). Phases 59 and 60 are implementation-complete with only manual tests left, so their test items move to awaiting_testing.md and the phase files are recorded in the changelog and deleted. Six phases remain in rotation. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01HUB73REGigaN5TCgvSkM2s
Add a README with a high-level roadmap
- Pdf gains a thumbnail_cache fully separate from page_cache (with its own rendering_thumbnails/failed_thumbnails bookkeeping), since the main view and the thumbnail sidebar want the same page at very different scales at the same time - sharing one cache would mean one view constantly evicting the other's render. Pdf::request_thumbnail mirrors request_page's dedup logic but at one fixed THUMBNAIL_SCALE (no zoom-dependent re-render, no strip-rendering path - thumbnails never get big enough to need it). - The sidebar itself uses gpui's uniform_list for virtualization (only visible rows render) rather than reimplementing PdfContentElement's continuous-scroll viewport math - row heights genuinely are uniform here, unlike the main page view where they aren't. New Image toolbar button and ToggleThumbnails action, following the same embedded-in-PdfView pattern Phase 2's outline sidebar already established (two independent toggles, not merged into one tabbed sidebar the way Chrome's actually is - noted as a scope simplification in FEATURE_ROADMAP.md rather than reworking Phase 2's shipped UI). - Row click calls jump_to_page, the same navigation next/previous-page already use. Not implemented: thumbnail_cache eviction (mirrors the exact gap page_cache had before High zed-industries#7's fix - unbounded growth over a long thumbnail-sidebar scroll on a very large document). Added a #[gpui::test] confirming a thumbnail renders into its own cache without touching page_cache, and that a repeat request short-circuits. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Pdf gains a thumbnail_cache fully separate from page_cache (with its own rendering_thumbnails/failed_thumbnails bookkeeping), since the main view and the thumbnail sidebar want the same page at very different scales at the same time - sharing one cache would mean one view constantly evicting the other's render. Pdf::request_thumbnail mirrors request_page's dedup logic but at one fixed THUMBNAIL_SCALE (no zoom-dependent re-render, no strip-rendering path - thumbnails never get big enough to need it). - The sidebar itself uses gpui's uniform_list for virtualization (only visible rows render) rather than reimplementing PdfContentElement's continuous-scroll viewport math - row heights genuinely are uniform here, unlike the main page view where they aren't. New Image toolbar button and ToggleThumbnails action, following the same embedded-in-PdfView pattern Phase 2's outline sidebar already established (two independent toggles, not merged into one tabbed sidebar the way Chrome's actually is - noted as a scope simplification in FEATURE_ROADMAP.md rather than reworking Phase 2's shipped UI). - Row click calls jump_to_page, the same navigation next/previous-page already use. Not implemented: thumbnail_cache eviction (mirrors the exact gap page_cache had before High zed-industries#7's fix - unbounded growth over a long thumbnail-sidebar scroll on a very large document). Added a #[gpui::test] confirming a thumbnail renders into its own cache without touching page_cache, and that a repeat request short-circuits.
Carried items: fix .map interceptor registration-order claim, add a clippy.toml disallowed-methods pin on WizardState::targets (use dest_targets() instead), compose_map_rgba unit test landed upstream via ggo PR zed-industries#82. Nine-crate sweep green (390 tests), clippy -D warnings clean, fmt clean, licenses clean, cargo check -p zed clean. MIGRATION.md counts re-tallied for the map editor and PNG import wizard rows. Merge drill zed-industries#7: 6 upstream commits, 0 conflicts.
Test results from 2026-08-11. Bugs zed-industries#7, zed-industries#66, zed-industries#67, zed-industries#68, zed-industries#70 and zed-industries#72 confirmed fixed and removed; zed-industries#64 failed and stays open with the finding recorded; zed-industries#73's repro is confirmed on Windows. Three fixes here. zed-industries#69 (column titles wrapping their last character) was NOT fixed by the previous attempt, and that attempt was aimed at the wrong layer. Measuring headers semibold was a real discrepancy and stays, but the cause is that the table renders at a different font SIZE from the one it measures with: ui's font_buffer sets only the font family, so the size stayed ambient while TableView::new measured against buffer_font_size. Every glyph then rendered a fraction wider than measured, and the error accumulates with string length — invisible on a 15-character title, just past the padding slack on a 16-character one, which is exactly the reported Column_number_9-fits, Column_number_10-wraps threshold. The rendered size is now pinned to the measured one. zed-industries#74: a notebook whose file was deleted while Zed was closed didn't restore at all, taking any unsaved changes with it. Nothing to do with deletion handling — open_buffer deliberately returns an empty buffer for a path with no entry, so the notebook parses, but try_open then demanded a worktree entry id and a deleted file has none. That id was already optional on NotebookItem (it is only a fallback for entry_id, which re-resolves from the path), so nothing needed it to be present. zed-industries#75: a restored untitled notebook showed no dirty marker until the next keystroke. Same trap phase 69 fixed for file-backed notebooks and missed here: rebuilding a notebook from stored JSON makes those cells their own baseline, so it reads as clean. Contents are only stored when there ARE unsaved changes, so a restored untitled notebook is unsaved by definition and now says so. Also filed zed-industries#76: restoring unsaved changes over a file edited elsewhere gives no notification. The conflict flag may well be set correctly and simply invisible until save time, which is the first thing to check — the two possible causes need different fixes, so nothing is attempted yet. Backlogged from zed-industries#72's confirmation: a restart should read Restarting → Idle, not Restarting → Starting → Idle. Verified: clippy clean, 57 repl tests pass. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01JkvYRuX9r6PmLXpxNie2R7
Rendered
Here are my high-level thoughts on our roadmap for roughly the next year. I link out to tracking issues for the first two milestones, which we can populate with more detail.
@maxbrunsfeld @as-cii I'd love your feedback on this. Do you agree with this high-level plan? What am I missing? What else could we do? No pressure to give critique if you're on board, but I'd love to get to an explicit approval from both of you before we merge.
@ledwards Hopefully this can set some context for next week's meeting. Your feedback is welcome, too.