Website: the machine estimator (/gepigeny, /en/sizing), live but unlisted; SIZING.md §5 in three verdicts; R-997 filed
gates / gates (push) Successful in 6m42s

A household ticks apps and sees, for an 8, 16 and 32 GB box: fits well / fits, but
tight / does not fit, the totals, and the apps that burst on CPU. One rule in
assets/sizing.js, every number from assets/sizing-data.json (built by
scripts/build_sizing_web.py from the 2026-10-13 N100 measurement; test: 7 cases,
red-proofed). Unlisted: noindex, robots Disallow, not in the sitemap, linked from
nowhere — site_gates.py gate 25 enforces it (UNLISTED twin pairs keep every twin
check). SIZING.md §5 corrected: the 8 GB line contradicted its own x1.5 rule; guest
sizes 4.0/11.4/27.4 GB with the measured 612 MiB MemTotal shortfall. R-993 gains the
fact that the installer accepts a host below any minimum.
This commit is contained in:
2026-10-13 14:31:44 +02:00
parent fdca4971af
commit d5ef93b13b
14 changed files with 1813 additions and 15 deletions
+44 -6
View File
@@ -109,6 +109,15 @@ URL = {"index.html": "/", "alkalmazasok.html": "/alkalmazasok", "technologiak.ht
# English twin is written then, as a twin.
NO_TWIN = {"szolgaltatasok-nonpublic.html", "404.html", "adatkezeles.html", "feltetelek.html"}
# UNLISTED (2026-10-13, the machine estimator) — a twin pair that is LIVE but deliberately unlisted until the
# operator says "list it" (R-997). It gets every twin check (hreflang, globe, canonical, lang, no Hungarian on the
# English page), and gate 25 adds what unlisted means: `noindex` on both pages, a `Disallow:` line for each URL in
# robots.txt, absent from sitemap.xml, and linked from no other page. To LIST it: move the pair into TWINS, drop
# noindex and the Disallow lines, add both URLs to the sitemap and a link from the menu — in one commit.
UNLISTED = {"gepigeny.html": os.path.join("en", "sizing.html")}
URL.update({"gepigeny.html": "/gepigeny", os.path.join("en", "sizing.html"): "/en/sizing"})
ALL_TWINS = dict(TWINS, **UNLISTED)
MOCKUPS = set() # the design studies were deleted with website/preview/ at the M3 switch
# SHELL (2026-10-11, M3) — every public page now wears the controller's frame instead of the
@@ -138,10 +147,12 @@ SHELL = {
os.path.join("en", 'testing.html'),
os.path.join("en", 'contact.html'),
os.path.join("en", 'download.html'),
'gepigeny.html',
os.path.join("en", 'sizing.html'),
}
PAGES = list(TWINS) + list(TWINS.values()) + sorted(NO_TWIN) + sorted(MOCKUPS) + sorted(SHELL)
EN_PAGES = set(TWINS.values())
PAGES = list(ALL_TWINS) + list(ALL_TWINS.values()) + sorted(NO_TWIN) + sorted(MOCKUPS) + sorted(SHELL)
EN_PAGES = set(ALL_TWINS.values())
HU_PAGES = set(PAGES) - EN_PAGES - MOCKUPS - SHELL
ANALYTICS_EXEMPT = {"szolgaltatasok-nonpublic.html"} | MOCKUPS
@@ -425,7 +436,7 @@ def check_globe(p, lang, urls=None):
ALT_RE = re.compile(r'<link rel="alternate" hreflang="([^"]+)" href="([^"]+)">')
for hu, en in TWINS.items():
for hu, en in ALL_TWINS.items():
if hu not in pages or en not in pages:
fail("twin pair %s <-> %s: a page of the pair is missing" % (hu, en))
continue
@@ -438,7 +449,7 @@ for hu, en in TWINS.items():
canon = re.search(r'<link rel="canonical" href="([^"]+)">', pages[p])
if not canon or canon.group(1) != SITE + URL[p]:
fail("%s: canonical %r — it must point at the page itself (%s)" % (p, canon and canon.group(1), SITE + URL[p]))
print(" twins: %d pairs checked both ways (hreflang, globe, canonical)" % len(TWINS))
print(" twins: %d pairs checked both ways (hreflang, globe, canonical), %d of them unlisted" % (len(ALL_TWINS), len(UNLISTED)))
for p in sorted(NO_TWIN): # no twin: the globe's structure, current mark and label still hold
if p in pages:
check_globe(p, lang_of(p))
@@ -546,7 +557,7 @@ for p in sorted(x for x in pages if is_en(x)):
if BEG.search(t):
fail("%s: the site does not beg — no \"please\"/\"kindly\": %r" % (p, t[:120]))
# the retrieval promise: the English page may make it no more often than its Hungarian twin does
hu = next((h for h, e in TWINS.items() if e == p), None)
hu = next((h for h, e in ALL_TWINS.items() if e == p), None)
if hu and hu in pages:
en_n = sum(len(re.findall(rx, t, re.I)) for t in visible_strings(s) for rx in _RET_EN)
hu_n = sum(fold(t).count(fold(st)) for t in visible_strings(pages[hu]) for st in _RET_HU)
@@ -817,7 +828,8 @@ if not os.path.exists(_SITEMAP):
else:
_sm = io.open(_SITEMAP, encoding="utf-8-sig").read()
_locs = [re.sub(r"^https?://felhom\.eu", "", u) for u in re.findall(r"<loc>([^<]+)</loc>", _sm)]
_want = set(URL.values()) | _LEGAL_URLS
_unlisted_urls = {URL[_p] for _pair in UNLISTED.items() for _p in _pair}
_want = (set(URL.values()) - _unlisted_urls) | _LEGAL_URLS
_have = set(_locs)
for _u in sorted(_want - _have):
fail("sitemap.xml: %s is a public page and is NOT listed — search engines are told the site "
@@ -844,6 +856,32 @@ else:
if not [f for f in fails if "sitemap.xml" in f]:
print(" sitemap: %d URL(s), every public page once, every twin pair cross-named" % len(_locs))
# ---------------------------------------------------------------------------------------------
# gate 25 (2026-10-13): an UNLISTED pair stays unlisted until the operator lists it (R-997). Live and reachable by its
# link, but: noindex on the page, Disallow in robots.txt, absent from sitemap.xml, and no other page links to it —
# each of the four is what "unlisted" means, and losing any one of them quietly publishes the page.
_ROBOTS = os.path.join(W, "robots.txt")
_robots = io.open(_ROBOTS, encoding="utf-8-sig").read() if os.path.exists(_ROBOTS) else ""
_sm_locs = set(re.sub(r"^https?://felhom\.eu", "", u) for u in re.findall(r"<loc>([^<]+)</loc>", io.open(_SITEMAP, encoding="utf-8-sig").read())) if os.path.exists(_SITEMAP) else set()
_unl_pages = {p for _pair in UNLISTED.items() for p in _pair}
for _p in sorted(_unl_pages):
_s = pages.get(_p, "")
_u = URL[_p]
_m = re.search(r'<meta name="robots" content="([^"]*)">', _s)
if not _m or "noindex" not in [x.strip() for x in _m.group(1).split(",")]:
fail("%s: an UNLISTED page without <meta name=\"robots\" content=\"noindex\"> — search engines may show it" % _p)
if not re.search(r"(?m)^Disallow:\s*%s\s*$" % re.escape(_u), _robots):
fail("robots.txt: no 'Disallow: %s' — the UNLISTED page %s is open to crawlers" % (_u, _p))
if _u in _sm_locs:
fail("sitemap.xml: %s is listed — an UNLISTED page must not be in the sitemap" % _u)
_link = re.compile(r'href="(?:https?://felhom\.eu)?%s(?:[#?"])' % re.escape(_u))
for _q in sorted(pages):
if _q in _unl_pages:
continue # the pair's own globe, canonical and alternates point at each other
if _link.search(pages[_q]):
fail("%s links to the UNLISTED page %s — it is published by that link" % (_q, _u))
print(" unlisted: %d page(s) — noindex, robots Disallow, not in the sitemap, linked from no other page" % len(_unl_pages))
if fails:
print("\nSITE GATES FAILED: %d problem(s)" % len(fails))
sys.exit(1)