website globe: report, live check (desktop JS on/off, 376 px menu), R-903 (no-JS phone menu); 130 -> 131
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_012qRErfCoiTkvDK9N5XHbzb
This commit is contained in:
@@ -0,0 +1,63 @@
|
||||
# REPORT — the website's language switch becomes a globe (2026-10-08)
|
||||
|
||||
> `REPORT-website-globe.md`, not `REPORT.md`: another session worked in this repo today (commit c7aade40).
|
||||
|
||||
| Part | What | State |
|
||||
|---|---|---|
|
||||
| A | The globe on every page, CSS, no JavaScript | **Done, live** (803af479) |
|
||||
| B | Gates 3 and 13 read the globe from one `LANGS` list; leftover text switch fails; 5 decoys | **Done** |
|
||||
| C | CHANGELOGs, `.claude/rules/website.md`, `10-localisation.md` ruling 11 + §10.7, STATUS | **Done** |
|
||||
|
||||
Register: before **130**, after **131**, opened **1** (R-903), closed **0**.
|
||||
|
||||
## What was built
|
||||
|
||||
- Every page's nav: `<details class="lang-globe">` with the sprite's `i-globe` (`aria-hidden`), `aria-label`/`title`
|
||||
„Nyelv" / „Language", a list Magyar, English (same order everywhere). The page's own language points at the page
|
||||
itself, is bold with a blue rule and carries `aria-current="true"`; the other item points at the twin.
|
||||
- `site.css`: globe rules for desktop (list below the icon, right-aligned) and for the ≤900 px menu (last item, list
|
||||
opens in place). Tokens only. `?v=2` → `?v=3` on all 16 pages. **There was no `.lang-switch` rule to retire** — the
|
||||
text switch had no CSS.
|
||||
- **No script added.** The controller's globe has no close-on-outside-click or Escape script either (searched
|
||||
`lang-globe` in its templates and CSS: markup + CSS only).
|
||||
- The current item is marked by weight and a rule, not by the controller's „✓" (`content: "\2713"`): U+2713 is in
|
||||
the site's emoji gate range.
|
||||
- `404.html` and the hidden prices page carry the globe too (their English item points at `/en/`): gate 3 requires one
|
||||
nav across the Hungarian set, and gate 13 checks their globe's structure (not targets — they have no twin).
|
||||
|
||||
## Decoys (mutation on `website/en/apps.html` → failure line; `documentation/audits/website-globe-2026-10-08/decoys.txt`)
|
||||
|
||||
| Decoy | Failure line |
|
||||
|---|---|
|
||||
| Magyar item → `/` | `en/apps.html: the globe's hu item points at '/' — its twin is '/alkalmazasok'` |
|
||||
| Magyar item also `current` + `aria-current` | `en/apps.html: the globe marks 'hu' as current (class current=True, aria-current=True) — only the page's own language 'en' is` |
|
||||
| Magyar item removed | `en/apps.html: the globe lists [('en', 'English')] — every page lists exactly [('hu', 'Magyar'), ('en', 'English')], in that order` |
|
||||
| items swapped | `en/apps.html: the globe lists [('en', 'English'), ('hu', 'Magyar')] — every page lists exactly …` |
|
||||
| a text switch added back | `en/apps.html: a leftover text language switch (class="lang-switch") — the globe replaced it` (also gate 3: `<nav> differs from en/index.html`) |
|
||||
|
||||
The decoy suite (`scripts/test_gate_decoys.py`) runs all five with `must=` on these lines: all rejected. The old
|
||||
`site/switch-wrong-twin` decoy was retired with the switch.
|
||||
|
||||
## Live check (headless Chrome on scratch guest 9202, after git-sync; `check.txt` + six screenshots)
|
||||
|
||||
- `/` and `/en/`, 1280 px, JavaScript on and off: the globe is closed at load, opens on a click; Magyar → `/`
|
||||
(lang hu), English → `/en/` (lang en); the current one has `aria-current="true"`; no sideways scroll.
|
||||
- 376 px, JavaScript on, inside the opened three-line menu: the list opens in place, both items visible, both go to the
|
||||
right page, no sideways scroll.
|
||||
- **376 px, JavaScript off: not possible** — the three-line menu itself is scripted, so it does not open. This is
|
||||
older than the globe. Filed as **R-903** (P4).
|
||||
|
||||
## Files and pushes
|
||||
|
||||
- **803af479**: `website/*.html` (9), `website/en/*.html` (7), `website/assets/site.css`, `website/CHANGELOG.md`,
|
||||
`scripts/site_gates.py`, `scripts/test_gate_decoys.py`, `scripts/CHANGELOG.md`, `.claude/rules/website.md`,
|
||||
`documentation/architecture/10-localisation.md`, `STATUS.md`, `documentation/audits/website-globe-2026-10-08/`
|
||||
(decoys.txt, globe.py). **CI: job 1538, success.**
|
||||
- This report, the live evidence and R-903: the next commit (named in the chat).
|
||||
- Earlier, `67b3fd23` (website refresh): CI job 1535 failed with no log and every step failing in the same second
|
||||
(R-887's shape); `663421dd` on top passed (job 1536).
|
||||
|
||||
## Instruction-file edits
|
||||
|
||||
`.claude/rules/website.md`: one bullet added — the switch is a globe; a new language = one `LANGS` entry + its twins.
|
||||
No rule was loosened.
|
||||
@@ -0,0 +1,38 @@
|
||||
const puppeteer = require('puppeteer');
|
||||
const sleep = ms => new Promise(r => setTimeout(r, ms));
|
||||
async function items(p) {
|
||||
return p.$$eval('.lang-globe-item', as => as.map(a => ({text: a.textContent, href: a.getAttribute('href'), cur: a.getAttribute('aria-current'),
|
||||
vis: a.offsetParent !== null, r: (b => [Math.round(b.left), Math.round(b.right)])(a.getBoundingClientRect())})));
|
||||
}
|
||||
(async () => {
|
||||
const b = await puppeteer.launch({args: ['--no-sandbox']});
|
||||
for (const js of [true, false]) {
|
||||
for (const [w, h] of [[1280, 800], [376, 812]]) {
|
||||
for (const [u, tag] of [['https://felhom.eu/', 'hu'], ['https://felhom.eu/en/', 'en']]) {
|
||||
const p = await b.newPage();
|
||||
await p.setJavaScriptEnabled(js);
|
||||
await p.setViewport({width: w, height: h});
|
||||
await p.goto(u, {waitUntil: 'networkidle2', timeout: 60000});
|
||||
const name = `${tag}-${w}-${js ? 'js' : 'nojs'}`;
|
||||
const closed = await p.$eval('details.lang-globe', d => d.open);
|
||||
if (w < 900) {
|
||||
if (!js) { console.log(name, 'phone menu needs JavaScript (the three-line button is scripted); skipped'); await p.close(); continue; }
|
||||
await p.click('#hamburger'); await sleep(500);
|
||||
}
|
||||
await p.click('summary.lang-globe-btn'); await sleep(300);
|
||||
const open = await p.$eval('details.lang-globe', d => d.open);
|
||||
const it = await items(p);
|
||||
const scroll = await p.evaluate(() => [window.innerWidth, document.documentElement.scrollWidth]);
|
||||
await p.screenshot({path: `/out/${name}.png`});
|
||||
console.log(name, JSON.stringify({openBefore: closed, openAfterClick: open, items: it, viewport_scroll: scroll}));
|
||||
for (const i of it) {
|
||||
const q = await b.newPage(); await q.setJavaScriptEnabled(js);
|
||||
const r = await q.goto(new URL(i.href, u).href, {waitUntil: 'domcontentloaded'});
|
||||
console.log(' ', i.text, '->', q.url(), r.status(), await q.$eval('html', e => e.lang)); await q.close();
|
||||
}
|
||||
await p.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
await b.close();
|
||||
})().catch(e => { console.error(e); process.exit(1); });
|
||||
@@ -0,0 +1,20 @@
|
||||
hu-1280-js {"openBefore":false,"openAfterClick":true,"items":[{"text":"Magyar","href":"/","cur":"true","vis":true,"r":[1043,1161]},{"text":"English","href":"/en/","cur":null,"vis":true,"r":[1043,1161]}],"viewport_scroll":[1280,1280]}
|
||||
Magyar -> https://felhom.eu/ 200 hu
|
||||
English -> https://felhom.eu/en/ 200 en
|
||||
en-1280-js {"openBefore":false,"openAfterClick":true,"items":[{"text":"Magyar","href":"/","cur":null,"vis":true,"r":[1043,1161]},{"text":"English","href":"/en/","cur":"true","vis":true,"r":[1043,1161]}],"viewport_scroll":[1280,1280]}
|
||||
Magyar -> https://felhom.eu/ 304 hu
|
||||
English -> https://felhom.eu/en/ 200 en
|
||||
hu-376-js {"openBefore":false,"openAfterClick":true,"items":[{"text":"Magyar","href":"/","cur":"true","vis":true,"r":[129,344]},{"text":"English","href":"/en/","cur":null,"vis":true,"r":[129,344]}],"viewport_scroll":[376,376]}
|
||||
Magyar -> https://felhom.eu/ 200 hu
|
||||
English -> https://felhom.eu/en/ 304 en
|
||||
en-376-js {"openBefore":false,"openAfterClick":true,"items":[{"text":"Magyar","href":"/","cur":null,"vis":true,"r":[129,344]},{"text":"English","href":"/en/","cur":"true","vis":true,"r":[129,344]}],"viewport_scroll":[376,376]}
|
||||
Magyar -> https://felhom.eu/ 304 hu
|
||||
English -> https://felhom.eu/en/ 304 en
|
||||
hu-1280-nojs {"openBefore":false,"openAfterClick":true,"items":[{"text":"Magyar","href":"/","cur":"true","vis":true,"r":[1043,1161]},{"text":"English","href":"/en/","cur":null,"vis":true,"r":[1043,1161]}],"viewport_scroll":[1280,1280]}
|
||||
Magyar -> https://felhom.eu/ 200 hu
|
||||
English -> https://felhom.eu/en/ 200 en
|
||||
en-1280-nojs {"openBefore":false,"openAfterClick":true,"items":[{"text":"Magyar","href":"/","cur":null,"vis":true,"r":[1043,1161]},{"text":"English","href":"/en/","cur":"true","vis":true,"r":[1043,1161]}],"viewport_scroll":[1280,1280]}
|
||||
Magyar -> https://felhom.eu/ 304 hu
|
||||
English -> https://felhom.eu/en/ 304 en
|
||||
hu-376-nojs phone menu needs JavaScript (the three-line button is scripted); skipped
|
||||
en-376-nojs phone menu needs JavaScript (the three-line button is scripted); skipped
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 96 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 96 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 26 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 96 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 96 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 28 KiB |
@@ -272,7 +272,7 @@ stopping line that lies.
|
||||
| **R-89** | Business & legal | P4 | Retention as a per-customer **commercial** policy on the hub | READY (increment 2) | — | Policy object + reconciler → ep0 prune job; keep box tokens write-only | CC |
|
||||
| **R-794** | Business & legal | P4 | **[P3-LOW] redis 7.4 (RSALv2 / SSPL, not OSI) runs as a private cache in seven apps: dawarich, docmost, immich, nextcloud, outline, paperless-ngx, romm.** READ 2026-10-02 (`audits/licences-2026-10-02/TABLE.md`). Read as permitted (a private cache only its app uses is not Redis offered as a service — inferred). Valkey (BSD-3) or redis 8 (AGPL option) removes the question. **Needs:** a ladder step per app to valkey or redis 8, through the harness — no hurry. | **READY — rank P3-LOW; owner: CC** **Re-ranked 2026-10-03: P3→P4: the row itself says no hurry; usage read as permitted.** | — | — | CC |
|
||||
|
||||
## Process & tooling — 20 rows (P3 3, P4 17)
|
||||
## Process & tooling — 21 rows (P3 3, P4 18)
|
||||
|
||||
| ID | Category | Sev | What | State | Blocked on | Next action | Owner |
|
||||
|---|---|---|---|---|---|---|---|
|
||||
@@ -296,6 +296,7 @@ stopping line that lies.
|
||||
| **R-786** | Process & tooling | P4 | **[P3-LOW] SparkyFitness's onboarding record has six open rows** (`app-catalog-felhom.eu/onboarding/sparkyfitness.md`): 0.5 runtime internet (food search providers), 0.7 the phone app's sign-in route through traefik, 1.6 the env names the server reads, 1.7 the entrypoint read, 5.4 a second memory watch at another limit, 8.3 no logo/screenshots on felhom.eu (404). Everything else measured this session (bench + 9202). **Needs:** each row measured, or n/a with a reason. | **READY — rank P3-LOW; owner: CC (catalog)** **Re-ranked 2026-10-03: P3→P4: onboarding record completeness; no household meets it directly.** | — | — | CC |
|
||||
| **R-807** | Process & tooling | P4 | **[P3-LOW] 13 apps stay UNDETERMINED because their seeds write only to the database, so their upload / media / cache / redis volumes stay empty.** MEASURED 2026-10-02 (`audits/persistence-sweep-2026-10-02/A/sweep/TABLE.md`): claper, crafty-controller, dawarich (redis), docmost, gramps-web, immich (ML cache), outline (+redis), sparkyfitness, tandoor, vikunja, wger, wishlist, zipline — plus plex (no seed route: a plex.tv claim token) and wanderer (no fixture; unhealthy under the gate). None is BROKEN: nothing was written outside a preserved folder. **Needs:** per app, a seed that uploads one file (or the volume named n/a with a reason: a redis cache, an ML model cache). | **READY — rank P3-LOW; owner: CC** **Re-ranked 2026-10-03: P3→P4: test coverage; nothing was found broken.** | — | — | CC |
|
||||
| **R-896** | Process & tooling | P4 | **The catalog's `volume-persistence` gate runs its canary and the apps on the LOCAL Docker, so a full gate run on DooPlex acts on the production host — and there its self-test fails for every app.** SEEN 2026-10-06 night: a helper's full `catalog_gates.py` run on DooPlex built `felhom-volgate-canary:1` and ran the canary pair locally (`scripts/check-volume-persistence.py` canary self-test, ~line 830); it reported the self-test failing for every app, also on `main` (checked with gokapi). Not re-run (it would act on DooPlex again). Same class as R-650 (tests that reach real Docker act on DooPlex). | **OPEN — filed 2026-10-06 night** | — | Point the gate at the bench guest (as the upgrade harness does) or refuse to run on a host without a marker; then find why the self-test fails | CC |
|
||||
| **R-903** | Process & tooling | P4 | **On a phone with JavaScript off, the website's menu does not open — so its links and the language globe cannot be reached there.** MEASURED 2026-10-08 (globe brief, headless Chrome at 376 px, JavaScript off): the three-line button is scripted (`#hamburger` toggles `.nav-links.is-open`), and below 900 px the nav list sits off-canvas until then. Older than the globe — the text switch had the same limit. The globe itself works with JavaScript off at desktop width (`audits/website-globe-2026-10-08/check.txt`). Fix direction: a CSS-only menu toggle (a `<details>` or a checkbox), a nav change on every page plus gate 3. | **OPEN — owner: CC** | — | Build the no-JS menu in a website task | CC |
|
||||
|
||||
<!-- DUE-CHECKS-BEGIN — machine-readable. Parsed by scripts/due_checks_gate.py.
|
||||
One row per dated check. The R-number must have a row above. Dates are UTC.
|
||||
|
||||
Reference in New Issue
Block a user