Ledger · worktree-dark-selection
<task-notification> <task-id>ac737cf6b95c2c010</task-id> <tool-use-id>toolu_01Ms5ShPjqrTmN7xe2nRzkx8</tool-use-id> <output-file>/home/paulca/.cache/tmp/claude-1001/-home-paulca-amadan-net-oficina-sheets-core/e2cc9163-61af-4161-828a-b77ea1500a39/tasks/ac737cf6b95c2c010.output</output-file> <status>completed</status> <summary>Agent "Review dark selection fix" finished</summary> <note>A task-notification fires each time this agent stops with no live background children of its own. The user can send it another message and resume it, so the same task-id may notify more than once.</note> <result>Reviewed. Findings ranked, most severe first. I changed nothing. Root: `/home/paulca/amadan.net/carlos/sheets/.claude/worktrees/dark-selection` ## Verdict on the two direct questions **Q1 — cascade.** Every new rule wins. I hand-counted each against both stylesheets and there is no dead rule among the additions: | new rule | spec | strongest competitor | spec | |---|---|---|---| | `…> tr > td.highlight-top` (sheets.css:187) | (0,3,3) | vendor `.jss_worksheet .highlight-top.highlight-left` :310 | (0,3,0) | | same, vs our own grid line (sheets.css:114) | (0,3,3) | (0,2,3) | | | `…> tr.selected > td:first-child` (:139) | (0,4,3) | vendor :198 **and** our own header rule :123, both (0,3,3) | | | `[data-sheet-grid] .jss_corner` (:224) | (0,2,0) | vendor `.jss_corner` :340 | (0,1,0) | The corner is reachable: `jspreadsheet.js` does `e.content.appendChild(e.corner)` and `grid.js:126` mounts into `[data-sheet-grid]`. `box-shadow: none` also beats the `-webkit-box-shadow` alias in that vendor rule. And `jspreadsheet.css:75` is `border-collapse: separate`, so the 1px dashed/dotted edges are not eaten by a neighbour's solid border under collapsed-border conflict resolution — I checked that specifically because it would have been silent. **Q4 — contrast.** `--rst-accent` on `--rst-accent-soft` = **6.59:1 dark** (`#7ba7ff` on `#17233a`), **4.75:1 light** (`#2464e0` on `#eef3fe`). Both correct, both match `theme.css:59` and `theme.css:87`. One other number is wrong — see finding 3. --- ## Findings **1. `.selection*` is mislabelled. It is the drag-fill preview, not "the range a formula is pointing at."** `sheets.css:169-172` and `sheets.css:213-214`, repeated in `vendorpaint_test.go` and the commit message. In `jspreadsheet.js` the `selection`/`selection-top`/`-bottom`/`-left`/`-right` classes are added by `updateCopySelection`, which runs off the fill-handle drag. And `grid.js:127` sets `parseFormulas: false`, so formula-range highlighting does not exist in this app at all. Failure scenario: someone drags the fill handle down five rows. The source range is `--rst-accent-soft` + solid accent; the preview is now also `--rst-accent-soft` + dotted accent. The two merge into one accent slab whose only boundary is 1px of dotted-vs-solid in an identical hue. That is parity with the vendored look (both were `rgba(0,0,0,0.05)`), so it is not a regression — but the comment tells the next person this rule is formula chrome, and they will look for the fill-preview rule and not find one. **2. The guard test goes green with the bug reintroduced.** `vendorpaint_test.go:99` is `strings.Contains(ours, ".highlight-right")`. `sheets.css:150` contains, inside the explanatory comment, the literal text `.highlight-right/-bottom border: 1px solid #000`. Proof — I deleted `sheets.css:201` (the `.highlight-right` selector line) on a copy and re-ran both guards' substring checks: all pass, and the only surviving match for `.highlight-right` is line 150, the comment. The selection's right edge then falls back to the vendor's `border-right: 1px solid #000` — invisible on `#111418`, which is the reported bug. `.highlight-top` has the same hole via lines 151 and 157, but is rescued by the second test. Fix: strip `/* … */` before matching, or match the full scoped selector (`> tbody > tr > td.highlight-right`) the way the second test does. **3. The row-header contrast figure names the wrong token and understates the problem.** Commit message, `sheets.css:136-137` and `vendorpaint_test.go:31-32` all say "`--rst-text-muted` at 1.9:1". Computed from `theme.css:157`: dark `--rst-text-muted` `#a8b0ba` on `#dcdcdc` = **1.60:1**. 1.91:1 is `--rst-text-faint` (`#98a1ac`). The rule that actually set the colour is `sheets.css:125` (`color: var(--rst-text-muted)`), so 1.60 is the real number — the bug was worse than the commit claims. **4. The new AGENTS.md bullet claims an enforcement it does not have, and the repo violates it on landing.** `AGENTS.md:145-153`: "every one the app can render must be restated in `sheets.css`. Enforced by `TestVendorSelectionChromeIsRestated`." That test covers 15 selection selectors. Vendored literals the app renders today and does not restate: - `internal/sheets/static/vendor/jspreadsheet.css:66-72` — `.jss_content::-webkit-scrollbar-track { background:#eee }`, `-thumb { background:#666 }`. `.jss_content` is the grid's own scroll container inside `[data-sheet-grid]`. In Chromium dark mode, the grid's scrollbar is light grey. - `internal/sheets/static/vendor/jsuites.css:978-1120` — the entire `.jcontextmenu` block. `grid.js` sets no `contextMenu`, and `jspreadsheet.js` binds `defaultContextMenu` to the `contextmenu` event, so right-click on any cell opens a jsuites menu painted in literals. Also, the bullet says "the vendored stylesheet" singular; `sheet.html:2-3` links two. Either narrow the bullet to selection chrome (matching the test's name) or add these to the exclusion list. `AGENTS.md:139` — "Rules here are enforced by tests, not by memory" — is the standard this misses. **5. `selectionChrome`'s docstring overclaims relative to the list.** `vendorpaint_test.go:15-17` says "every vendored rule that paints the SELECTION in a hard-coded colour and that this app can actually render". Missing, all selection chrome with literals: - `jspreadsheet.css:288` `.jss_worksheet .highlight { rgba(0,0,0,0.05) }` — restated at `sheets.css:174`, unguarded. - `jspreadsheet.css:320` `.jss_worksheet .selection { rgba(0,0,0,0.05) }` — restated at `sheets.css:215` **by this very commit**, and no guard entry was added for it. - `jspreadsheet.css:111` `.jss_worksheet > thead > tr > td.selected { #dcdcdc }` — restated at `sheets.css:138`, unguarded; only the tbody half is listed. Delete the thead half of that selector and every test stays green. - `jspreadsheet.css:270` `.jss_worksheet > tbody > tr > td.highlight > a { color: blue }` — neither restated nor named in the exclusion list. Whichever it is, it needs a line, by the commit's own standard. **6. `TestSelectionOutlineOutranksTheGridLine` does not pin specificity — it pins a string.** `vendorpaint_test.go:107-146`. Its own comment says "pins the specificity, which is the half of the bug that is invisible in a diff." If `sheets.css:114` ever gains one step — `td:not(.jss_freezed)`, an extra class, a `:where()` used wrong — it becomes (0,4,3), the outline rules stay at (0,3,3), and the original bug returns with both tests green. Name that limit in the comment, or compute specificity properly. **7. Vendor CSS loads *after* sheets.css, so ties go to the library — and the new AGENTS.md rationale omits this.** `layout.html:21` links `sheets.css`; `layout.html:24` renders `{{block "head"}}`, and `sheet.html:2-3` links `jsuites.css` and `jspreadsheet.css` there. So the vendored sheets are later in source order. The bullet at `AGENTS.md:150-153` justifies the scoped selector only by the grid-line rule's extra step, but even an *equal*-specificity restatement loses: `[data-sheet-grid] .jss_worksheet .highlight-top` is (0,3,0), tying `jspreadsheet.css:310` `.jss_worksheet .highlight-top.highlight-left` (0,3,0), and the vendor's `box-shadow` would win on order. This is also the real reason the vendored `#dcdcdc` beat our generic header rule at `sheets.css:123` — both are (0,3,3), and the vendor came last. The commit tells only the dead-selector half of that story. Cheapest durable fix: move the vendor `<link>`s above `sheets.css`, or write "strictly higher, because vendor CSS loads last" into the bullet. **8. `sheets.css` breaks the invariant it states three paragraphs earlier.** `sheets.css:101-106`: "Every rule that sets a background also sets a color… A background without a foreground beside it is the bug, not the colour." `sheets.css:183-185` sets `background-color: var(--rst-bg)` with no `color`. It happens to render fine (it inherits `--rst-text` from `sheets.css:176`, 15.32:1), but the file's own stated rule is now false in the file. **9. Source-order collision between the anchor and the fill preview.** `sheets.css:183` (`.highlight-selected` → `--rst-bg`) and `sheets.css:215` (`.selection` → `--rst-accent-soft`) are both (0,3,3), and 215 is later. With `.selection` correctly understood as the drag-fill preview (finding 1), a cell that is both the anchor and inside the preview loses its cursor. Put the anchor rule last, or give it one more step. **10. `td[data-contrast="low"]` is the same dead-rule shape this commit is written against.** `sheets.css:246-257`. `grep -rn data-contrast` across `.go`, `.js` and `.html` returns *only* `sheets.css`. Fills are imported and stored (`store.Format.FillHex`), contrast-checked (`internal/sheets/contrast.go:88-91`) and surfaced as the banner at `layout.html:70-85`, but no fill is ever painted into a cell. Pre-existing, not introduced here — but it is why the fill-interaction question is moot today. Forward risk worth writing down: when fills do land, if they are applied by class or attribute rather than an inline `style`, `sheets.css:183` at (0,3,3) will paint `--rst-bg` over a filled anchor cell's fill, and `sheets.css:174` will do the same across the whole range. **11. Nothing stops a restatement being a literal.** `csstokens_test.go` only checks that every `var(--rst-…)` used is defined. `.highlight-right { border-right-color: #7ba7ff }` passes all three guards and is wrong in light mode. **12. Process.** `AGENTS.md` Shipping cadence 2 says branches are cut from current `main`; this is cut from `core` and there is no local `main` in the worktree — confirm the stack is deliberate. §Review also requires the reviewing model recorded in the branch discussion, not just the PR body. --- ## Verified clean - All fifteen new/changed selectors take effect; no new dead rule. - `copying-*` is added *on top of* `highlight-*` (`jspreadsheet.js`), both sit in the same declaration blocks, and the vendor's `dashed` style survives the colour-only override — the marquee works both while it overlaps the selection and after the selection moves away. - `.readonly` / `.jss_freezed` / `.jss_pagination` exclusions are accurate: `grid.js` contains no `readOnly`, `freeze`, `pagination` or `editable`, and `sheet.html` does no role gating, so no read-only grid exists today. - `[data-cell-table]` is untouched and unreachable from every new selector (all scoped under `[data-sheet-grid]`). - `vendorpaint_test.go` is 146 lines, under the cap; `go vet` and `gofmt` clean; `go test ./internal/sheets/... ./internal/guardstest/` green. The two that would block me merging are **2** (the gate does not gate) and **3** (a wrong number stated three times in the permanent record). **1** and **4** are documentation that will mislead the next person.</result> <usage><subagent_tokens>118150</subagent_tokens><tool_uses>47</tool_uses><duration_ms>591214</duration_ms></usage> </task-notification>
merge and deploy
Why does docs have a random URL https://docs.bds.oncarlos.com/d/y44jfsq2g6 but sheets has an integer URL? https://sheets.bds.oncarlos.com/sheets/1
/handoff