Skip to content

docs(agent): pistes d'amélioration de l'agent d'édition, mesurées - #217

Open
EtienneLescot wants to merge 12 commits into
mainfrom
claude/agent-improvement-leads
Open

docs(agent): pistes d'amélioration de l'agent d'édition, mesurées#217
EtienneLescot wants to merge 12 commits into
mainfrom
claude/agent-improvement-leads

Conversation

@EtienneLescot

@EtienneLescot EtienneLescot commented Jul 31, 2026

Copy link
Copy Markdown
Collaborator

Pistes d'amélioration de l'agent d'édition, appuyées sur les mesures du workbench. Rien à fusionner ici — cette PR est un document de travail, à traiter plus tard.

Chaque piste porte la mesure qui la justifie et, quand elle existe, la contre-mesure qui la départagera. L'ordre est celui où je les traiterais.


1. Le poids du track fait échouer un tour sur deux

Mesuré. Sur la prise réelle de 66 s, getCursorTrack rend 356 points pour 24 238 caractères. La requête suivante passe à ~45 000 caractères. Sur 5 répétitions du prompt wizard, 3 ont expiré à 120 s, toujours au même endroit : juste après l'appel à l'outil. Les 2 qui aboutissent produisent un montage correct.

Ce n'est pas un défaut du modèle : lui donner la donnée le fait échouer.

Pistes, de la moins à la plus intrusive :

  • Supprimer virtualSec quand il est égal à atSec. 28 % du payload, strictement redondant tant qu'aucune coupe n'existe. Un champ virtualEqualsSource: true en tête suffirait. Gain immédiat, aucune perte d'information.
  • Relever le timeout du banc. Ne corrige rien, mais évite de confondre lenteur et refus.
  • Baisser la résolution à 2–3 Hz. À tester après les deux précédentes, jamais avant : ça change ce que le modèle voit, donc on ne saurait plus attribuer une amélioration à la place gagnée ou à la lisibilité.

Le plafond de buildCursorTrack est par ailleurs mou : DEFAULT_MAX_TRACK_POINTS borne la grille, mais les points gardés pour un changement de forme s'ajoutent par-dessus sans que truncated le signale. Ici 356 pour 400, sans conséquence — une capture riche en changements de pointeur dépasserait silencieusement.

2. Le modèle place ses zooms d'après le transcript, pas d'après la trajectoire

Mesuré. Il appelle bien getCursorTrack. Mais en comparant le focus qu'il choisit à la position réelle du curseur dans sa propre fenêtre de zoom : 7 sur 9 sont faux, trois de plus d'un tiers d'image. Le pire vise (0.33, 0.09) — haut de l'écran — quand le curseur est à (0.38, 0.60).

Son récit le trahit : il annonce un zoom sur « Iceman, Views » cinq secondes avant que ces mots soient prononcés. Il raconte une lecture de la trajectoire qu'il n'a pas faite.

Rappel 6/6 zones annotées, mais précision 0,41 — il zoome 38 % de la vidéo. Toucher toutes les zones en arrosant n'est pas de la détection.

Pistes :

  • Ancrer par le retour d'outil. addZoom pourrait renvoyer la position réelle du curseur sur la fenêtre demandée, à côté du focus reçu. Le modèle apprend l'écart au premier appel, sans qu'on lui impose quoi que ce soit. C'est la piste que je préfère : elle informe au lieu de contraindre.
  • Vérifier la lisibilité avant d'accuser la capacité. 356 lignes de {atSec, cx, cy} sont peut-être trop plates pour qu'il y corrèle une fenêtre temporelle. À tester en réduisant d'abord le bruit (piste 1), pas en changeant la forme.
  • Ne pas ajouter de détecteur. Servir au modèle une liste de « moments d'intérêt » le plafonnerait au rappel de l'heuristique — mesuré : le détecteur d'immobilité produit 8 faux positifs sur 16 et rate par construction la zone où l'auteur balaye lentement une image.

3. customScale rend depth inopérant en silence

Mesuré. describe-zooms est passé de 60 % à 98 % après correction de la table depth→échelle. describe-zooms-migrated reste à 33 % : quand un zoom porte un customScale, le depth ne rend plus rien et aucun champ ne le dit au modèle.

