Defect: an ADDED block was colored via `render(wordDiffByAuthor("", raw, ...))`, which
wrapped the whole raw markdown in an inline `<ins>` BEFORE markdown-it parsed it — so
`## X` rendered as literal text, not `<h2>`. Same structural bug for list, blockquote,
and thematic-break blocks.
Fix (three-part, pure / vscode-free):
1. `blockContentStart(raw)`: new helper returning the offset past block-level markers
(ATX headings `## `, unordered/ordered list markers, blockquotes). Open sentinels must
not precede these markers or markdown-it cannot recognise the block construct.
2. `injectSentinels`: clamp the open-sentinel lower-bound to `blockContentStart(raw)`,
so for `## New Section` the sentinel lands AFTER `## ` (at position 3) instead of at
position 0, letting the heading parse correctly.
3. `sentinelsToSpans` / `colorByAuthor`: add optional `kind: "by" | "ins" = "by"`
parameter so the sentinel path can emit `cw-ins-{author}` for added blocks while all
existing `colorByAuthor`/`cw-by-*` call-sites are unchanged.
4. `renderReview` ADDED block path: `render(wordDiffByAuthor("", raw, ...))` →
`colorByAuthor(raw, blk.start, landedSpans, render, "ins")`. The CHANGED non-atomic
prose path (`wordDiffByAuthor(before, after, ...)`) is untouched — it correctly keeps
the block markers at column 0 of the unchanged prefix.
5. `renderReviewOp`: removed the now-vestigial `colored` parameter (was always `render`
after the added-block path moved to `changedHtml`); unchanged blocks now call
`render(op.block.raw)` directly.
Regression test added: "renderReview: an added heading block renders as a heading AND
is author-colored as an insertion" — RED on old code, GREEN after fix. All 259 tests
pass; typecheck clean.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Surface Claude live output/progress during the asking-Claude status: notification activity line + token count, a Cowriting: Claude OutputChannel streaming assistant text, and a cancellable turn. Pure turnProgress reducer + runEditTurn onProgress/AbortSignal (vscode-free) + both call sites. INV-43..47.
Fixes#60
`tsc -p tsconfig.e2e.json` emits to `out/` but never removes outputs for test
sources absent on the current branch, so stale compiled
`out/test/e2e/suite/*.test.js` from other branches were picked up and run by the
suite glob (`**/*.test.js`) — this caused real cross-branch test confusion in
session 0048 (a deleted probe + another branch's tests ran on an unrelated branch).
Add a `clean:e2e` script (`fs.rmSync('out/test', {recursive, force})` via node, so
no shell `rm` dependency) and run it between `build` and `tsc` in `pretest:e2e`.
Cleans ONLY `out/test` — never `out/`, which holds the just-built esbuild bundle
(`out/extension.cjs`, `out/media`).
Verified: planting a stale `out/test/.../zz.test.js` then running `pretest:e2e`
removes it, and the E2E suite stays green (73 passing + 1 pending, both passes
exit 0).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`executeCommand("undo")` is non-functional in some headless `.vscode-test`
instances (it does not restore the buffer — see #54's diagnosis), which
false-fails every undo-dependent host E2E and turns `main`'s E2E red there even
though the product code is fine.
Add a RUNTIME preflight probe (`undoCapable.ts`): edit a scratch buffer, undo,
and report whether the buffer was actually restored (memoized per run). The #38
undoMarks suite gates on it via `suiteSetup` — running normally where undo works
(full coverage preserved, real regressions still caught) and skipping with a LOUD
console warning where it doesn't (no false red, no silent loss — the skip is
logged and shows as `pending`).
This fixes the "main E2E red" symptom. The underlying headless-undo limitation is
documented in #54; #40's undo-behavior coverage runs wherever undo works.
Test-infra only — no product code changed. Verified: with undo broken locally the
#38 suite skips (1 pending) and both E2E passes exit 0.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The F9/F10 author-coloring technique injects paired PUA sentinels into prose
source at span offsets, renders, then maps sentinels → cw-by-* spans. Two failure
modes when a span boundary met emphasis markup (characterized in session 0032):
- CASE1: a boundary strictly inside a delimiter run (a**b**c, between the two *)
split `**`, breaking markdown-it's delimiter pairing (stray <em></em>, raw **).
- CASE3: a boundary inside an emphasis run (**bold** span covering **bo) rendered
the emphasis but MISNESTED span/element (<strong>bo</span>ld</strong>).
Token-aware fix (both, per the issue #33 comment):
- injectSentinels: clamp any sentinel offset that lands strictly inside a
delimiter run (* _ ~ `) to the run's start, so a sentinel never splits a run
(CASE1). Delimiters are invisible once rendered, so this only shifts the colored
boundary across markup. Drop spans that clamp to empty.
- sentinelsToSpans: replace the naive global split/join with a walker over the
rendered HTML that emits the cw-by-* span only around TEXT runs — closing it
before any <tag> and reopening after — so a span is always well-nested within
inline elements (one span segment per text run, CASE3). Tags are copied
verbatim with any stray sentinel stripped (no Private-Use-Area char leaks).
Pure, vscode-free, deterministic (INV-33). No regression: existing colorByAuthor /
renderReview cases stay green; the common no-emphasis case is byte-identical.
222 unit + 74/5 host E2E green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A pinned baseline with no changes since should read as a clean starting point,
but the F10 on-render still author-colored every block (colorByAuthor), painting
the whole document green/blue right after a pin. Now, when the baseline reason is
"pinned" and there are zero changes since (every diffBlocks op unchanged), the
on-render is fully clean — no change marks and no authorship coloring — while the
data-src block mapping (INV-36) and any pending proposals (review actions, not
annotations) are kept.
Scoped to the PIN specifically, not all zero-diff: a baseline advanced by a
machine-landing (accept) is also zero-diff but keeps its authorship coloring so
accepted Claude text stays blue (F10 INV-33). renderReview takes a `pinned`
option; the controller passes baseline.reason === "pinned" from both refresh and
the renderHtmlFor test seam.
- trackChangesModel.ts: renderReview gains the `pinned` RenderOption; when pinned
+ zero-diff, blocks render plain (no colorByAuthor).
- trackChangesPreview.ts: pass { pinned } through refresh + renderHtmlFor.
- unit: pinned+zero-diff → no cw-by-*; pinned+zero-diff still shows proposals;
zero-diff WITHOUT pin (machine-landing) keeps coloring (INV-33); real changes
after a pin re-color.
- s48PinClean host E2E: type → colored; pin → clean; edit → annotations return.
218 unit + 74/5 host E2E green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>