Measured on the deployed M3 page: .btn-primary 4.01:1 (white on --blue, 13.6px/600)
and .nav-group-label 3.14:1 (--text-3), both under the 4.5 floor.
This is R-930 arriving a second time by a different route: fixing .cta-button on
the website did not fix the CONTROLLER's button, and the shell imports it. Same
remedy the operator already chose - 1.2rem/700 puts the label in WCAG's large-text
band where 3:1 applies - so --blue stays untouched.
The sidebar's own CTA becomes an OUTLINE button rather than a filled one: blue on
transparent passes comfortably, and a 19.2px filled button does not fit a 240px
sidebar without wrapping. Measured both ways rather than guessed.
The controller has both defects on its own surface. Not fixed here - that repo is
read-only this session - so it is going to the register.
The page header and the hero both carried a title, which read as two headings
stacked. The brief's shape is one: .page-header holds the h1 - and it holds
today's indexed headline, 'Sajat felhod, sajat szabalyaid', unchanged - and the
hero row below is the lede sentence plus the button beside the dashboard window.
shell.css loads after site.css, but later-wins is per PROPERTY, not per rule.
Where site.css declares something the controller's rule does not, the website's
value survives inside the shell. Measured: exactly six declarations, three
selectors, and the first one was visibly broken on the deployed page -
.nav-links display:flex + gap:32px -> the sidebar menu rendered as a
HORIZONTAL row spilling across the content column
.lang-globe-menu top + right -> fought the controller's bottom/left
.page-header padding + text-align -> the website's centred subpage header
fought the controller's left-aligned flex row
Reset in site.css, the website's own file, so shell.css stays a verbatim copy.
Found by diffing the two files' declaration sets per selector rather than by
spotting the one that happened to be visible.
The direction the operator chose: the controller's FRAME around the website's own
content. Staged at /preview/m3/, noindex, nothing live.
website/assets/shell.css - 117 rules COPIED VERBATIM from felhom-controller
style.css @ dcd89364a4 (sidebar, mobile top bar, off-canvas drawer, and the
component atoms the restyled pages use: btn, alert, app-info-card, launch-*,
stack-detail-card, form-*). Exactly ONE rule is not verbatim and says so:
body.shell { display:flex; min-height:100vh }, which lives in the controller's
base rather than in a component, scoped to .shell so other pages are untouched.
The generator NAMES every rule it copies and REFUSES to write the file if one has
disappeared upstream. That is not ceremony: it already caught two things this
session. It refused because .sidebar::-webkit-scrollbar does not exist (only the
-track override does), and because prefers-reduced-motion is split across FIVE
separate blocks in the controller - reading only the first would have silently
lost four rules. The M1 round lost a rule exactly this way.
The bare `body { flex-direction: column }` that broke M1's phone layout is copied
this time, by name, as are body.nav-open and the no-js/js pair.
Load order is deliberate: shell.css AFTER site.css. Thirteen selectors exist in
both files with different meanings (.nav-links, .page-header, .lang-globe*, .ico)
- a horizontal site nav there, a vertical sidebar here. Later wins, and a page
that does not load shell.css is completely unaffected.
website/preview/m3/index.html - the home page in the shell. Content is /preview/'s
cleaned text; body carries `shell page-index` so the existing .page-index content
rules (caps, flow, vs, band, who-mini) are reused rather than duplicated. Six
sections, left-aligned headers, the controller's cards. Head tags (title, meta
description, canonical, hreflang) are already the FINAL values, so the switch in
part D needs no head edit.
Four changes from the operator's mockup, each with its reason in the generator:
1. the active item follows the page (the mockups show Inditopult active on all)
2. Teszteles is ALSO a primary button at the top of the lower group - it is the
one action we want and the mockup buries it at the bottom
3. the Alkalmazasok categories are anchors on /alkalmazasok: real URLs, no
JS-only state
4. ADDED GYIK. It is in today's nav; dropping it silently would take a page out
of navigation, which is not a change a restyle gets to make.
scripts/site_gates.py - gate 3 extended rather than duplicated, as asked: a SHELL
set whose pages must carry an IDENTICAL <nav class="sidebar"> after the active
marker is normalised. RED-PROOFED: a one-character change to one menu label on a
second shell page convicts. It also prints "DIFFER" rather than a reassuring
"identical" count when it fails - a false reassurance printed beside a conviction
is the exact defect this repo files rows about.
documentation/design/design-system.md - new "Shell" section: what is shared (tokens
+ shell + atoms), what each side owns, how a change flows (controller first, the
website's copy the same week), and that the cross-repo gate is deliberately NOT
built yet.
Recorded in CONTEXT.md as a dated ruling and at the top of the comparison.
Nothing deleted: /preview/, /preview/m1/ and /preview/m2/ all stay up, hidden, so
the next direction can be compared against them. Nothing built: SHARED-DESIGN.md
recommended the canonical-stylesheet-plus-gate mechanism only once a direction
exists, and none does, so it stays unbuilt.
The measured case for a mechanism rather than a convention survives the ruling and
is kept in the audit folder: a component copy between the repos silently dropped a
bare body rule and broke the phone layout in four measurable ways.
documentation/audits/mockups-2026-10-11/
COMPARE.md the three directions side by side, measured the same way:
A current preview 12 req / 270 KB
M1 dashboard 68 req / 1012 KB (front page 1 660 px)
M2 hybrid 16 req / 150 KB (phone 3 057 px, the shortest)
plus three sentences each (good / risk / cost) and the risk I
think is real: a non-technical visitor may read M1 as somebody
else's account and look for a login.
SHARED-DESIGN.md REPORT ONLY, nothing built. M1 needed 260 CSS rules: 188 (72%)
copied from the controller UNCHANGED, 8 adapted, 64 new. It used
188 of the controller's 826 rules (23%). The 18 tokens are
already identical - verified by parsing both :root blocks, zero
differing values.
Recommends (a) a canonical felhom-ui.css plus a copy-drift gate,
over (b) serving the controller's stylesheet to the website -
and recommends NOT building it until a direction is chosen.
shots/ desktop and phone captures of all three, from the deployed pages
The finding that outranks the percentage, and the reason for the recommendation: a
component-level copy silently drops the element-level rules those components depend
on. The extractor copied every .sidebar/.content/.mobile-topbar rule and missed
"@media(max-width:768px){ body { flex-direction: column } }". Everything LOOKED
copied; the phone layout was broken in four measurable ways. Invisible to a diff,
invisible to review. A second instance the same evening: the drawer is gated on a
'js' class set by an inline script that was not copied either.
Register 129 -> 129 (one closed, one opened):
R-929 CLOSED - NOT A DEFECT. Measured from the catalog: 60 templates, minus
plant-it (abandoned) and wger (hidden) = 58 offered, exactly what the prose
says; the page's 59th card is FileBrowser carrying the single "Beepitett"
badge. Set difference both ways is empty. The premise was wrong on BOTH
halves. Rule kept: before filing a count disagreement, check whether the
counts are counting the same set.
R-932 OPEN - 8 catalog strings across 5 apps carry an em dash, and they render in
the controller's app page on every real box. Found because the mockups are
the first thing to show catalog copy on a website page. Lives in a repo this
session did not have in scope, so it is a row, not a fix.
The controller's mobile layout depends on '@media(max-width:768px){ body {
flex-direction: column } }'. My extractor keys on CLASS names, so it copied every
.sidebar/.content/.mobile-topbar rule and silently dropped that one - and a bare
element rule is invisible to that kind of copy.
The symptom was not subtle once measured: the sticky topbar stayed a ROW flex item,
pushed .content 135px right, and the launcher grid fell to ONE column, making the
front page 15 597px tall on a phone and overflowing mentesek/kapcsolat to a
scrollWidth of 467 in a 375 viewport.
This is a real data point for the shared-design question, not just a mockup bug: a
component-level copy between the two repos misses the element-level rules the
components silently depend on.
Measured at 390px: the sidebar stayed an in-flow flex child and squeezed the
content column to ~208px, so the launcher grid collapsed to ONE column and the
front page became 15 597px tall on a phone; the contact page overflowed
horizontally (scrollWidth 467 in a 375 viewport).
The cause was not missing CSS - the controller's off-canvas rules were all copied.
They are gated on a 'js' class that layout.html sets in an inline head script and
the mockup shell did not. Copied that hook verbatim, so the drawer engages only
where the drawer script can open it, and the no-js path (sidebar static and full
width, burger hidden) is the controller's own.
The catalog writes the customer's domain as the literal DOMAIN and the controller
replaces it at render time, so the panels were showing 'photos.DOMAIN'. The mockup
now substitutes the same sample domain the rest of the page already uses.
Design studies only. Nothing live changes, nothing is linked from a live page, both
carry noindex and /preview/ is already Disallow-ed in robots.txt. felhom-controller
was read ONLY (baseline dcd89364a4); nothing in it was touched.
M1 website/preview/m1/ - the website wearing the controller's shell
index (Indítópult) welcome card + all 58 launcher tiles
alkalmazasok the catalog in the site's own five buckets, vanilla search
mentesek the three backup levels, every number marked "minta"
hogyan / teszteles / kapcsolat today's copy inside the dashboard shell
M2 website/preview/m2/ - short hero beside a window holding a clickable launcher
Real data, not invented:
* 58 apps from app-catalog-felhom.eu @ 8ea72aa: real names, logos, Hungarian
taglines, use cases and first steps out of each .felhom.yml app_info.
* Tile colours reproduce the controller's OWN tileColor (funcmap.go): FNV-1a 32
of the slug -> hue, 45% 38%. No brand_color exists in the catalog, so every
tile here is the colour a real launcher would paint.
* Page copy is today's live website only.
* Category labels are the five buckets alkalmazasok.html already uses. None invented.
The 58 is now measured, not quoted: 60 templates minus plant-it (abandoned) and
wger (hidden) = 58 offered, which is exactly what the site's prose says. See the
R-929 note in the audit folder.
Mockup rules honoured:
* No value pretends to be live - every sample wears a "minta" badge.
* No control does nothing: the disabled sidebar entries (Tárhely, Megosztás,
Rendszermonitor, Beállítások) say "a saját dobozodon működik" rather than
404-ing, and the contact form's submit is disabled with the same kind of hint.
The consent text is the live one, unchanged.
* App panels open with :target, so they work with JavaScript switched OFF and
their text is real HTML a crawler can read - not JS-built content.
* No login form anywhere. No external request, no AI or stock image.
CSS: website/preview/mockup.css is three labelled blocks so the shared-design
report can count them - adapted base (font URLs repointed /static -> /assets),
188 rules copied VERBATIM from the controller, and the mockup-only rules. The
burger is the website's own three-span control, copied rather than hand-rolling an
SVG: the shared sprite carries no i-menu.
scripts/site_gates.py gains a MOCKUPS set, with the reasoning in a comment. The
mockups keep every correctness check (BOM, emoji, banned strings and CDN hosts, no
<style>, cache-busting, <html lang>, clean tail, dashboard-picture language, the
picture-viewer rules) and are excluded from exactly the three chrome-consistency
checks a dashboard-shell study must fail by construction: nav/footer identical to
index.html, the NO_TWIN language globe, and analytics - the last deliberately, so a
hidden mockup cannot pollute the real stats. Delete the set with the directories.
site_gates.py green.
Every document that described the biography as a home-page section would now be
wrong, so each is corrected rather than left:
ki-all-mogotte.md gains 'Where it ended up' with the operator's reasoning, the
rejected /rolam option, and why the home page keeps a trace
AUDIT.md lever 2 carries a dated revision note (the audit itself is a
record of what was found, so it is annotated, not rewritten)
PREFLIGHT.md re-measured on the deployed page after the move:
1440: 6 780 -> 6 385 px (42% shorter than the old page)
390: 10 865 -> 10 145 px (49% shorter, 23.6 -> 12.0 screens)
sections 10 -> 9, layout families 10 -> 9, still no repeat
eyebrow allowance 4 -> 3, still 0 used
website/CHANGELOG the live change, with the layout bug that the deployed page
revealed and the markup did not
REPORT + STATUS what is live vs what is still waiting on the operator
The numbers moved because a whole section left the page; leaving the old ones in
place would have been a quiet overclaim in the other direction.
.close-cta is text-align:center, so an inline-flex row flowed onto the same line
as the CTA button and pushed the whole group off-centre. flex + max-width 34rem +
margin auto puts it under the button as a centred block, with the hairline above
it spanning the row rather than the section.
Caught by looking at the deployed page rather than trusting the markup.
site.css?v= -> 14.
The preview page is generated from this file, so fb9fe080 changed the generated
page without its source. Same change: the 'Ki all mogotte' section removed, the
compact who-mini row added to the closing call to action.
Operator's call after seeing the preview: the biography sat between the dashboard
gallery and "Hogyan mukodik?", interrupting "what it does" -> "how you get it"
with a life story. And its last sentence, "Ha irsz, en valaszolok", is a contact-
page sentence - it belongs next to "Valaszido", not mid-product-story.
/kapcsolat + /en/contact gain the full block (photo + the five approved
sentences). These are LIVE pages, so the bio and the photograph are public
from this commit. Both halves of the twin move together, as the twin rule
requires; the English promises nothing the Hungarian does not, and uses the
site's established "the box" rather than a new term.
/preview/ loses the section and gains a compact row in the closing call to
action: a 56x70 portrait, one line, and a link to the full block. This is
deliberate, not a compromise - the audit's R-11 finding was "nobody is
behind it" on the HOME page, and deleting every human trace from it would
re-open exactly that. The trace now sits where someone decides to write.
A dedicated /rolam page was considered and rejected: five sentences cannot
carry a page, and the alternative was inventing biography to fill one.
The .who* rules are now UNSCOPED (were .page-index .who*), because the block
lives on two pages and will live on the home page's twin later. Following
69035f4af5's lesson: a genuinely shared pattern goes site-wide rather than being
duplicated per page scope. No existing page uses .who, so nothing else moves.
assets/operator-portrait-small.webp - 160x200, 8.4 KB, cropped from the same
original outside the repo, metadata stripped and verified by read-back (0 EXIF
tags, 0 GPS entries, none of the phone's byte sequences present).
site.css?v= -> 13 on all 21 files.
The preview builder asserted a hardcoded "site.css?v=10" and broke the moment the
site moved to 12. It now follows whatever index.html pins and prints it.
site_gates.py green.
REPORT-redesign-2026-10-10.md (not REPORT.md: that file belongs to the kernel
night session de4a8d20 and is overwritten per session).
NEXT.md - the other pages in order with what each needs, and the traps:
teszteles/en/testing first (the destination of every CTA, newest page)
gyik/en/faq second, with the warning that gate 17 requires the FAQPage JSON-LD
to EQUAL the visible answers, so every copy edit there is a two-place edit
technologiak third ("Ipari szintu megoldasok" is the exact language the home
page just removed)
alkalmazasok is BLOCKED on R-929 - prose 58, page 59 cards, catalog 60
adatkezeles/feltetelek are reviewed legal texts; a dash sweep there needs the
operator's word separately and is not ours to take
It also records the per-page em-dash count: 300 across 19 pages, the preview
being the only page at zero.
STATUS.md - the operator's one screen, in plain language: the preview is waiting
on him, the two fixes that DID go live, and the two new decisions (R-931 the
missing compression, R-929 the app count).
website/CHANGELOG.md - the footer sweep and R-930, both with their measurements
and with the scope stated rather than implied.
Register 127 -> 129 (R-930, R-931 opened; none closed).
Two operator decisions from STOP 3, both independent of the home-page switch,
which was NOT approved and has NOT happened. index.html still serves the old
design; only the footer line and the button change on it.
1. The footer em dash, on all 21 page files (12 HU + 8 EN + the preview).
"(c) 2026 felhom.eu - Sajat felhod..." now uses the spaced en dash, which is
the correct Hungarian gondolatjel. Done as one sweep because site_gates.py
gate 3 requires every page in a language set to carry an IDENTICAL footer, so
this could never have been a one-page edit. Gate green: all 14 still match.
Scope, stated honestly: this clears the footer only. 300 em dashes remain in
the BODIES of the other 19 pages (gyik 53, en/faq 53, adatkezeles 33,
szolgaltatasok-nonpublic 27, testing 24, teszteles 23, index 17/19, ...). The
preview page is the only page at zero. The rest is NEXT.md work, page by page.
2. R-930, the operator's choice of the three offered: white on --blue measures
4.01:1, under WCAG AA's 4.5:1 for body text. .cta-button goes to 1.2rem/700,
which is >=18.66px at >=700 weight, so WCAG counts it as LARGE text and the
floor becomes 3:1 - which 4.01 clears without touching --blue. Darkening the
token was rejected because --blue is shared with the hub and controller UIs.
Horizontal padding trimmed 32->26px to protect the one-line rule for the
longest label; verified in the browser at 390px, not assumed.
.cta-button-secondary is unchanged: it already measures 7.17:1.
This reaches the primary button on 16 pages, which is the point - the defect
was never the home page's.
site.css?v= bumped to 12 on all 21 files. The pages sat at v=10 and the preview
at v=11; leaving them split would have served a returning visitor the old
stylesheet and the change would have looked like it did not deploy.
site_gates.py green.
PREFLIGHT.md - taste-skill §14 plus the Felhom additions, run in writing against
the DEPLOYED preview, Pass/Fail per line. Measured, not assumed:
page height 1440: 10 922 -> 6 780 px (38% shorter)
page height 390: 19 913 -> 10 865 px (45%, 23.6 -> 12.9 phone screens)
initial load: 13 req / 308 KB -> 11 req / 260 KB (same conditions)
fully loaded: 16 req / ~540 KB -> 15 req / ~545 KB (ESSENTIALLY UNCHANGED -
the 93 KB portrait is paid for by dropping the 49 KB hero logo
and the 31 KB mono font; stated rather than spun)
third-party hosts: 0 -> 0
em dashes in our copy: 18 -> 0
layout families: 8 for 11 sections (3 repeats) -> 10 for 10 (none)
no horizontal scroll: clientWidth == scrollWidth at 390 and at 1440
Contrast: three NEW styles measured 3.88 / 3.14 / 3.57 on the first pass, all under
WCAG AA. Fixed to --text-2 and re-measured on the deployed file: 7.56 / 6.12 / 6.96.
A measurement that lied, recorded because it nearly shipped a false verdict: the
first helper reported .limits p at 1.17:1 by treating the translucent --warn-dim
as opaque. Compositing the whole ancestor stack gives the true 5.67:1, a pass. The
three real failures in the same run were real. An instrument that can report a
wrong number silently is not a measurement.
Two rows filed, both needing something this session may not do alone:
R-930 (Business & legal, P3) the primary CTA is 4.01:1, under AA. MEASURED
IDENTICAL ON THE LIVE PAGE, so pre-existing and site-wide, not a regression
of this redesign. Three fixes offered, each touching brand, so it goes to
the operator rather than being changed unilaterally.
R-931 (Process & tooling, P4) felhom.eu serves NO compression at all - site.css
goes over the wire as 94 310 raw bytes with no Content-Encoding even when
gzip is offered. An nginx setting in manifests/, not a website change.
Worth more than anything on this page.
Register: 127 -> 129 rows, 2 opened, 0 closed. register_shape_gate.py OK.
Measured on the live preview, not assumed. --text-3 gave 3.14:1 (comparison
header), 3.57:1 (comparison rows) and 3.88:1 (hero caption) against their own
backgrounds, all under the 4.5:1 floor for body text. The audit's own rule was
that the existing 7.56:1 body contrast is an accessibility win to protect, so
dimming a column below AA to make it read as 'the quieter half' is a regression,
not a style. All three move to --text-2; the two halves of the comparison are
still told apart by the accent colour and the bold lead on the right.
Part C. The live home page is NOT touched: every CSS rule added is a new class
under .page-index, so index.html renders byte-for-byte as before.
website/preview/index.html (BOM + CRLF, noindex, Disallow: /preview/)
Ten sections, ten different layout families. The three repeats the audit found
are gone: why-grid ran 3x and apps-showcase 2x.
- hero: a real dashboard screenshot instead of the logo, sized by its content
instead of a forced 100vh band, no infinite float animation, and a sentence
made of four facts instead of "Professzionalis ... telepites es uzemeltetes".
- the ten capability cards become three labelled clusters. Their TEXT is
verbatim: the audit's own finding was that the writing is right and the
layout is wrong, so only the layout moved. Every hedge ("ha van") survives.
- NEW "Ki all mogotte" with the operator's photo and five approved sentences.
- the 3 cloud-problem cards + the 4 own-server cards merge into ONE two-column
comparison. No new claim about any competitor: all four left-hand sentences
already existed on the page.
- the two tile walls (10 + 7 tiles duplicating pages that already exist) shrink
to a two-item band that keeps the app names and the links.
- section headers are left-aligned and varied, not 11x centred-two-words-plus-
one-blue-word.
Em dashes in the page's own copy: 18 -> 0. Deliberately kept: the 6 in <title>,
meta description, OG, Twitter and JSON-LD (operator decision at STOP 1 - indexed
strings deserve their own pass), and 1 in the shared footer, which gate 3 requires
to be identical across all 14 pages. Both stated, neither swept silently.
JetBrains Mono is no longer fetched. Exactly 13 characters were loading 31 KB:
the 1..5 process badges, the 01..05 service numbers, and - the audit missed this
at first and it is corrected in the file - the 3/2/1 of the backup rule.
assets/operator-portrait.webp 720x900, 93 KB
Cropped from an original that stays OUTSIDE this public repo. All metadata
stripped and VERIFIED BY READ-BACK: 0 EXIF tags, 0 GPS IFD entries, and none of
EXIF/XMP/ICCP/samsung/SM-A705FN/2023:10:18 appear anywhere in the bytes. Built by
re-writing the pixels into a fresh image, so the source info dict cannot travel.
Honest correction to the brief's premise: this photo had NO GPS tag to begin with.
scripts/site_gates.py
preview/index.html registered in NO_TWIN, with the reason. It cannot go in TWINS:
gate 3 wants its nav identical to index.html's (globe -> / and /en/) while the
twin check wants a pair's globe pointing at the pair's own URLs. The English twin
is written in the same commit as the switch, as a real twin. Everything else still
runs on the preview - BOM, emoji, nav/footer, globe, analytics, CDN, tokens,
cache-busting, dash-language, viewer, tail.
site_gates.py green. The builder (kept in the session scratchpad) writes BOM + CRLF
and asserts both: it first wrote LF and turned the nav/footer gate red for the
preview, which is exactly the trap this CHANGELOG's morning entry records.
Part 0 and Part A of the home-page redesign. Nothing under website/ is touched.
skills/
- design-taste-frontend installed to ~/.claude/skills/ as a VERBATIM copy of
github.com/Leonxlnx/taste-skill @ 717446e07a (2026-10-09, MIT). Body verified
byte-identical line by line; the only addition is a provenance block. The repo
holds scripts (skill.sh, scripts/*.mjs) but the SKILL FOLDER holds only SKILL.md,
so no script was copied and none was run. Not installed via npx or skill.sh.
- skills/felhom-web-design/SKILL.md is the override layer and names every conflict:
the static-HTML stack fence (no React/Tailwind/Motion/npm), nothing fetched from
another server (kills taste-skill's picsum/Unsplash/SimpleIcons ladder and its
npm icon libraries, since we have a self-hosted sprite), real images only, the
Hungarian gondolatjel surviving the en-dash ban, the claim rule, and the SEO-keep
list. 137 lines, check_skills.py PASS.
- SOURCES.md records the provenance and the kept/overridden table.
documentation/audits/redesign-2026-10-10/AUDIT.md
Measured against the LIVE site, not the file. 12 patterns to retire (R1-R12), each
naming the taste-skill rule it breaks; 7 to keep; dial reading 3/2/4 -> 6/3/4;
8 levers in priority order for the operator to approve at STOP 1.
Headline measurements: 10 922 px tall at 1440 and 19 913 px at 390 (23.6 phone
screens); 11 sections but only 8 layout families, why-grid used 3x and
apps-showcase 2x; 18 em dashes; 16 requests / 533 KB from exactly two hosts.
Contrast measures 7.56:1 body and 16.67:1 headings - AAA, and an accessibility win
to preserve rather than a thing to fix.
Method note: resize_window does not move window.innerWidth on this workstation and
DevTools device mode could not be driven, so the page was rendered in a same-origin
iframe at each width. Faithful, with two positive observables: the hamburger
computes to display:block at 390, and clientWidth == scrollWidth == 375.
Gates: site, hostinstall, hub-confirm, register and the rest green. instructions and
script-tests fail on this Windows workstation for environment reasons that predate
this change (E:\git\CLAUDE.md is the deliberate Windows adaptation the gate wants
identical; fcntl missing; relpath across C:/E:; the cp1250 console trap). Re-run on
DooPlex follows.
Records what your screenshots turned up: the per-page scoping that left the
tester pages unstyled, the site-wide missing base link rule (which also
affected gyik/technologiak/biztonsagimentes), the sitemap BOM, and the
correction that nobody is testing today so all five places are open.
'Ha most nincs szabad hely' read oddly directly after saying every place is
free. It now talks about what happens when the test fills up, which is the
case the sentence is actually for.
Root cause of "no formatting for the points": .info-card and .highlight-box are
scoped per page (.page-technologiak / .page-biztonsagimentes) and the new pages
carry .page-teszteles, so NONE of the card chrome applied - the sections were
unstyled text runs. The tester pages now join the technologiak scope, whose h3
is a flex row (these headings carry an icon).
Three more gaps the new pages exposed, all fixed site-wide rather than patched:
- There was no base `a` rule at all, so an inline link in body copy fell back
to the browser default and changed colour once visited. .legal was the only
place this had been fixed. /gyik, /technologiak and /biztonsagimentes each
carry such links and had the same bug.
- `code` was styled only under .legal.
- A <ul> inside an info-card was unstyled: wrong colour, wrong indent.
Justified body copy on the tester pages, as asked. NOTE: no other page
justifies today, so this is deliberately scoped to .page-teszteles and can be
rolled out site-wide or dropped in one edit. hyphens:auto, because Hungarian
sets badly when justified without it.
site.css -> v=10 on all 20 pages.
sitemap.xml: BOM removed. Search Console shows the freshly submitted clean
sitemap as "Sikertelen lekeres" with an empty last-read, and a BOM before the
XML declaration is a known irritant for its parser. The file is otherwise valid
(18 <url>, parses, served 200 as application/xml). No gate covers sitemap.xml
and the BOM rule is for website/*.html only.
Facts corrected - there are NO testers (operator, this evening): "Tester 1" is a
disposable virtual box and "Tester 2" is a friend whose machine is unassembled
and switched off. The page said "Nehany hely mar betoltve", which was untrue; it
now says the test is starting and all 5 places are open. Same correction in
OUTREACH.md (five people needed, not three), CONTEXT.md, and the terms-1.1 mail,
which in practice has one recipient, not two. No scheduled post claimed a
filled place, so none needed editing.
Both credential attempts against the Gitea jobs endpoint returned 401 and the
store holds no Gitea API key, so no green is claimed. The positive observable
is the DooPlex run of the same gate entry point at this HEAD: 19/19 green.
OUTREACH.md: a personal message, a Facebook group post with a shorter variant,
a forum post, and a table of 15 places with the rules quoted where they are
published. Nothing was posted and no group was joined; the group data was read
from each group's public About page on 2026-10-10.
The rules that matter, measured rather than assumed: Magyar Linux Felhasznalok
(11970, public) has no advertising ban and Felhom is on topic; DIY Smart Home
forbids ads and names an e-mail route; HUP forbids unlicensed advertising; the
prohardver family forbids "tagok, vasarlok gyujtese ... felhivas" without the
operator's permission, which is literally what recruiting testers is.
R-929 (P4): the website's prose says 58 apps, the apps page renders 59 cards,
the catalog holds 60 template directories. Gate 15 cannot catch it because it
compares the two language sets to each other, never to the catalog. Not fixed
here - it needs the catalog skill's exclusion rules. The campaign posts say 58,
matching every prose claim on the site.
COPY.md section 7: five Hungarian posts for Tue/Thu 19:00 over three weeks, each
with a source comment per claim and one link. The Monday post (2026-10-12, 6.2)
is untouched and no slot shares its day.
fb_probe.py gains --link (default unchanged). Every scheduled post used to
attach the home page's preview card whatever its text said, so a post about
/teszteles would have sent the people who clicked its card to the home page.
build_parser() split out of main() so a test can parse argv. Red-proved: both
new tests fail with "Namespace object has no attribute link" without the flag.
Search Console audit (seo-2026-10-10/not-indexed.md): all seven not-indexed
URLs with a verdict each - four expected by design, two waiting on Google's
crawl queue, one fault on our side. That one is /api/contact, the contact
form's POST endpoint crawled as a page; robots.txt now disallows /api/. The
performance baseline (0 clicks, 47 impressions, position 73, nine queries) is
recorded there to compare against in a month.
New page pair /teszteles + /en/testing: who the closed test is for, what a
tester needs (machine + internet quoted verbatim from the FAQ, plus a domain),
what they get (terms section 3, nothing new), what we ask for, and how many
places there are. Every "Jelentkezem tesztelonek" button now goes there.
Operator rulings 2026-10-10, recorded in CONTEXT.md:
D1 - the tester's domain is in the TESTER'S OWN name; Felhom pays its fee for
two years and the tester keeps it when the test ends or they leave; DNS
is managed with a token scoped to that one zone only.
D2 - the closed test runs with at most 5 households.
Terms 1.1 adds section 4 (the domain) with the D1 text; old sections 4-11 are
renumbered 5-12 and nothing else changed. Section 11 promises the testers an
e-mail before a change, so one is drafted for the operator to send.
Registered in site_gates.py TWINS/URL and listed in sitemap.xml.
Needs attention, Waiting for you (one card per kernel/Docker/Proxmox set the button may approve), a
7-column Boxes table whose rows open to every old value, and a closed Details (release ids, cancelled
approvals, ring-0 counts, floors, crash guard and root files, Approve now - which now asks first).
Same data, buttons, routes and CSRF field. No deploy. Screenshots in audits/hub-system-page-2026-10-10/.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012qRErfCoiTkvDK9N5XHbzb
A session that needs the swap-0 venue for checklist 5.1 had to find it with
pct list, and a brief this week placed it on the wrong host. Now recorded where
such facts live: which host, why swap is 0, that it is normally stopped, what it
keeps in /opt/upg between runs, and that its images are not pruned.
1631 (catalog b7f0f7c), 1632 (felhom.eu 970616b7) and 1634 (felhom.eu 29c9cb1e):
every step failure with 0 s after 10.5-14 minutes in set-up, logs absent. A job
never picked up would end at an arbitrary second; the same second-of-the-hour
twice two hours apart points at a periodic sweep or a fixed deadline. Between
them the runner worked — catalog job 1633 succeeded normally on the same tree,
and a shallow clone with no sibling runs the catalog gates green by hand, so the
code is not what is red. The 2026-10-12 due-check's premise is false and the row
now says so.
The operator was asked at the end, as the brief said, and ruled: keep Monica
out. R-927 opened and closed in the same session, with the measurements and the
sentence worth keeping — an app is not added because a gap exists, it is added
because the app can be kept. Register 137 -> 138: 2 opened, 1 closed.
Also in here: the two small things seen and not filed (remove leaves hold-logs
in the stack directory, with no secret in it; the catalog clone's pre-push hook
is unarmed, so the gates were run by hand before the commit and by CI after).
The catalogue side is app-catalog-felhom.eu b7f0f7c. Here: the evidence, the
website, the register and the operator's view.
Website
- Two cards in the Otthon & Eletmod / Home & Lifestyle section of BOTH apps
pages, with assets: grocy-logo.svg is grocy's own icon with its single fill
made white like the other logos, lubelogger-logo.png is the app's own icon
with its dark background dropped and the mark made white (the rule
SparkyFitness's PNG follows), and six screenshots of each app's own UI with a
household's own data, taken headless on the bench from the published template.
- The app count moved 56 -> 58 in 15 places per language set, both languages,
and the open-source tile 49 -> 51. Checked by asking the same patterns for the
new number afterwards. marketing/facebook/COPY.md still says 56 and is NOT
changed: the post it carries is already scheduled.
Evidence
- documentation/audits/new-apps-2026-10-10/ — FIT.md (checklist group 0 for all
three, with the Hungarian-UI column), the bench and box transcripts, the
memory samples, the screenshots and the gate runs.
Register: 137 -> 139 rows, 2 opened, 0 closed.
- R-926 after a remove-keeping-backups and restore, nobody has checked what the
app page shows for an after_install app's generated password. Measured here:
LubeLogger is safe (its login is derived from the environment at every start,
the new password signs in, the data is back); grocy's install password still
signs in from the restored database but the deployed environment no longer
carries ADMIN_PASSWORD at all. Seven apps are in the class.
- R-927 Monica, stopped at checklist 0.2 with the measurements, waiting on the
operator.
Gate scripts: the same console trap in nineteen of them and in repo_gates.py
itself, where it ABORTED THE WHOLE RUNNER at the first gate — printing a
non-ASCII character on this workstation's cp1250 console raised
UnicodeEncodeError before the gate had decided anything, and reuse_refs_check.py
died while printing a NOTE. All now reconfigure their own streams. Red-proof
that the remaining script-test failures are not mine: test_due_checks_gate.py
fails the same 5 of 42 with the change reverted.
scripts/test_repo_gates_docstring.py pins the numbered list in repo_gates.py's
docstring against the GATES table IN ORDER. I listed poster-facts as 17 but
inserted it before decoy-coverage, so the sets matched and the order did not:
'same set, different order'. Caught on DooPlex, where script-tests runs (it
needs fcntl and cannot run on Windows).
Moved the table entry after decoy-coverage rather than renumbering the
docstring: the gate genuinely belongs last, since it only reports.
PART A -- the poster. documentation/architecture/felhom-system-poster.html
(307 KB). Secret scan first: ZERO IPv4, zero PEM blocks, zero ssh keys, zero
Bearer. The one EAA... match is base64 inside an embedded "mime":"font/woff2"
blob, not a Facebook token. "token"/"secret"/"password" appear 11 times and
every one is a NAME ("6. ep0 read token", "the hub seal key"); the poster
itself says "Names only; no secret values". All five long base64 blobs are
declared assets: 1 image/png, 3 text/javascript, 1 font/woff2.
It renders with NO network: the source mentions cdn.jsdelivr.net and Google
Fonts, but the loaded requests are only the HTML plus blob:/data: URLs -- the
bundler inlined everything. Measured, not assumed, and it matters: this is a
disaster-recovery document, so needing the internet to draw would be a defect.
No console errors.
The operator's three Claude Design fixes are all present: (a) no "WG" badge,
WireGuard only for the tunnel, no badge on the ep0-copy tile; (b) the box ->
ep0 arrow reads "encrypted on the box, sent through WireGuard"; (c) "Known
gaps" holds two items and NOT the household-keys sentence, which is now a
neutral "By design" note under the ep0 household namespace.
ONE FACT ON IT WAS WRONG. The felhom.eu tile said "served from DooPlex through
Cloudflare". It is not: Cloudflare is DNS only and the traffic goes direct --
measured this morning for the privacy notice, which states exactly that. The
poster would have contradicted a published page. Fixed in place (a label):
"served from DooPlex, Cloudflare DNS only". The first wording overflowed the
fixed-size tile, so it was shortened to fit and the evidence lives in the
facts file instead -- checked by re-rendering, not by hoping.
PART B -- the facts and the rule. DESIGN-PROMPT-...md is renamed
felhom-system-poster.facts.md (one home per fact), with the three fixes folded
in as explicit instructions so a regeneration cannot undo them, plus a new
"Badges" section saying a "WG" chip must never come back.
New rule, section 6 "The system poster stays true", added IDENTICALLY to all
five copies of unprompted-work.md (the four repos and the workspace root on
DooPlex; verified identical by diff before and after) and to
PROMPT-TEMPLATE.md's end-of-session checklist as a FIFTH coupled artifact.
scripts/poster_facts_gate.py WARNS when the facts file has a newer commit than
the poster. It never fails a push, deliberately: a refresh needs Claude Design
and the operator, --no-verify is forbidden here, so a blocking gate would leave
deleting it as the only way out. It compares COMMIT times, not mtimes, because
a checkout rewrites mtimes and every fresh clone would shout.
RED-PROOF -- and it found a real bug in the gate. The first run warned
correctly but exited 1: a single non-ASCII character in its own warning raised
UnicodeEncodeError on this cp1250 console. A gate whose entire contract is
"never fails a push" was failing pushes. Fixed (ASCII output + an encode
guard), and the decoy now asserts BOTH the warning and exit 0. Three branches
proven: facts newer -> warns, rc 0; poster newer -> quiet, rc 0; poster
missing -> "could not tell", rc 2, not a false all-clear.
The decoy itself was seen to fail, twice, on Linux (the suite needs fcntl and
cannot run on Windows): breaking the warning gives STALE_WARNS=False, and
making it exit 1 gives RC_STALE=1. All 80 felhom.eu decoys behave.
PART C -- do box reports pass through Cloudflare? NO. Two channels. DNS from
PUBLIC resolvers (not DooPlex's own, which answers the LAN address):
hub.felhom.eu is a CNAME to dooplex.hopto.org -> 37.191.56.193, not a
Cloudflare address, and no cf-ray comes back. The manifest: an ordinary k3s
Ingress, Cloudflare named only in a DNS setup comment. THE CONTROL that makes
the negative mean something: iso.felhom.eu resolves to 172.67.x / 104.21.x,
real Cloudflare addresses -- so the method does detect proxying.
So nothing is added to the Cloudflare row: the hub path does not touch it.
06-offsite-connectivity.md section 1 claimed the public edge is a
Cloudflare-Tunnel and "DooPlex has no public IP" -- both untrue today. Kept
and marked STALE with the measurement rather than rewritten, because that
paragraph is the reason ep0 exists and the argument needs its premise visible.
total-loss-of-dooplex.md's "today a CNAME to dooplex.hopto.org" is confirmed
correct.
Register: 137 before, 137 after, 0 opened, 0 closed -- every finding here was
small and fixed in the session.