C4/D11 follow-up - the integration review's seven confirmed findings
An adversarial review (four lenses, every finding independently verified by two skeptics told to refute it) ran over2a5f6b3and8cf8c0f. Seven findings survived; all seven are fixed here. Against the C4 fix: - help.js: the nav hover was renamed onto its own surface token, keeping a no-op T9.9 had introduced (two different grays had been mapped to one name). Hover is now --cds-layer-hover, the token that exists for exactly this. - wp-creation-app.js: the drawer's critical CSS pre-painted --cds-layer-accent while the stylesheet paints --wp-nav-bg; now both paint --wp-nav-bg. Against D11: - wp-sections.js: the Assets toggle note still described the pre-D11 card ('Asset tags and controls.dev links') with a rationale the picker inverts. - runAssetSearch: the result cap counted contains-matches before the exact and prefix tiers finished, so 500 alphabetically-early substring hits could evict the exact match - and Enter then added the wrong asset, ID-locked. The cap now bounds each tier; the scan always sees the whole catalog. - addCatalogAsset: the one mutation in the section with no announced outcome was the successful pick. It now toasts (role=status), matching every sibling path (C1). - assets_db.py: failures are remembered for FAIL_CACHE_SECONDS (default 30s) and a stale catalog is served over an error, so a Micron outage costs one CONNECT_TIMEOUT per window instead of one per page load stacking up in the shared sync threadpool until login itself stalls. - assets_db.py: MICRON_ASSETS_CACHE_SECONDS='5m' no longer crashes the boot - a malformed knob on an OPTIONAL feature degrades to its default, loudly. assets_check grows four regressions for these (27 -> 31): per-tier cap against 600 decoys, the announced pick, boot with a malformed knob, and the stable cached 503. Battery: assets_check 31/31, color_check 5/5, sections_check ALL PASS. Items: C4, D11. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -149,6 +149,10 @@ def main():
|
||||
page.eval("(() => { const r=[...document.querySelectorAll('#asset-results button.asset-result')];"
|
||||
" return r.length===2 && r.every(b=>!b.disabled); })()"))
|
||||
page.eval("addCatalogAsset(0)")
|
||||
chk("the pick is announced (role=status toast) - a keyboard pick is otherwise silent",
|
||||
page.eval("(() => { const t=document.getElementById('toast');"
|
||||
" return !!t && t.getAttribute('role')==='status'"
|
||||
" && /Added PUMP-01/.test(t.textContent); })()"))
|
||||
chk("picking adds a catalog row: locked ID (no input), badge, source:'catalog'",
|
||||
page.eval("pkgAssets.length") == 1
|
||||
and page.eval("pkgAssets[0].source") == "catalog"
|
||||
@@ -195,6 +199,20 @@ def main():
|
||||
page.eval("(() => { const b=document.querySelector('#asset-results button');"
|
||||
" return !b.disabled && /add/.test(b.textContent); })()"))
|
||||
|
||||
# The tier-cap regression (review finding, fixed same day): 600
|
||||
# alphabetically-early contains-matches must not evict a prefix match
|
||||
# that sorts after every one of them. Before the fix the scan broke at
|
||||
# a COMBINED 500 and Enter added the wrong asset, ID-locked.
|
||||
got = json.loads(page.eval(
|
||||
"(() => { const c=[];"
|
||||
" for(let i=0;i<600;i++) c.push('A'+String(i).padStart(4,'0')+'-PMP-10');"
|
||||
" c.push('PMP-10-EXTRA');"
|
||||
" assetCatalog=c; assetCatalogIndex=new Map(c.map(t=>[t.toLowerCase(),t]));"
|
||||
" assetCatalogState='ready'; runAssetSearch('pmp-10');"
|
||||
" return JSON.stringify([assetResults[0], assetResults.length]); })()"))
|
||||
chk("a prefix match outranks 600 earlier contains-matches (cap is per tier)",
|
||||
got[0] == "PMP-10-EXTRA" and got[1] == 500, ascii_(got))
|
||||
|
||||
# ── 5. configured-but-broken: a 503 that does not leak ────────────────
|
||||
print("\n5. broken is not a leak")
|
||||
browser.close()
|
||||
@@ -202,10 +220,15 @@ def main():
|
||||
server.terminate()
|
||||
server.wait(timeout=10)
|
||||
os.environ["MICRON_DB_URL"] = "mssql+pymssql://user:S3CRETpw@127.0.0.1:1/MicronDB"
|
||||
# A malformed tuning knob must degrade, not crash the boot (review
|
||||
# finding: int() at import time made "5m" a total-outage switch).
|
||||
os.environ["MICRON_ASSETS_CACHE_SECONDS"] = "5m"
|
||||
try:
|
||||
port2 = cdp.free_port()
|
||||
base2 = "http://127.0.0.1:%d" % port2
|
||||
server = start_server(port2, db_path)
|
||||
chk("the suite boots with MICRON_ASSETS_CACHE_SECONDS='5m' (degrades, no crash)",
|
||||
server is not None and server.poll() is None)
|
||||
st, body = api(base2, "/api/assets", tok["root"])
|
||||
detail = (body or {}).get("detail") or ""
|
||||
chk("a configured-but-unusable catalog answers 503, not 500",
|
||||
@@ -213,8 +236,16 @@ def main():
|
||||
chk("...and the message never echoes the URL, login or password",
|
||||
"S3CRETpw" not in detail and "user" not in detail
|
||||
and "127.0.0.1:1" not in detail, ascii_(detail))
|
||||
# The negative cache (review finding): the second request inside the
|
||||
# failure window must answer from the remembered error - same 503,
|
||||
# same safe text - not stack another connect attempt in a worker.
|
||||
st2, body2 = api(base2, "/api/assets", tok["root"])
|
||||
chk("...and a second request answers the cached failure, stable and safe",
|
||||
st2 == 503 and (body2 or {}).get("detail") == detail,
|
||||
(st2, ascii_(body2)))
|
||||
finally:
|
||||
del os.environ["MICRON_DB_URL"]
|
||||
del os.environ["MICRON_ASSETS_CACHE_SECONDS"]
|
||||
finally:
|
||||
if browser:
|
||||
browser.close()
|
||||
|
||||
Reference in New Issue
Block a user