Skip to content

Commit 33ebf96

Browse files
authored
feat: harden benchmark runs and stabilize TUI prompt bar (#196)
* fix(tui): stop the input card fossilizing above the stream as a ghost prompt After submitting a prompt the input card's top border (──────── ● off) + ❯ was fossilized above the streamed content as a second, ghostly prompt. The turn's first scrollback commit runs `run_in_terminal`, whose teardown erase-height drifts on the first transition into streaming and leaves the card behind; suppressing the card only *during* the handoff is too late (the erase runs before the repaint). - Hide the input card from turn-start until the turn's first commit, then repaint it below the live stream so the user can still see where to steer. Two windows are covered: a `_turn_starting` hint set the instant a turn is dispatched (the pre-attach gap where the resumed prompt can repaint before the running-prompt delegate exists), and, once attached, the delegate reporting `running_prompt_hide_input_card()` until it commits. - Wire the hint once, inside `run_soul_command` (the single funnel for all five dispatch paths), before the first await lets the resumed prompt repaint. - `clear_turn_starting()` is the public counterpart to `mark_turn_starting()`, used by the shell's error-path cleanup instead of writing the private `_turn_starting` attribute directly. - Tests: a pyte (VT100 emulator) e2e asserting the card never fossilizes above the stream and returns mid-turn once content commits (documented as a manual/local check — it is skipped on CI); a CI-runnable renderer-direct test that the chrome renderer actually consults the gate; unit tests for the gate + hint lifecycle, including the new public clear method. pyte is a new dev/test dependency. * fix(tui): keep prompt top border during turn start * Add native benchmark slash command * Use active model for benchmark slash command * Expand native benchmark suite * Improve benchmark run reports * Clean up benchmark summary output * Harden benchmark artifacts * Add namespaced benchmark slash commands * Improve benchmark aggregate reports * fix(tui): keep lazy-load input card marker * Add SWE-style benchmark command * Keep invoked skills active across turns * Harden benchmark runner and TUI prompt state * Add benchmark reproducibility metadata * Fix benchmark timeout and pythinker metadata * Add benchmark coding activity metrics * fix(benchmark): show tool-call activity breakdown * Handle missing benchmark activity in reports * Add publishability warnings to benchmark reports * Fix benchmark publishability warning scopes * fix(tui): keep running prompt input visible * Fix benchmark publishability warning scope * docs(tui): specify full pi renderer port * docs(tui): plan full pi renderer port * Fix benchmark local fixture warnings * Add benchmark compare command * feat(tui): add renderer primitives * feat(tui): add line diff planner * Fix benchmark compare execution and report filtering * feat(tui): coalesce render invalidations * feat(tui): compose running prompt scene * Fix benchmark compare review regressions * feat(tui): render running prompt as stable scene * fix(tui): keep running scene body visible * Add benchmark export command * test(tui): cover stable streaming prompt scene * Add benchmark source discovery * Remove benchmark discovery task scratch report * fix(tui): document running card handoff exception * Add provisional benchmark quiz fixtures * Fix benchmark discovery test typing * Write discovered benchmark quiz manifests * fix(tui): preserve running scene styles * Document benchmark comparison workflow * Remove generated docs changes from benchmark docs * Fix benchmark runner import order * Format benchmark compare warnings * Fix benchmark typing for package check * Sync benchmark changelog docs * Parse benchmark activity tool names from wire payloads * feat(tui): add experimental Focus TUI mode for active agent turns Adds an opt-in fullscreen Focus TUI (tui.focus_mode) that pins the composer, hides file activity by default behind a shelf, and renders live agent output without terminal scrollback jumps. Wires the new model/surface pair through shell and visualize so streamed turns can opt in alongside the existing diff-rendered scene mode. Covered with focused unit + integration tests for the model, the surface, the running-prompt scene regression, the config default, the shell PTY prompt layout, and the wire helper for Focus TUI init/close events. * Fix subagent activity in TUI status tail * Harden benchmark execution and prompt chrome * Keep prompt bar visible while agent loads * Add prompt bar changelog entry * Fix PR review and Python 3.12 benchmark metadata * Address benchmark and TUI review findings * Resolve PR review follow-ups * Normalize prompt visualization test imports * Address prompt test code-quality nit
1 parent 7736f7e commit 33ebf96

87 files changed

Lines changed: 8672 additions & 86 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ static/
6565
!.claude/hooks/
6666
!.claude/hooks/**
6767
.pythinker/
68+
.pi-subagents/
6869
.worktrees/
6970
reference-scan/
7071
blackbox/
@@ -85,4 +86,4 @@ htmlcov/
8586
.playwright/
8687

8788
# Cursor debug-mode session logs (machine-local NDJSON)
88-
.cursor/debug-*.log
89+
.cursor/debug-*.log

CHANGELOG.md

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,52 @@ GitHub Releases page; `0.8.0` is the new starting line.
1515

1616
## Unreleased
1717

18+
- Show active subagent tool work in the pinned TUI status tail instead of
19+
leaving long foreground agent runs on the generic composing spinner.
20+
- Keep the TUI prompt bar visible while an agent turn is starting so the
21+
empty composer does not disappear during lazy-load frames.
22+
- Ported the running agent TUI toward Pi's stable diff-rendered scene model so streamed output keeps the input card visible without prompt jumps.
23+
- Added publishability-focused benchmark comparison planning for multi-model
24+
runs, activity metrics, exportable reports, and safe online source discovery.
25+
- Added an experimental Focus TUI mode for active agent turns, keeping the composer pinned, hiding file activity by default, and rendering live output without terminal scrollback jumps.
26+
- Stream file write/edit activity in a compact live shelf so changed files update in place during agent runs instead of adding noisy terminal rows.
27+
- Hardened `/benchmark` local fixture runs: task `max_steps` now caps the
28+
underlying agent turn, and `/benchmark:swe` requires `--trusted-dataset true`
29+
because trusted local fixture datasets execute verification commands.
30+
- Hardened benchmark and active-skill security edges: SWE verification commands
31+
are shape-validated before execution, benchmark discovery requires explicit
32+
network opt-in, benchmark run IDs avoid clock collisions, runtime overrides
33+
restore after setup failures, and active-skill deactivation persistence
34+
failures are surfaced instead of swallowed.
35+
- Strengthened the bundled `pythinker-core` benchmark suite with edge-case
36+
fixtures for atomic rollback, iterable de-duplication, explicit falsey
37+
metadata values, and safe path joins across absolute, sibling-prefix, parent,
38+
and symlink escapes.
39+
40+
- Keep the terminal input composer pinned to the bottom during agent runs with a fullscreen prompt mode to reduce TUI flicker.
41+
42+
- Explicitly invoked skills now remain active across later turns through a
43+
compact reminder, and can be cleared with a named stop request or "normal mode".
44+
45+
- **No more ghost/duplicate input prompt while the agent works.** After
46+
submitting a prompt, the editable input row is no longer fossilized above the
47+
stream as a second, ghostly prompt. The top border stays visible while the
48+
pre-attach race frame still collapses before the running-prompt delegate
49+
exists, preventing prompt chrome from fossilizing above the spinner. Once the
50+
running frame owns the prompt, the `` marker stays visible while only the
51+
editable buffer is hidden until the first scrollback commit, avoiding the
52+
collapsed one-line card under the lazy-load spinner; the full editable row
53+
remains below the live stream so you can still see where to steer.
54+
55+
- Added native `/benchmark` slash command for deterministic local Pythinker
56+
model evaluation with bundled smoke tasks, replayable artifacts, and branded
57+
markdown reports.
58+
- Expanded `/benchmark start` to use a richer default core suite, isolate file
59+
edits through the active toolset workspace override, and exclude generated
60+
verification caches from changed-file reports.
61+
- Added `/benchmark:swe` for native SWE-style JSONL benchmark tasks that run
62+
through Pythinker's existing model, tool, verification, and artifact path.
63+
1864
## 0.56.0 (2026-07-02)
1965

2066
- **Windows shell UI recovers from mid-session console blanking.** The TUI's

README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,22 @@ Optional web frontend and visualization frontend ship alongside the CLI for rich
126126

127127
Swap providers and models per-session: `--model openai/gpt-5.5`, hosted Pythinker models, or your own keys.
128128

129+
</td>
130+
</tr>
131+
<tr>
132+
<td width="50%" valign="top">
133+
134+
### 📊 Local Benchmarks
135+
136+
Run `/benchmark` to execute deterministic local coding tasks through the active Pythinker session, with replayable artifacts and verification reports.
137+
138+
</td>
139+
<td width="50%" valign="top">
140+
141+
### 🧪 SWE-Style Fixtures
142+
143+
Run trusted local JSONL fixtures with `/benchmark:swe --dataset <path> --trusted-dataset true` when you want SWE-style task inputs without a hosted evaluator.
144+
129145
</td>
130146
</tr>
131147
</table>
@@ -618,6 +634,7 @@ Pythinker is a small, extensible runtime — not a monolith. Build on it.
618634
| 🌊 **Flows** | `/flow:<name>` executes bundled prompt flows | bundled & user-defined |
619635
| 🪝 **Hooks** | Observe or block tool execution; integrate policy or automation | hook events API |
620636
| 🧩 **Plugins** | Installable extension packages | `pythinker plugin` |
637+
| 📊 **Benchmarks** | Deterministic local coding tasks with verification reports | `/benchmark`, `src/pythinker_code/benchmark/` |
621638

622639
---
623640

docs/.vitepress/config.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@ export default withMermaid(defineConfig({
8383
{ text: 'pythinker term Subcommand', link: '/en/reference/pythinker-term' },
8484
{ text: 'pythinker dashboard Subcommand', link: '/en/reference/pythinker-dashboard' },
8585
{ text: 'pythinker web Subcommand', link: '/en/reference/pythinker-web' },
86+
{ text: 'Pythinker Benchmark', link: '/en/reference/pythinker-benchmark' },
8687
{ text: 'Slash Commands', link: '/en/reference/slash-commands' },
8788
{ text: 'Keyboard Shortcuts', link: '/en/reference/keyboard' },
8889
],

docs/AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ This repository uses VitePress for the documentation site. Most pages now contai
99
- Guides: getting-started, use-cases, interaction, sessions, ides, integrations
1010
- Customization: mcp, plugins, hooks, skills, agents, print-mode, wire-mode
1111
- Configuration: config-files, providers, overrides, env-vars, data-locations
12-
- Reference: pythinker-command, pythinker-info, pythinker-acp, pythinker-mcp, pythinker-term, pythinker-dashboard, pythinker-web, slash-commands, keyboard
12+
- Reference: pythinker-command, pythinker-info, pythinker-acp, pythinker-mcp, pythinker-term, pythinker-dashboard, pythinker-web, pythinker-benchmark, slash-commands, keyboard
1313
- FAQ: faq
1414
- Release notes: changelog, breaking-changes
1515
- Navigation and sidebar are defined in `docs/.vitepress/config.ts`. Any new or renamed page must be wired there.

docs/en/customization/architecture.md

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,38 @@ Provider modules in `auth/`: `openai`, `anthropic_direct`, `opencode_go`, `minim
166166
follow `<platform_id>/<model_id>`. Provider-aware code derives the provider from the active
167167
model; `/usage` defaults to the active provider, with `/usage all` as the explicit aggregate.
168168

169+
## Benchmark runner
170+
171+
Native benchmark execution lives under `src/pythinker_code/benchmark/` and is surfaced through
172+
the slash-command registry in `src/pythinker_code/soul/slash.py`. It is a local-fixture harness:
173+
tasks materialize files into a per-run workspace, run through the same `PythinkerSoul.turn`
174+
path as a normal session, then execute a verification command and persist artifacts.
175+
176+
| Path | Purpose | Key entry points and interfaces |
177+
| --- | --- | --- |
178+
| `src/pythinker_code/benchmark/commands.py` | Slash-command parser and orchestrator for `start`, `estimate`, `list`, `show`, `report`, `export`, `compare`, `discover`, and `swe`. | `dispatch_benchmark`, `BenchmarkArgs`, `benchmark_usage` |
179+
| `src/pythinker_code/benchmark/compare.py` and `export.py` | Publishability warnings and report-row export formatting for model comparisons. | `readiness_warnings`, `render_export` |
180+
| `src/pythinker_code/benchmark/discovery.py` | Allowlisted online source discovery and provisional quiz-fixture JSONL conversion. | `discover_benchmark_sources`, `quiz_fixture_from_discovery` |
181+
| `src/pythinker_code/benchmark/runner.py` | Per-task execution: workspace materialization, work-dir override, temporary task `max_steps` limit, timeout handling, verification, and artifact finalization. | `run_task`, `BenchmarkResult`, `VerificationResult` |
182+
| `src/pythinker_code/benchmark/tasks.py` | Bundled task schema and workspace materialization. | `BenchmarkTask`, `load_task`, `materialize_workspace` |
183+
| `src/pythinker_code/benchmark/suites.py` | Bundled suite loading and ordering. | `load_suite`, `list_suite_names` |
184+
| `src/pythinker_code/benchmark/swe.py` | Trusted local SWE-style JSONL fixture loading. | `load_swe_instances`, `swe_instance_to_task` |
185+
| `src/pythinker_code/benchmark/records.py` and `report.py` | Per-run artifact writing and report rendering. | `BenchmarkRecorder`, `render_run_report`, `render_show` |
186+
187+
The bundled suite files live in `src/pythinker_code/benchmark/bundled/suites/`; bundled task
188+
definitions live in `src/pythinker_code/benchmark/bundled/tasks/`. The `pythinker-core` suite
189+
targets common agent failure modes: transactional rollback, ordered de-duplication, explicit
190+
`None` versus falsey metadata, and safe path joins. The runner uses the active model provider
191+
from session config; it does not create a separate provider stack.
192+
193+
`/benchmark:swe` is intentionally labeled SWE-style local fixture support, not full SWE-bench
194+
Docker evaluation. It accepts local JSONL records, rejects unsafe workspace paths, and requires
195+
`--trusted-dataset true` before running dataset-provided verification commands.
196+
197+
`/benchmark discover` only writes provisional review manifests when `--output` ends in
198+
`.jsonl`; those records are untrusted quiz fixtures with empty workspaces, not runnable
199+
SWE-style local fixtures.
200+
169201
## Wire and UI frontends
170202

171203
| Path | Purpose | Key entry points and interfaces |
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
# Pythinker Benchmark
2+
3+
Pythinker Benchmark is the native local-fixture harness for comparing how a configured Pythinker model handles small, deterministic coding tasks. It runs inside the current Pythinker session, uses the active toolset and approval runtime, materializes each task into an isolated workspace, executes the agent turn, then runs the task's verification command and writes replayable artifacts.
4+
5+
It is designed for repeatable local checks, not hosted leaderboard scoring. SWE-style input is supported as trusted local fixtures; it is not a full SWE-bench Docker runner.
6+
7+
## Commands
8+
9+
Run the default core suite:
10+
11+
```sh
12+
/benchmark start
13+
```
14+
15+
Run a single task or named suite:
16+
17+
```sh
18+
/benchmark start --task core-safe-path-join
19+
/benchmark start --suite pythinker-smoke
20+
```
21+
22+
Estimate a run without making model calls:
23+
24+
```sh
25+
/benchmark estimate --suite pythinker-core
26+
```
27+
28+
Inspect available tasks and saved runs:
29+
30+
```sh
31+
/benchmark list
32+
/benchmark show <run-id>
33+
/benchmark report --suite pythinker-core
34+
```
35+
36+
Compare configured models and export report data:
37+
38+
```sh
39+
/benchmark compare --models model-a,model-b --suite pythinker-core --repeat 3
40+
/benchmark export --suite pythinker-core --format csv
41+
```
42+
43+
Discover candidate tasks from an allowlisted online source:
44+
45+
```sh
46+
/benchmark discover --source terminal-bench --difficulty hard --limit 5 --output ./candidate-tasks.jsonl
47+
```
48+
49+
Namespaced aliases are available for interactive completion: `/benchmark:start`, `/benchmark:all`, `/benchmark:estimate`, `/benchmark:list`, `/benchmark:show`, `/benchmark:report`, `/benchmark:compare`, and `/benchmark:swe`. There is no `/benchmark:export` or `/benchmark:discover` alias.
50+
51+
The supported flags are:
52+
53+
| Flag | Applies to | Behavior |
54+
| --- | --- | --- |
55+
| `--model <model-key>` | `start`, `estimate`, `swe` | Uses a configured model instead of the active/default model. |
56+
| `--models <model-a,model-b>` | `compare` | Runs each selected task for at least two distinct configured models. |
57+
| `--task <task-id>` | `start`, `estimate`, `compare` | Runs, estimates, or compares one bundled task. Mutually exclusive with `--suite`. |
58+
| `--suite <suite-name>` | `start`, `estimate`, `report`, `export`, `compare` | Selects a bundled suite or filters report/export output. |
59+
| `--repeat <n>` | `start`, `estimate`, `compare`, `swe` | Runs or estimates each selected task multiple times. |
60+
| `--timeout-seconds <n>` | `start`, `compare`, `swe` | Overrides the task timeout for the agent turn. |
61+
| `--format json\|csv` | `export` | Selects the export format. |
62+
| `--output <path>` | `start`, `compare`, `show`, `report`, `export`, `swe`; `.jsonl` only for `discover` | Uses a custom benchmark artifact root for run/report/export commands. For `discover`, it only writes a provisional manifest when the path suffix is `.jsonl`. |
63+
| `--dataset <path.jsonl>` | `swe` | Loads SWE-style local fixture records from a JSONL file. |
64+
| `--instance <instance-id>` | `swe` | Runs only one instance from the dataset. |
65+
| `--trusted-dataset true` | `swe` | Required acknowledgement before dataset verification commands can run. |
66+
| `--source <allowlisted>` | `discover` | Selects an allowlisted benchmark source such as `terminal-bench`. |
67+
| `--difficulty <difficulty>` | `discover` | Filters discovered candidates by difficulty. Defaults to `hard`. |
68+
| `--limit <n>` | `discover` | Limits discovered candidates. Defaults to `5`. |
69+
70+
`--max-concurrency` is parsed but must remain `1` in the current implementation. `--judges` is parsed but only `off` is supported.
71+
72+
## Architecture
73+
74+
The benchmark integration has four layers:
75+
76+
1. Slash commands in `src/pythinker_code/soul/slash.py` register `/benchmark` and the namespaced aliases.
77+
2. `src/pythinker_code/benchmark/commands.py` parses arguments, resolves the active model, expands tasks or suites, and dispatches each run.
78+
3. `src/pythinker_code/benchmark/runner.py` prepares the workspace, temporarily applies the task's `max_steps` to the soul loop, overrides the runtime work directory, calls `PythinkerSoul.turn`, runs verification, and restores the previous runtime state in a `finally` block.
79+
4. `src/pythinker_code/benchmark/records.py` and `src/pythinker_code/benchmark/report.py` persist run metadata, traces, summaries, context and Wire slices, and Markdown reports.
80+
81+
Task and suite definitions are regular bundled JSON files under `src/pythinker_code/benchmark/bundled/`. `src/pythinker_code/benchmark/tasks.py` validates bundled task shape and rejects unsafe workspace paths before files are materialized.
82+
83+
## Bundled suites
84+
85+
`pythinker-core` is the default suite. It covers deterministic local coding tasks that expose common agent failure modes:
86+
87+
- `core-atomic-transfer`: transactional rollback, missing accounts, insufficient funds, and non-positive transfer amounts.
88+
- `core-dedup-order`: ordered de-duplication for lists and generators, including falsey values.
89+
- `core-explicit-none-metadata`: explicit `None` handling without losing valid falsey metadata values.
90+
- `core-safe-path-join`: path traversal defense, absolute-path rejection, sibling-prefix escapes, and symlink escapes.
91+
92+
`pythinker-smoke` contains smaller tasks for validating the runner itself:
93+
94+
- `smoke-edit-readme`
95+
- `smoke-fix-python-test`
96+
- `smoke-add-small-function`
97+
98+
## Task schema
99+
100+
A bundled task JSON object contains:
101+
102+
```json
103+
{
104+
"id": "core-safe-path-join",
105+
"title": "Safe Path Join",
106+
"description": "Reject path traversal while allowing paths inside the root.",
107+
"prompt": "Fix paths.safe_join ...",
108+
"workspace": {
109+
"files": {
110+
"paths.py": "...",
111+
"test_paths.py": "..."
112+
}
113+
},
114+
"verification": {
115+
"type": "command",
116+
"command": "python -m pytest test_paths.py -q"
117+
},
118+
"limits": {
119+
"timeout_seconds": 120,
120+
"max_steps": 60
121+
},
122+
"tags": ["core", "security"]
123+
}
124+
```
125+
126+
Workspace paths must be relative, non-empty, and must not contain `..` path segments. Verification type is currently `command`.
127+
128+
## Publishable comparisons
129+
130+
Use `/benchmark compare` when comparing configured models:
131+
132+
```sh
133+
/benchmark compare --models model-a,model-b --suite pythinker-core --repeat 3
134+
```
135+
136+
Export the saved report rows when you need machine-readable results:
137+
138+
```sh
139+
/benchmark export --suite pythinker-core --format csv
140+
```
141+
142+
Reports include publishability warnings. Treat warnings as blockers for public claims, not as lint. Local fixture runs are useful for regression and internal comparison, but they are not SWE-bench Docker evaluations.
143+
144+
## Online discovery and quiz fixtures
145+
146+
`/benchmark discover` fetches metadata from allowlisted benchmark sources and writes provisional manifests. It does not execute source-provided commands and does not make discovered tasks trusted:
147+
148+
```sh
149+
/benchmark discover --source terminal-bench --difficulty hard --limit 5 --output ./candidate-tasks.jsonl
150+
```
151+
152+
The `--output` flag writes a manifest only when the path suffix is `.jsonl`. These JSONL records preserve the source URL and are marked `trusted: false`. Online quiz fixture records use deterministic review metadata:
153+
154+
```json
155+
{
156+
"verification": {
157+
"type": "answer_contains",
158+
"expected_substrings": ["terminal-bench", "hard"]
159+
},
160+
"trusted": false,
161+
"workspace": {
162+
"files": {}
163+
}
164+
}
165+
```
166+
167+
These manifests are for dataset review and offline conversion first. They are not runnable through `/benchmark:swe`, and `answer_contains` is not executed by the benchmark runner. Convert reviewed tasks into trusted local fixtures with workspace files and a local verification command before running them.
168+
169+
## SWE-style local fixtures
170+
171+
`/benchmark:swe` loads newline-delimited JSON records with local workspace files and a verification command. The command is executed on the local machine after the agent turn, so the slash command refuses to run unless `--trusted-dataset true` is present:
172+
173+
```sh
174+
/benchmark:swe --dataset ./cases.jsonl --trusted-dataset true
175+
```
176+
177+
Each record must include `instance_id`, `repo`, `base_commit`, `problem_statement`, `workspace.files`, and `verification.command`. Optional `FAIL_TO_PASS`, `PASS_TO_PASS`, and `limits` fields are folded into the generated local fixture task.
178+
179+
## Artifacts
180+
181+
By default, benchmark runs are written under the Pythinker share directory in `benchmarks/<run-id>/`. A custom root can be supplied with `--output <path>`.
182+
183+
Each run directory contains:
184+
185+
| File or directory | Contents |
186+
| --- | --- |
187+
| `run.json` | Run metadata: command, model key, provider key, task id, suite name, repeat index, timestamps, and final status. |
188+
| `summary.json` | Runtime summary: duration, step count, tool calls, changed files, token counts, verification status, and exit reason. |
189+
| `report.md` | Human-readable report for the run. |
190+
| `trace.jsonl` | Benchmark event trace, including workspace preparation, model message, and verification result. |
191+
| `workspace/` | The materialized local task workspace after the run. |
192+
| `context.jsonl` and `wire.jsonl` | Slices copied from the active session for replay and debugging. |
193+
194+
Generated verification caches such as `__pycache__`, `.pytest_cache`, `.ruff_cache`, and `.mypy_cache` are excluded from changed-file summaries.

0 commit comments

Comments
 (0)