- openDb pragmas: journal_mode=WAL, synchronous=NORMAL, temp_store=MEMORY,
cache_size=-8000; 3000-record stress write throughput 9514ms -> 1241ms
(7.7x), single upsert ~0.3ms; read paths unchanged (<=22ms)
- authUser: devices.last_seen_at now written only when stale > 60s,
removing one write per authenticated request (write amplification)
- /auth/register: strip client-supplied seed flag (PoC confirmed it bypassed
invite code, MAX_USERS quota AND first-user auto-admin, granting admin
without any credential); demo-rebuild internal path unaffected
- /auth/login: per-IP+username of 5 consecutive failures gets a 10-minute
cooldown against in-memory counter (brute-force guard)
- /auth/logout: new endpoint revokes the current Bearer token server-side;
frontend calls it (best-effort) before local clearToken on sidebar logout
- attachment docx/xlsx preview: sanitize generated HTML (strip scripts,
event handlers, dangerous protocols) before dangerouslySetInnerHTML
render, preventing token theft via malicious previewed documents
PoC suite: seed smuggling rejected, 429 on 6th login attempt, token dead
after logout, per-user isolation intact (403/401/404 as before);
integration 61/61 green
- headless DOM audit (Edge dump, 1440/390 x 10 routes): no real horizontal page overflow on any screen size; timeline bleed & drawer offsets are intentional and verified benign
- header now labels profile/resume/assessment/admin routes (was blank h1 on those pages)
- dialog header truncates long titles instead of pushing controls out
- page containers audited for consistent rhythm (1080/1280/880 widths share identical paddings)
- www rebuilt, temp instrumentation removed
- profile_upsert now merges top-level keys (info/resume/zongce are independent sub-documents); previously any page saving its own slice silently wiped the other two (profile save lost zongce rules, assessment save lost resume, resume save lost zongce)
- ZcItem gains recordIds: aggregated cap items (wcap/ccap/kcap/gcap/hcap) expose the records they aggregate, so the evidence gallery scored/unscored badge works (matchedIds was always empty before)
- add regression coverage notes; www rebuilt
- scoring rules panel moved above the sheet and auto-scrolls into view on toggle (was rendered at the very bottom, felt unresponsive)
- word export: unified single spec table with numbered section rows (one/two/three...), subtotal and total rows, editorial heading fonts
- standardized fonts and metrics: body SimSun 10.5pt, headings SimHei, numerals Times New Roman, line-height, A4 margins 72/78/66pt
- footer with PAGE/NUMPAGES fields via mso-element footer, Word opens in Print layout view
- evidence captions and rules annex restyled to match; www rebuilt
- fix per-year computation: termYearOf parses semester terms (2023-2024-1) so year Scots no longer skipped the semester filter (previous bug made yearly score empty)
- academic score now uses semester.gpa shown on grades page (single source of truth); fallback recalculates with the same band-linear + category weights formula
- gradeLabel infers grade year (freshman/sophomore) from eduStart, falls back to earliest semester year
- info grid 3x3 with phone number as the 9th cell (page + word export)
- evidence gallery shows every activity/competition record with attachments in scope (no 2-image cap), records marked scored/unscored
- scoring rules reference panel: default rules editable and persisted with profile; restore-default button; no college/school names in citations
- word export mirrors all of the above, adds rules annex table; removed dead zongceWord.ts with school citation
- sync-server/www rebuilt