From 538a4a267b813147ea0d6350d8c70f05cd5f2405 Mon Sep 17 00:00:00 2001
From: Piotr Osiewicz <24362066+osiewicz@users.noreply.github.com>
Date: Sun, 2 Aug 2026 20:40:37 +0200
Subject: [PATCH] Add wezel scenario for an incremental editor build (#62016)
# Objective
This PR codifies one of the scenarios I've used to measure build health
of a codebase via [Wezel](https://wezel.build), a tool I've built for
that purpose.
## Solution
`wezel exp run incremental-editor`
## Testing
- Did you test these changes? If so, how?
- Are there any parts that need more testing?
- How can other people (reviewers) test your changes? Is there anything
specific they need to know?
- If relevant, what platforms did you test these changes on, and are
there any important ones you can't test?
## Self-Review Checklist:
- [ ] I've reviewed my own diff for quality, security, and reliability
- [ ] Unsafe blocks (if any) have justifying comments
- [ ] The content adheres to Zed's UI standards
([UX/UI](https://github.com/zed-industries/zed/blob/main/CONTRIBUTING.md#uiux-checklist)
and
[icon](https://github.com/zed-industries/zed/blob/main/crates/icons/README.md)
guidelines)
- [ ] Tests cover the new/changed behavior
- [ ] Performance impact has been considered and is acceptable
## Showcase
> This section is optional. If this PR does not include a visual change
or does not add a new user-facing feature, you can delete this section.
- Help others understand the result of this PR by showcasing your
awesome work!
- If this PR includes a visual change, consider adding a screenshot,
GIF, or video
- A before/after comparison is very useful for changes to existing
features!
While a showcase should aim to be brief and digestible, you can use a
toggleable section to save space on longer showcases:
Click to view showcase
My super cool demos here
---
Release Notes:
- N/A or Added/Fixed/Improved ...
---
.wezel/.gitignore | 3 +
.wezel/config.toml | 13 +
.../editor-doc-comment.patch | 12 +
.../incremental-editor/experiment.toml | 23 ++
.wezel/schema.json | 226 ++++++++++++++++++
.wezel/wezel.lock | 10 +
6 files changed, 287 insertions(+)
create mode 100644 .wezel/.gitignore
create mode 100644 .wezel/config.toml
create mode 100644 .wezel/experiments/incremental-editor/editor-doc-comment.patch
create mode 100644 .wezel/experiments/incremental-editor/experiment.toml
create mode 100644 .wezel/schema.json
create mode 100644 .wezel/wezel.lock
diff --git a/.wezel/.gitignore b/.wezel/.gitignore
new file mode 100644
index 0000000000..14a8cd73b5
--- /dev/null
+++ b/.wezel/.gitignore
@@ -0,0 +1,3 @@
+# Wezel project-local state. Add patterns here as needed.
+events/
+*.local.toml
diff --git a/.wezel/config.toml b/.wezel/config.toml
new file mode 100644
index 0000000000..ffe52f025c
--- /dev/null
+++ b/.wezel/config.toml
@@ -0,0 +1,13 @@
+project_id = "85f406f4-69f1-4f41-b8e2-45550cd925d7"
+name = "zed"
+
+[tools]
+targets = [
+ "aarch64-apple-darwin",
+ "x86_64-apple-darwin",
+ "aarch64-unknown-linux-gnu",
+ "x86_64-unknown-linux-gnu",
+]
+
+[tools.foragers.cargo]
+github = "wezel-build/forager_cargo"
diff --git a/.wezel/experiments/incremental-editor/editor-doc-comment.patch b/.wezel/experiments/incremental-editor/editor-doc-comment.patch
new file mode 100644
index 0000000000..42839fdf71
--- /dev/null
+++ b/.wezel/experiments/incremental-editor/editor-doc-comment.patch
@@ -0,0 +1,12 @@
+diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs
+index 31ddc4b5c4..cd8e8e77e2 100644
+--- a/crates/editor/src/editor.rs
++++ b/crates/editor/src/editor.rs
+@@ -11,6 +11,7 @@
+ //! All other submodules and structs are mostly concerned with holding editor data about the way it displays current buffer region(s).
+ //!
+ //! If you're looking to improve Vim mode, you should check out Vim crate that wraps Editor and overrides its behavior.
++//!
+ pub mod actions;
+ pub mod blink_manager;
+ mod bracket_colorization;
diff --git a/.wezel/experiments/incremental-editor/experiment.toml b/.wezel/experiments/incremental-editor/experiment.toml
new file mode 100644
index 0000000000..1d664fa18f
--- /dev/null
+++ b/.wezel/experiments/incremental-editor/experiment.toml
@@ -0,0 +1,23 @@
+#:schema ../../schema.json
+
+description = "Measures rebuild time after a trivial edit to the editor crate"
+
+# `zed` rather than `workspace`: it pulls in the whole editor -> ... -> binary
+# chain that a developer actually waits on, without also building collab, the
+# benchmarks and the other workspace members that no editor edit can affect.
+
+# Prime the build cache off a clean build, so the measured step below pays only
+# for editor and its dependents.
+[step.cargo.prime-cache]
+command = "build"
+build_target = ["zed"]
+
+# The patch only appends an empty `//!` line to the crate-root doc comment, so
+# nothing about editor's public surface changes. What's left is the floor cost
+# of touching the crate at all: rustc's incremental reuse for editor itself,
+# plus the relink of every crate downstream of it.
+[step.cargo.editor-doc-comment]
+apply-diff = true
+command = "build"
+build_target = ["zed"]
+summary.incremental-editor-build = { outcome = "time_ms", aggregation = "min", samples = 5 }
diff --git a/.wezel/schema.json b/.wezel/schema.json
new file mode 100644
index 0000000000..e95c3d9e9d
--- /dev/null
+++ b/.wezel/schema.json
@@ -0,0 +1,226 @@
+{
+ "$schema": "http://json-schema.org/draft-07/schema#",
+ "definitions": {
+ "Aggregation": {
+ "description": "How to aggregate measurement values into a conclusion scalar.",
+ "enum": [
+ "sum",
+ "mean",
+ "median",
+ "max",
+ "min"
+ ],
+ "type": "string"
+ },
+ "DiffField": {
+ "anyOf": [
+ {
+ "type": "boolean"
+ },
+ {
+ "type": "string"
+ }
+ ],
+ "description": "Either a boolean (uses `.patch`) or an explicit patch filename."
+ },
+ "EmbeddedSummaryToml": {
+ "description": "Summary definition embedded under a step. The summary's `name` and `step` fields are recovered from the map keys (`step..summary.`).",
+ "properties": {
+ "aggregation": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/Aggregation"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "default": null,
+ "description": "How to combine multiple matching values. Omit when the filter is expected to select a single value."
+ },
+ "bisect": {
+ "default": true,
+ "description": "Trigger bisection when this summary regresses.",
+ "type": "boolean"
+ },
+ "filter": {
+ "additionalProperties": {
+ "type": "string"
+ },
+ "default": {},
+ "description": "Tag key=value filters applied before aggregation.",
+ "type": "object"
+ },
+ "outcome": {
+ "description": "Outcome name (as emitted by the forager) to aggregate over.",
+ "type": "string"
+ },
+ "samples": {
+ "default": 1,
+ "description": "Number of forager invocations of the step to take. Lint requires all summaries on the same step to agree. Default 1.",
+ "format": "uint",
+ "minimum": 0.0,
+ "type": "integer"
+ }
+ },
+ "required": [
+ "outcome"
+ ],
+ "type": "object"
+ },
+ "Step_cargo": {
+ "additionalProperties": false,
+ "description": "Body of a `[step..]` table. The tool name is recovered from the outer key, not a field; flatten-collected `rest` is forwarded to the forager plugin via `FORAGER_INPUTS` as JSON.",
+ "properties": {
+ "all_targets": {
+ "default": false,
+ "description": "Build every target (lib, bins, tests, benches, examples) via `--all-targets`.",
+ "type": "boolean"
+ },
+ "apply-diff": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/DiffField"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Apply a patch before running this step. `true` uses `.patch`; a string overrides the filename."
+ },
+ "benches": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/TargetSpecifier"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Benchmarks to build, as `--benches` (all) or `--bench ` per name. Omit to build none."
+ },
+ "bins": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/TargetSpecifier"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Binaries to build, as `--bins` (all) or `--bin ` per name. Omit to build none."
+ },
+ "build_target": {
+ "allOf": [
+ {
+ "$ref": "#/definitions/PackageSpecifier"
+ }
+ ],
+ "description": "Build target — either a list of package names or the literal string \"workspace\"."
+ },
+ "command": {
+ "allOf": [
+ {
+ "$ref": "#/definitions/Command"
+ }
+ ],
+ "description": "Cargo subcommand to invoke (`build`, `bench`, or `test`)."
+ },
+ "description": {
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "examples": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/TargetSpecifier"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Examples to build, as `--examples` (all) or `--example ` per name. Omit to build none."
+ },
+ "lib": {
+ "default": false,
+ "description": "Build the library target via `--lib`.",
+ "type": "boolean"
+ },
+ "profile": {
+ "default": null,
+ "description": "Cargo profile to use, propagated as `--profile `. Omit to use the default profile.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "summary": {
+ "additionalProperties": {
+ "allOf": [
+ {
+ "$ref": "#/definitions/EmbeddedSummaryToml"
+ },
+ {
+ "properties": {
+ "outcome": {
+ "description": "**`time_ms`** — cargo's own reported build duration (the `Finished … in ` line), in milliseconds. Excludes process startup and dependency resolution.",
+ "type": "string"
+ }
+ }
+ }
+ ]
+ },
+ "description": "Summaries emitted by this step, keyed by summary name.",
+ "type": "object"
+ },
+ "tests": {
+ "anyOf": [
+ {
+ "$ref": "#/definitions/TargetSpecifier"
+ },
+ {
+ "type": "null"
+ }
+ ],
+ "description": "Test targets to build, as `--tests` (all) or `--test ` per name. Omit to build none."
+ }
+ },
+ "required": [
+ "build_target",
+ "command"
+ ],
+ "type": "object"
+ }
+ },
+ "description": "Top-level shape of `.wezel/experiments//experiment.toml`.\n\nSteps live under `[step..]`. The outer map is keyed by tool name and the inner by step name; bodies are wrapped in `toml::Spanned` so the parser can recover file-line order across reopened tables.",
+ "properties": {
+ "description": {
+ "description": "Human-readable description of what the experiment measures.",
+ "type": [
+ "string",
+ "null"
+ ]
+ },
+ "step": {
+ "additionalProperties": false,
+ "description": "`[step..]`. Two-level map; step names must be unique across tools (enforced in [`parse_experiment`]).",
+ "properties": {
+ "cargo": {
+ "additionalProperties": {
+ "$ref": "#/definitions/Step_cargo"
+ },
+ "description": "Runs `cargo ` and records the build time cargo reports",
+ "type": "object"
+ }
+ },
+ "type": "object"
+ }
+ },
+ "required": [
+ "step"
+ ],
+ "title": "Wezel experiment.toml",
+ "type": "object"
+}
\ No newline at end of file
diff --git a/.wezel/wezel.lock b/.wezel/wezel.lock
new file mode 100644
index 0000000000..c4c1d9ed27
--- /dev/null
+++ b/.wezel/wezel.lock
@@ -0,0 +1,10 @@
+version = 1
+
+[tools.foragers.cargo]
+github = "wezel-build/forager_cargo"
+tag = "nightly-2026-07-17-dd1c13f"
+
+[tools.foragers.cargo.assets]
+aarch64-unknown-linux-gnu = "sha256:4162756b5268d29046242a1f29d41649e07ef024c72a690914bff61af82dcee1"
+x86_64-apple-darwin = "sha256:263224ed47256fabd71a02e638fccd768cda4ff7023f34cc35bd520edcee0162"
+x86_64-unknown-linux-gnu = "sha256:18092f74eabef4e0774d14ac3ca967505b6d5ccf6fd2a1f1ddef79d8317b0722"