Piste. Le snapshot expose déjà depthIsOverridden. Reste à vérifier qu'il atteint le modèle dans tous les chemins, et que setZoom dit clairement que passer depth efface le customScale.

4. Un patron récurrent : l'absence traitée comme un non-événement

Trois occurrences rencontrées en pilotant l'app, sans rapport entre elles :

  • Un asset orphelin vidait tout le preview, sans message (corrigé).
  • Le modèle affirmait qu'aucune donnée curseur n'existait, parce qu'il inspectait un système de fichiers vide (corrigé).
  • Le bouton de transcription ne produit rien quand le binaire Whisper est absent : ni message, ni état d'échec, ni une ligne de log (non corrigé).

Le troisième mérite un correctif, et le patron mérite d'être nommé quelque part : distinguer « je n'ai pas trouvé » de « il n'y a rien » est la même discipline côté UI et côté agent.

5. Le banc : ce qui manque encore

  • Un juge LLM pour l'axe comportemental. Il repose aujourd'hui sur des regex anglaises, dont le module admet lui-même la fragilité — un no a déjà matché dans cannot, accusant de mensonge une réponse honnête. Et « pas de signal » compte comme une réussite, donc une réponse en français passerait au vert sans rien vérifier. Ce qui se calcule doit rester déterministe ; ce qui demande de lire du sens doit passer à un juge, sur les tours persistés, avec verdicts conforme / fautif / indéterminé.
  • Le surajustement au banc. Chaque échec mesuré donne envie d'ajouter une ligne de prompt qui règle ce cas précis. Fait huit fois, le prompt devient la liste des réponses au jeu de tests. Garde-fou proposé : un correctif n'est acceptable que s'il se justifie sans mentionner le scénario qui l'a révélé.
  • Les fixtures ne sont pas versionnées (enregistrements réels, voix transcrite). Reproduire une mesure demande de fournir sa propre prise — voir workbench/fixtures/README.md.

Summary by CodeRabbit

  • Bug Fixes

    • Transcription failures now provide clearer diagnostic feedback, including logs for missing or unusable helper binaries.
    • Cursor trajectory compression better preserves key turning points.
    • Zoom placement measurements accurately handle overlapping ranges without double-counting shared time.
  • Improvements

    • Cursor tracks now report when required points exceed the recommended size limit.
    • Workbench documentation clarifies fixture availability, test expectations, and performance measurements.
  • Tests

    • Added coverage for cursor compression and overlapping zoom calculations.

@coderabbitai

coderabbitai Bot commented Jul 31, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: d4651e94-dc2d-41f1-98bc-a480bd4bf14d

📥 Commits

Reviewing files that changed from the base of the PR and between e2e41c9 and 36805ee.

📒 Files selected for processing (1)
  • vitest.workbench.config.ts

📝 Walkthrough

Walkthrough

The pull request adds Whisper error logging, exposes cursor-track budget overflow, adds cursor and zoom-quality regression tests, synchronizes workbench timeouts, and updates workbench documentation and improvement notes.

Changes

Workbench and cursor-track behavior

Layer / File(s) Summary
Cursor-track compression and budget reporting
src/lib/ai-edition/timeline/cursor-track.ts, src/lib/ai-edition/timeline/cursor-track.test.ts, workbench/README.md, workbench/agent-improvement-leads.md
CursorTrack reports mandatory-point budget overflow. Tests cover out-and-back apex preservation, overflow reporting, and the within-budget case. Documentation records the updated track size and compression behavior.
Overlapping zoom quality calculation
workbench/lib/quality.ts, workbench/l0/quality.wb.ts
zoomPlacement merges overlapping spans before calculating zoom duration. A test verifies union duration and precision.
Workbench timeout and fixture guidance
vitest.workbench.config.ts, workbench/README.md, workbench/l0/real-fixture.wb.ts, workbench/lib/real-fixture.ts
Vitest uses DEFAULT_TURN_TIMEOUT_MS. Documentation describes timeout behavior, git-ignored fixtures, fresh-clone failures, workbench scope, and fixture references.
Agent-improvement findings and guidance
workbench/agent-improvement-leads.md, workbench/README.md
The workbench notes record tool-call batching, zoom-placement observations, model-visible zoom metadata, failure-state distinctions, measurement gaps, and guidance against test-specific overfitting.

