Merge remote-tracking branch 'origin/main' into refactor/task-19-verify-repoint

This commit is contained in:
Marcos Paulo
2026-09-05 23:35:21 +00:00
7 changed files with 150 additions and 4 deletions
@@ -82,3 +82,44 @@ read the legacy files, so deleting `responsive.css` and breaking every mobile
layout passes it cleanly. Before you claim done, diff the **built** CSS
(`dist/_astro/*.css`) against `main` and enumerate every declaration that
disappeared. Each one is either present elsewhere or a regression.
## Attempt 1 stopped short — and the goal as written is not reachable yet
Preserved as `rejected/15e-attempt-1` (`d2312d7`). Start by reading that diff:
it ported rules into `ChapterHero`, `FleetDiagram`, `RouteTable`, `WorktreeMap`,
`LanguageToggle`, `ReadingProgress`, `agents.astro`, `full-guide.astro` and
`tokens.css`, and it added `pixelmatch`, `pngjs` and `postcss` plus a working
screenshot-compare harness. Reuse all of it. It never finished the comparison
loop and never committed on its own.
It also deleted `responsive.css` while **`full-guide/index.html` still links
it**. That page is the live site until task 20 cuts over; deleting the
stylesheet strips it. `src/components/islands/GuideSelector.astro` still
references it too.
So the "delete it" checkbox in this brief cannot be honestly ticked before the
cutover. Do not tick it, and do not delete the file. **The deliverable is
everything up to the deletion:**
- Every rule `responsive.css` provides to an Astro route lives with the
component that needs it, or in `tokens.css`.
- No `.astro` file and no Astro layout links `responsive.css` any more.
- `full-guide/index.html` still links it, unchanged, and still renders exactly
as it does today.
- A list, in this file, of the rules that remain in `responsive.css` solely for
the legacy page — that list is the deletion checklist task 20 will execute.
Amend the "Done when" boxes to match before you start, and say in your final
report that the file is intentionally still present.
### One rule to port properly while you are in there
`.route-meter span` animates `height: var(--score)` — a layout property, which
task 18 was meant to eliminate. 18 changed it in `responsive.css` and that
change was reverted (`0bca43c`) because it belongs in the component, not in a
legacy file. When you port this rule, port it as
`transform: scaleY(var(--score)); transform-origin: bottom` with `height: 100%`.
`--score` is set inline as a percentage (`--score:92%`) by `GuideSelector.astro`
and `full-guide.astro`; a percentage in `scaleY()` is valid and was verified
rendering correctly at `matrix(1, 0, 0, 0.92, 0, 0)`. Leave the legacy
`responsive.css` copy of the rule alone.
@@ -108,3 +108,59 @@ to remove one, that is the escalation path, not the workaround.
`.agents/scripts/visual-regression.mjs` runs. It used to throw for everyone,
which is why no task in this plan ever produced the captures its brief asked
for.
## Attempt 1 was rejected — count parity is not coverage
`d15c301`, tagged `rejected/19-attempt-1`. It did good work: `verify.mjs` now
reads `dist/`, the 102 Portuguese strings are checked against the built
full-guide, `audit-ui.mjs` gained per-page CSS-variable resolution and a
built-CSS baseline (`.agents/snapshots/built-css-values.json`). Keep all of
that.
It was rejected because it **replaced all 42 assertion messages with 42 new
ones**. Not one of the originals survives. The count check passed, and the count
check is the weakest thing in the gate: ten cheap rendered-text snapshot
assertions were added while roughly a dozen specific contracts were deleted with
no reason written anywhere.
Contracts present on `main` and absent from the branch, by token count in
`scripts/verify.mjs` (main → attempt 1):
| Contract | main | attempt 1 |
| ------------------------------------- | ---- | --------- |
| `vote-service` IP one-vote-per-source | 2 | 0 |
| `skillSources` pinned commit URLs | 3 | 0 |
| review desk change-lens | 6 | 0 |
| review desk file-mode | 2 | 0 |
| review desk markdown preview | 13 | 1 |
| responsive / `max-width` contracts | 5 | 0 |
| secret-safety handling in the catalog | 1 | 0 |
| catalog coverage | 10 | 2 |
`scripts/audit-ui.mjs` also went from 6 assertions to 5.
A rendered-text snapshot does not replace these. "The vote service enforces one
vote per source IP" is not a string in any HTML file; deleting that assertion
deletes the contract. Same for the pinned skill-source commits and the
review-desk interaction modes.
### What attempt 2 must produce
1. **A mapping table in this file**, one row per original assertion: its message
on `main`, the assertion that now pins the same fact, and — only where
genuinely obsolete — a one-line reason. Build it from
`git show rejected/19-attempt-1^:scripts/verify.mjs | grep -oP 'throw new Error\(\s*\K.*'`
so no row is missed. 42 rows, no gaps.
2. **Every fact still pinned.** Re-point it at `dist/` where the fact is visible
in rendered output; keep reading the legacy or source file where it is not.
`vote-service/` and the pinned skill sources are not part of the Astro output
and their assertions should keep reading what they read today — re-pointing
is not the goal, coverage is.
3. **Raise the baseline.** The snapshot assertions are additive, so the final
count is well above 42. Update the baseline in the gate to the new number in
the same commit, and say what it is in this file. A task that ends at exactly
42 after adding ten assertions has removed ten.
4. Keep `audit-ui.mjs` at 6 or more.
Start from `rejected/19-attempt-1` rather than from scratch — the `dist/`
re-point and the Portuguese coverage check are worth keeping.
+2 -1
View File
@@ -137,7 +137,8 @@ const isDiff = mode === 'diff';
.skill-diff {
border: 1px solid var(--ink);
color: var(--paper);
animation: lens-enter 0.28s ease both;
/* purpose: change lens reveal (state change) */
animation: lens-enter 200ms cubic-bezier(0.2, 0, 0, 1) both;
}
.change-lens {
+23
View File
@@ -139,4 +139,27 @@ const share = (count: number) => (total ? Math.round((count / total) * 100) : 0)
flex-direction: column;
}
}
.vote-buttons button b {
display: inline-block;
}
:global(.is-updating) {
/* purpose: vote widget tally update (feedback) */
animation: tally-pop 200ms cubic-bezier(0.2, 0, 0, 1) both;
}
@keyframes tally-pop {
from {
opacity: 0;
transform: translateY(-4px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@media (prefers-reduced-motion: reduce) {
:global(.is-updating) {
animation: none;
}
}
</style>
+25 -1
View File
@@ -99,7 +99,11 @@ const { rootSelector, data } = Astro.props;
function replace(target, markup) {
const panel = root.querySelector(target);
if (panel) panel.innerHTML = markup;
if (!panel) return;
panel.innerHTML = markup;
panel.classList.remove('is-swapping');
void panel.offsetWidth;
panel.classList.add('is-swapping');
}
// Names deliberately match legacy functions until task 19 updates its checks.
@@ -291,4 +295,24 @@ const { rootSelector, data } = Astro.props;
outline: 3px solid var(--gold);
outline-offset: -3px;
}
:global(.is-swapping > *) {
/* purpose: tab panel change in the guide (state change) */
animation: guide-swap 200ms cubic-bezier(0.2, 0, 0, 1) both;
}
@keyframes guide-swap {
from {
opacity: 0.15;
transform: translateY(5px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
@media (prefers-reduced-motion: reduce) {
:global(.is-swapping > *) {
animation: none;
}
}
</style>
+1 -1
View File
@@ -42,7 +42,7 @@ const { target = '.reading-progress span' } = Astro.props;
function update() {
const height = document.documentElement.scrollHeight - window.innerHeight;
span.style.width = (height > 0 ? (window.scrollY / height) * 100 : 0) + '%';
span.style.transform = `scaleX(${height > 0 ? window.scrollY / height : 0})`;
}
// `{ passive: true }` is non-negotiable. The legacy app.js binds
@@ -235,7 +235,8 @@ const packageFiles: PackageFile[] = [
}
.package-preview.is-swapping {
animation: package-preview-in 0.34s ease both;
/* purpose: review-desk detail swap on selection (state change) */
animation: package-preview-in 200ms cubic-bezier(0.2, 0, 0, 1) both;
}
@keyframes package-preview-in {