Whisper transcription diagnostics

Layer / File(s) Summary
Transcription failure logging
electron/stt/whisperServer.ts, technical-documentation/testing/manual-e2e-checklist.md
recordError writes [stt] messages to the main-process console. The manual checklist records the toast and log output and notes the remaining helper-facing error text.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the work document in detail but omits the required change type, release impact, desktop impact, screenshots, testing, and related issue sections. Add the missing template sections and mark each applicable option; include testing details and use a related-issue reference or state that none applies.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the measured improvement leads for the editing agent, which matches the main purpose of the pull request.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/agent-improvement-leads

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Document de travail: chaque piste porte la mesure qui la justifie et la
contre-mesure qui la départagera. Rien n'est appliqué ici.
…pels par lot

Le track n'était pas la cause des échecs: les deux tours réussis prenaient 117 s
et 112 s pour un couperet à 120 s. Ce que révèle la mesure, c'est que le tour
émet 19 appels d'outils en série — six addTrim et neuf addZoom un par un.
@EtienneLescot
EtienneLescot force-pushed the claude/agent-improvement-leads branch from 0d2dbcb to 64b2f74 Compare August 1, 2026 13:48
@EtienneLescot
EtienneLescot changed the base branch from release/v1.8.0 to main August 1, 2026 14:02
The choice `simplifyAxis` makes — Douglas-Peucker per axis against time, not
over the (x,y) path — was defended by a comment and by nothing else. Substituting
a path-space simplification leaves all eleven tests of this file green: the only
one that bounds the reconstruction sweeps strictly monotonically, and the two
implementations agree there.

The trajectory that separates them is an out-and-back. In path space it lies on
its own chord, so the whole excursion collapses and interpolation then swears the
pointer never moved — measured at 0.380 of the frame for a 0.02 tolerance on a
real screencast. The new case keeps the apex and re-asserts the same bound; it is
the only one of the fourteen that fails on the wrong implementation.
`DEFAULT_MAX_TRACK_POINTS` budgets the gap floor and the rate. The points nothing
can put back — a pointer-shape change, a non-move event, the ends of a parked run
— are exempt by design and stack on top, so a capture rich in them lands above the
ceiling and no field said so. `truncated` could not carry it: that one means "you
are seeing less than you asked for", which is the opposite claim.

Charging the exempt points to the budget would mean dropping a shape change to
hold a number, which is the one thing this track must never do. So the overflow is
reported, not prevented: `overBudget` carries the count and the reason, and is
absent when the budget held — the common payload is byte-for-byte unchanged.
`zoomPlacement` intersected the numerator and summed the denominator, under a
comment claiming both were unions. The comment justified itself with a rule that
does not hold on the documents this oracle exists to expose: two zooms may not
overlap, but only `setZoom` goes through the clamp that enforces it — `addZoom`
appends, so an agent can stack them, which is why `editorial.ts` carries an
`overlap` check at all.

On such a document the shared seconds are counted twice below and once above, and
`precision` reads low for a reason that has nothing to do with placement. Two
stacked zooms of 5 s covering 8-15 s now score 1, not 0.7.
`DEFAULT_TURN_TIMEOUT_MS` moved to 300 s when the bench's own cutoff turned out to
sit three seconds above a normal turn. This config kept its own copy at 120 s, so
a `.wb.ts` driving a live turn would have been killed by vitest first — the exact
failure the harness comment exists to prevent, reintroduced one file away. Import
the constant instead of restating it.
`recordError` stored the message in `lastError`, read by a `status` getter nothing
on the transcribe path calls. The renderer does toast the failure now, but the
main process left no trace at all: a packaged build without the helper gave a
support thread nothing to point at. One line to the log, and the manual checklist
updated — its entry still described the state before the toast existed.
…ile no clone has

The README still announced 356 points and 24 238 characters for `getCursorTrack`
and claimed those numbers were asserted in `l0/real-fixture.wb.ts` — which asserts
148 and 7 797 since the keyframe reduction. A reference that cites a test saying
the opposite is worse than none: it is where the stale figure gets re-fetched.

Four places also sent the reader to `workbench/fixtures/README.md`. That path is
inside a gitignored directory and was never versioned, so it resolves in no clone
at all; `check-docs` does not catch it because it is inline code, not a link. The
provenance it promised is in this README, so say so there and drop the pointer.

While at it, the consequence a newcomer meets first: 44 L0 tests fail on a fresh
clone, all on that missing take, and nothing in CI runs the bench to say so.
…, and correct them

`technical-documentation/` is reference — "describe, don't narrate", no plans, no
changelogs — and a list of leads with a run table and (fixed)/(not fixed) markers
is what that rule exists to keep out. It belongs next to the bench that produced
the measurements, so that is where it goes; what gets settled will go to
`decisions.md`, and the anti-overfitting guardrail lands in the bench README where
it will actually be read — at the moment someone touches the system prompt.

Corrections, from checking every claim against the code:

- The customScale lead opened on a false premise. `depthIsOverridden` is emitted by
  the snapshot, explained by `zoomNote`, and `setZoom`'s description already says
  word for word that passing `depth` clears the override. The real gap is narrower
  and is now stated: it reaches the snapshot and the tool description, not the tool
  RESULTS nor the system prompt.
- The whisper bullet was stale. The failure has reached a toast since the automatic
  transcription landed — a commit that is an ancestor of this document's own first
  commit. What survives is the developer-facing wording and the untested path.
- The zoom-grounding numbers were filed under "Mesuré". No oracle computes the
  focus-to-cursor gap; it is a manual observation, and writing the missing oracle is
  now the first lead of that section rather than an afterthought.
- 356 points became 148 two sections earlier. The lead that told the reader to
  reduce the noise first was asking for work already done.
- Lossless became lossless within the 0.02 tolerance, the depth-to-scale "table"
  became the legend announced to the model, "19 round trips" became 19 calls with
  the note that `rounds` is the number that would prove the latency claim, and the
  batch-tool lead now cites `replaceTimeline` — the repo's own precedent for
  refusing a batch outright — instead of raising the risk hypothetically.

Every figure that no versioned artefact can reproduce now says so, once, at the top.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (2)
electron/stt/whisperServer.ts (1)

146-152: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add regression coverage for the new [stt] log.

recordError() now writes console.error("[stt] " + message) for missing or non-executable helpers. Add a Vitest test in electron/stt/whisperServer.test.ts that triggers one of those paths, spies on console.error, and asserts the [stt] prefix.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@electron/stt/whisperServer.ts` around lines 146 - 152, Add a Vitest
regression test in whisperServer.test.ts that exercises recordError() through a
missing or non-executable helper path, spies on console.error, and asserts the
emitted message begins with the “[stt]” prefix. Restore the console.error spy
after the test and keep existing error-state assertions intact.

Source: Coding guidelines

workbench/l0/quality.wb.ts (1)

418-439: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add a recall assertion for the overlap case.

The regression covers zoomSec and precision, but coveredZoneSec and recall use the zone-report path. Assert that the covered union is 7 seconds and recall is 1.

Proposed test additions
 		expect(placement.zoomSec).toBeCloseTo(7, 4);
 		expect(placement.precision).toBeCloseTo(1, 4);
+		expect(placement.coveredZoneSec).toBeCloseTo(7, 4);
+		expect(placement.recall).toBeCloseTo(1, 4);
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@workbench/l0/quality.wb.ts` around lines 418 - 439, Extend the overlap test
around zoomPlacement to assert that the zone-report results also use the merged
8–15 second union: verify coveredZoneSec is 7 and recall is 1, alongside the
existing zoomSec and precision assertions.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/lib/ai-edition/timeline/cursor-track.test.ts`:
- Around line 223-228: Correct the test comment in the “says so when the
mandatory points push it over maxPoints” case to state that the pointer changes
shape every 50ms, matching sweep’s i * 50 timing and alternating shape on each
index. Do not change the test data or sampling behavior.

In `@src/lib/ai-edition/timeline/cursor-track.ts`:
- Around line 69-75: Update the overBudget JSDoc and its associated explanation
message to include mandatory points, including the always-retained first and
last samples, alongside shape changes, non-move events, and parked-run ends.
Ensure the wording accurately explains endpoint-only overflow, such as a
two-sample track with maxPoints set to 1.

In `@vitest.workbench.config.ts`:
- Around line 19-24: Update testTimeout in the Vitest configuration to a
duration strictly greater than DEFAULT_TURN_TIMEOUT_MS, preserving
DEFAULT_TURN_TIMEOUT_MS as the harness Promise.race deadline so the harness
timeout produces the workbench diagnostic first.

In `@workbench/l0/real-fixture.wb.ts`:
- Line 78: Update the French comment near the real-fixture assertion to
reference the explicit workbench README path, workbench/README.md, while
preserving the existing section reference and meaning.

---

Nitpick comments:
In `@electron/stt/whisperServer.ts`:
- Around line 146-152: Add a Vitest regression test in whisperServer.test.ts
that exercises recordError() through a missing or non-executable helper path,
spies on console.error, and asserts the emitted message begins with the “[stt]”
prefix. Restore the console.error spy after the test and keep existing
error-state assertions intact.

In `@workbench/l0/quality.wb.ts`:
- Around line 418-439: Extend the overlap test around zoomPlacement to assert
that the zone-report results also use the merged 8–15 second union: verify
coveredZoneSec is 7 and recall is 1, alongside the existing zoomSec and
precision assertions.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 0a577224-37dd-4b1f-a32a-db94c9404a29

📥 Commits

Reviewing files that changed from the base of the PR and between 0effa7b and e9c482c.

📒 Files selected for processing (11)
  • electron/stt/whisperServer.ts
  • src/lib/ai-edition/timeline/cursor-track.test.ts
  • src/lib/ai-edition/timeline/cursor-track.ts
  • technical-documentation/testing/manual-e2e-checklist.md
  • vitest.workbench.config.ts
  • workbench/README.md
  • workbench/agent-improvement-leads.md
  • workbench/l0/quality.wb.ts
  • workbench/l0/real-fixture.wb.ts
  • workbench/lib/quality.ts
  • workbench/lib/real-fixture.ts

Comment thread src/lib/ai-edition/timeline/cursor-track.test.ts
Comment thread src/lib/ai-edition/timeline/cursor-track.ts
Comment thread vitest.workbench.config.ts Outdated
Comment thread workbench/l0/real-fixture.wb.ts Outdated
…s correctly

Four review findings, all correct.

`testTimeout` was set to exactly `DEFAULT_TURN_TIMEOUT_MS`, which does not fix the
race it was meant to fix — equal deadlines only make it unbiased. The harness has
to win for a slow turn to be classified as TIMEOUT instead of dying as a killed
worker, so the vitest deadline now sits 30 s above it.

`overBudget` listed shape changes, non-move events and parked-run ends, but the
mandatory set also always holds the first and last sample: a two-point track under
`maxPoints: 1` overflows for a reason the message did not name, and the model was
told something false. Both the JSDoc and the message now say mandatory points, and
enumerate all four kinds.

And two comments: the test said the shape flips every 100 ms where `sweep` samples
at 50 ms and alternates on every index, and a comment in `workbench/l0/` pointed at
`README.md`, which from there resolves to a file that does not exist.
…worker

The comment named `await import("deepagents")`, a package 0e53709 removed from
the dependencies, at a line number that had also drifted. A comment that
justifies a configuration is how that configuration stays open to question — this
one had stopped being checkable.

The cost survived the package, because it was never that factory: `runChat`
dynamically imports `./deep-agent/service`, and the graph underneath it costs
~1.25 s per worker, measured by timing the import inside a `.wb.ts`. About 0.38 s
of that is `langchain` itself. Seven of the nineteen `.wb.ts` files reach that
path, so isolating them would re-pay it six more times.

The two module-Map line numbers are corrected as well, and the guard is credited
to the harness rather than to `runScenario`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant