2485ca7b6139d562f80337055d3bd19522a98370
9 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
| 2485ca7b61 |
T3.3 - S5: fix .field-hint properly and retire the comment it left behind
The comment at work-package-suite-styles.css:395 recorded the consequence of the four token systems: .field-hint's only rule lived in wp-creation-styles.css, which the wizard does not link, so the class rendered as unstyled body text. The rule was later added to the wizard sheet, but as a SECOND rule with declarations byte-identical to the .field small rule four lines above it - the same duplication that caused the original bug, in miniature. Merged into one rule under both selectors, so they cannot drift apart again, and deleted the comment. It described a fixed problem, and its account of where the class is used was wrong anyway: .field-hint appears once on the wizard, not in "step 2's CM hint, the team-member notices". The colour is deliberately untouched. --text-dim is #8d8d8d, which measures 3.3:1 on white and which console.css:103 already rejected for exactly this job - but propagating that fix is T4.6, which is told to re-measure rather than inherit either published figure. Fixing it here would change a rendered value in a wave that must produce none, and would take the decision away from T4.6. THE HUNT for the same class of failure elsewhere, which is the rest of the task. Statically, per page, over every stylesheet that page actually links plus its own inline <style>: every bare var(--x) reference where --x is declared by no sheet the page loads. That is the .field-hint failure exactly, and it is the one that renders nothing rather than rendering wrong. login 32 refs, launcher 54, sop 67, creator 92, admin 100, users 100, field 56 unresolvable: 0 of 501 Zero, and structurally zero from here on: after T3.2 every token resolves from theme-light.css, which all seven pages link first. The failure mode is no longer possible rather than merely absent. In the browser, on all seven pages: every class on a real element that no rule in any loaded stylesheet targets, including the sheets help.js and wp-creation-app.js inject at runtime. One candidate, and it is not a defect - .team-pick on the wizard's four project-team selects is a JS hook that sits beside data-team, and the selects are inside .field, so .field select styles them. Its sibling .user-pick needs its own rule only because those two selects sit OUTSIDE .field, which the comment there already explains. Stated as a limitation rather than left implicit: the browser half sees each page in its load state, so a class applied only on interaction would not appear in it. The static half has no such gap - it covers every rule in every sheet regardless of what is on screen - and it is the half that catches this particular bug. Verification: token_check reports every token and all 3,500 elements identical to wave 2 on all 7 pages. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
|||
| 9ab7b48de2 |
T3.2 - C3/S5: one source of truth for colour; page sheets alias only
theme-light.css is now the only file in html/ that contains a colour literal. The five page stylesheets and all four inline <style> blocks declare names and nothing else. theme-light.css 191 declarations, 175 with a literal value console.css 28 declarations, 0 work-package-suite-styles.css 16 declarations, 0 wp-chrome.css 14 declarations, 0 wp-creation-styles.css 24 declarations, 0 wp-sidenav.css 0 declarations, 0 #0f62fe is declared in one sheet, down from five. The eleven occurrences left inside theme-light.css are Carbon's own v10-to-v11 alias layer, which the inventory records as deliberate and not the S5 defect. Names were kept, because 111 var() references live in .js files across 23 token names and a rename there fails silently - no build error, no console warning, just an unstyled element. The rule the refactor was built on: consolidation is not unification. Where two sheets declared the same value, they collapse. Where they declared DIFFERENT values for one role - the two shadows, the eight status borders doing four jobs, the three mono stacks - each value got its own canonical name and the pair is recorded for T3.5. Picking a winner between two near-identical greys is a rendered change, which this task forbids. The console's zebra stripe is the one that would have bitten: #fafafa is six points from #f4f4f4, and merging them erases the striping on the nine-column user table. Collecting the one-offs in one place made two things countable that were not before: twelve distinct shadows, and a ninth amber (#8a6d00 on the field view, four points from #8e6a00 and doing the same job - BL-009). VERIFICATION - the screenshot done-when could not do the job, so it was replaced. Captured against wave 2, 11 of 14 shots were pixel-identical and 3 were not. Capturing wave 2 against ITSELF produced the same 3 differences at the same bounding box, so those shots cannot distinguish a regression from the clock. Trap 2 in the brief is half wrong: users.html is stable at both widths; the unstable third is the creator at 1440px, and admin's captured page height varies by ~600px between runs (BL-012). So tests/token_check.py was added. It checks what wave 3 actually claims: that every custom property resolves to the same literal, and every element computes the same colours, shadows and type. That is stronger than a screenshot - it covers the hover, focus and disabled rules a screenshot never exercises, and it is deterministic. wave 2 vs T3.2, all 7 pages: 178/178 wave-2 token names resolve identically, +213 new 3,500 elements compute identically, zero added, zero removed 16 tokens differ in notation only (#fff -> #ffffff), which is the duplicate class this task existed to collapse Two detours worth not repeating: the element walk was first keyed by sibling index and reported 55 phantom differences on the SOP page, where three JS-injected overlays append in whichever order their async work finishes (BL-011); and the comparator now normalises notation before reporting, because otherwise it fails on its own success. f_items 5 FIXED / F6 REPRODUCES as expected. browser_check 71/71. ONE DONE-WHEN NOT MET, recorded rather than skipped: "no page stylesheet declares a raw color, spacing or type value". The colour half is met in full. 483 raw spacing values, 281 font-sizes and 65 radii remain inside rules, 492 of them in the creator. That is arithmetic, not effort: the creator's spacing is every integer from 1px to 14px, so no token exists that padding:9px 11px maps to without changing one of the numbers - and this task forbids changing a rendered value. The two requirements are mutually exclusive. Logged as BL-010 for T5.x and T7.1, where those pages are re-laid-out and the values get chosen again. New backlog: BL-009 (ninth amber), BL-010 (raw spacing/type in rules), BL-011 (overlay append race), BL-012 (unstable screenshot targets). Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
|||
| 5f3141e2a3 |
T1.5 - F5 (interim): wizard fields stop looking disabled
INTERIM. T3.4 removes the duplicate token underneath this; the job here is only the appearance, and no token consolidation is started. The wizard filled its inputs with var(--bg) - which in this sheet is the PAGE BACKGROUND, #f4f4f4 - on a #e0e0e0 border. An empty required field was indistinguishable from a locked one, which is why people were not typing in them. The cause is the one the review named: this sheet redeclares its own tokens, so it never saw --cds-field: #ffffff, even though theme-light.css has been supplying that to this page all along. Fields now consume --cds-field, and take the same --border-strong the creator's inputs already use, so a field looks like a field on both pages. No new value is introduced - both tokens already existed. That inverts a signal if left there, so it needed the other half: there was no disabled rule at all on this page, meaning locked fields would have turned white too. Disabled and readonly fields now take --cds-field-02, the theme's own secondary field surface, matching .locked-field in the creator. Enabled #ffffff against disabled #f4f4f4, verified by computed style rather than by eye. The border is deliberately the same on both states. I first wrote `border-color: var(--border)` on the disabled rule and could not demonstrate it taking effect - the rule matches, is more specific than the base rule, and its background applies, but the computed border stayed --border-strong. Rather than ship a declaration whose effect I cannot show, it is gone: a consistent border is what "consistent with inputs elsewhere" asks for, and the fill is what carries the state. Screenshot diff is limited to the wizard, but establishing that took a control run. admin and users appeared to change too, until capturing twice with NO code change showed they differ from themselves - the console pages render live timestamps and are not byte-stable. login, launcher, sop, creator and field are. Recorded in the baseline README so the next task with a "no layout change" done-when does not chase it. The F5 probe now also fails if enabled and disabled fields become identical, which is the way this fix could silently go wrong. f_items: F1-F5 FIXED, F6 untouched as wave 1 requires. browser_check 71/71. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
|||
| d9f96f20e1 |
T1.3 - F3: give the SOP header's groups a defined relationship
.header-left and the injected chrome were in a dead tie. Both were
`flex: 1 1 auto` with `min-width: 0`, so both claimed the same run of the bar
and both were allowed to shrink to nothing. The chrome's content is wider, so
it won every time: .header-left computed to clientWidth 0 while its
flex-shrink:0 logo kept its 106px and overflowed underneath the project
switcher. With the real project name that meant "2667008" rendered on top of
the PRIME wordmark and the name itself clipped to "on EUV Cleanroom En...".
The bar now has an order of giving way rather than a tie:
.header-left flex: 0 1 auto, min-width: auto sizes to content, floors at
the logo plus the gap
.wp-chrome flex: 1 1 auto (unchanged) the only one that grows
.header-right flex: 0 0 auto keeps its buttons
min-width:auto restores the content-based floor the explicit `min-width: 0` had
removed. The inner title block keeps its own min-width:0, so the project name
still gives way first, through the ellipsis .header-subtitle already carries -
truncation policy stays B2's, and nothing here silently truncates.
Two things the review did not name were colliding on the same bar and are fixed
with it. .header-right was being squeezed below its buttons, so "Load Sample"
ran underneath "Feedback". And the header was a fixed 48px holding FOUR groups,
not two - the markup's two plus what wp-chrome.js and auth-guard.js inject - so
at 1024px the overflow had nowhere to go but on top of its neighbours, and
T1.2's user-menu wrap turned that into three rows spilling onto the tab row
below. min-height plus flex-wrap lets the bar grow instead.
Header height at 1440px with a normal project name is still exactly 48px, so
desk layout is unchanged; sop-1440 differs from the wave 0 baseline only
because T1.1 gave the switcher a name to show in place of "(unnamed)". With the
long name it grows to 62px at 1440 and 82px at 1024 - wrapping rather than
overlapping, which is the point.
The F3 probe was too narrow to have caught the right-hand collisions: it
compared the logo against the chrome and nothing else. It now checks every pair
of groups sharing the bar, plus anything spilling out of it, and still reports
FIXED at 390, 768, 1024 and 1440 with the long name.
Verified at all four widths with "Micron EUV Cleanroom Enable 2667008": no
overlapping pair, nothing spilling, logo fully visible.
browser_check 71/71. f_items: F1, F2, F3 FIXED.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
|
|||
| e3527a6e1d |
Act on the fragility audit: boot-order crash, real cache correctness, deep links
A 55-agent audit of the last few commits confirmed 32 findings. The high and medium ones are fixed here; the ranked leftovers are listed at the end. Boot-order crash (my regression, wave 2) - wp-format.js loaded AFTER wp-creation-app.js on every page, but the creator boots synchronously at parse time and its comment renderer calls wpFormatDateTime(). With any review comment present that threw a ReferenceError and aborted the rest of boot. The formatter now parses before the app scripts on all five pages. Verified with a comment seeded: the date renders and boot completes. The network-first fix didn't actually work - `fetch(req)` inherits the request's default cache mode, so it consults the browser HTTP cache — the previous commit's "network-first" still allowed a page to run against a stale sibling. Code is now fetched with cache:'no-cache' and precached with cache:'reload'. - Nothing pinned freshness on the wire either: no Cache-Control anywhere, so browsers applied heuristic caching (~10% of a file's age) and each file expired at a different moment. NGINX and the dev server now send no-cache for html/css/js/ webmanifest; images stay cacheable. Verified on the wire. - Non-ok responses were returned verbatim, so a 502 broke pages the cache could have served; they now fall back to the cache. Cache keys drop the query string, which fixes both the offline miss on every in-app link (?project=…&tab=…) and unbounded cache growth. respondWith can no longer resolve to undefined. Cache bumped to v5. Embedded creator - Dropped the &t=Date.now() cache-buster and made the frame's identity the PROJECT. The view and which package to open are now applied by calling into the loaded document, so switching tabs no longer reloads it — that reload discarded unsaved form edits, made the creator unreachable offline, and stored a fresh copy per click. - ?view=dashboard was re-read on every tab switch, so after one deep link the "Work Package Creation" tab kept opening the Dashboard for the rest of the session. Deep-link params are consumed once now. - ?wp=<id> — which the global search has been emitting since wave 2 — was read by nothing, so picking a work package in search opened a blank one. The creator now exposes openWpById() and the shell applies it after a new 'wp-creator-ready' event, because the frame's load fires before pullProject() resolves. - Math.max(320,…) could make the frame taller than the space available while page scrolling was disabled, pushing content off a window that couldn't scroll. Full-bleed is now only used when at least 460px remains, and the SOP-incomplete gate never runs inside it. A ResizeObserver re-measures when wp-chrome.js grows the app bar. Contract drift - .field-hint and .user-pick are used on the SOP suite page but their only rules lived in wp-creation-styles.css, which that page doesn't link — the CM hint and the sign-off pickers had no styling at all. Rules added to the suite's stylesheet. - The creator's critical floor now also hides modal overlays (a stale stylesheet rendered their contents inline in the form) and gives the jump bar a sane sticky top. - login.js dereferenced ids unguarded where the old version guarded, so a cached older login.html would break sign-in itself. Guarded. - The "Language & time" menu item was added only if wp-format.js had already parsed; the check now happens at click time. Verified: 157 API checks across five suites on a clean database, plus 22 driven UI checks — boot-with-comment, tab switching with a no-reload probe, short-viewport fallback, and the search deep link landing on the right package. Not done, ranked: ~50 dead CSS rules across three stylesheets; dead .team-pick and .constraint-option contracts; wp-chrome.js's documented '.header' mount branch is unreachable because the creator loads neither wp-chrome.js nor its CSS; the squeeze half of the embed layout (.content-area.embed-full) is still CSS-only, which degrades to the old narrow column rather than breaking; fingerprinted asset URLs would make a mismatched pair unrepresentable rather than merely unlikely. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> |
|||
| fcba74b584 |
Fix the WP navigator and the squeezed embedded layout; add per-project permissions
Layout — the reported "skinny scrolling windows" - .content-area capped the whole suite at 1000px, so on a 1920 screen the embedded Work Package Creator ran in a ~930px column with its own scrollbar inside the page's. The wizard now caps at 1700px and the Creator/Dashboard tab goes full-bleed: the iframe fills the window below the app chrome and owns the only scrollbar. Needed `flex: none` on the content area — as a `flex: 1` item its flex-basis overrode `height`, leaving the used height indefinite so the child's `height: 100%` collapsed the iframe to its 150px default. - The SOP wizard's fields were one per row; they now flow into ~340px columns. Navigator — now an auto-hiding drawer - It was a fixed 262px column that stole width from the form AND was hidden below 1100px, so embedded (the normal path) it never appeared at all — that's the "broken side menu". It's now an overlay drawer behind a slim always-visible edge handle: hover or tap to open, move away / Escape / pick a package to close, or pin it to keep it open (pinned shifts the form and the page chrome across, and is remembered). A gutter keeps the handle off the section-nav chips. Bugs found while checking the site over - collectStepData() still read the SOP team fields as text inputs, but wave 1 made them account pickers — so it wrote a user ID into state.team.pm where the display NAME belongs, and the SOP would print `user_ab12…` as the PM. Now synced properly from the pickers. - loadSampleData() set .value on those selects with fictional names; setting an unmatched value on a <select> silently does nothing, so the sample lost its team. It now stores them as names without an account, which the picker shows as "(no account)". - My earlier CSS block replacement had deleted the SOP-chip, people-picker and critical-tag styles. Restored. Same picker everywhere the SOP names someone - Sign-off roles (step 3, required and optional) are account pickers now, storing userId alongside the name, so a signature belongs to an account that can be notified. Titles stay free text. Per-project permissions (asked for: "change project permissions for individual users") - project_members.role overrides the account's role on that project, so a PM on one job can be a Project User on another. Empty = inherit; app admin is admin everywhere. effective_role() feeds require_project_admin, so WP delete, completed- SOP edits and project delete are all judged per project. - Project access is now its own column in the admin console (it was buried among the action buttons, which is why it couldn't be found), showing the project count per account; the dialog sets access plus the role on each project. - The members endpoint reports each person's effective role on that project. Verified: 157 API checks across five suites on clean databases (44 permissions + 22 password reset + 34 search/localization + 39 gates/notifications + 18 new per-project permission checks), 16 drawer-behaviour + 4 pinned-mode UI checks driven in headless Chrome, and probes confirming the team/sign-off pickers populate and no longer corrupt state.team on step navigation. Screenshots reviewed at 1920x1080. Service-worker cache bumped to v3 so browsers pick up the new shell. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
|||
| 61d1cf4bff |
Wave 2: form cleanups from the site comments, plus localization, project switcher and global search
Site comments (8/3) - BIM card: LOD removed, IFF # added next to the coordination status, and required once that status is "Signed off (IFF)" — an unnumbered sign-off isn't traceable. A LOD already stored on a package is preserved and shown as legacy, not blanked. - The blue "from SOP types" subtext under a field is now a SOP chip on the label with the detail in a tooltip. The chip stays visible rather than hover-only: field tablets have no hover, and "this came from the SOP" is the part that matters. The hint elements stay in the DOM (hidden) so the code writing to them keeps working; an observer mirrors their text into the tooltip. - Specification Section is no longer typed per package. Each WP type carries a spec section on the SOP; the field is read-only in the Creator and follows the type, with the SOP's spec folder linked underneath. This reads both spec comments as one intent — stop typing it, derive it. - Assignees and Distribution are multi-selects over the SOP project team, showing each person's job function, with the CM pre-added to Distribution (removable per package) and a free-text option for people with no account. The stored display strings are unchanged so print/export/dashboard keep working; account ids ride alongside for the notification work in wave 3. Localization + time - Per-user locale/timezone (Language & time in the user menu), an app-wide default in the admin console, then the browser. Timezones are validated against the server's zoneinfo and the picker is fed from it. Calendar dates are formatted from their parts so a due date never reads a day early in another zone. - Every displayed timestamp now goes through the shared helpers. Top-bar chrome - Project switcher beside the logo and a centered global search, injected into either generation of top bar; skipped in an iframe so the embedded Creator doesn't get a second one. Ctrl/Cmd-K focuses search. - GET /api/search covers work packages, projects and SOPs, scoped to the caller's projects, hiding archived packages, with LIKE wildcards escaped. Fixed along the way: showForm() cleared every card's inline display, which undid applyKind() — so the Package Type and BIM cards reappeared on an install-only project. Split out applyKindVisibility() and re-apply it there. Verified: 100 API checks on a fresh database (44 permissions + 22 password reset + 34 search/localization), 24 driven UI checks against the real Creator page in headless Chrome (SOP chips, both people pickers, spec auto-fill, critical tags, BIM suppression), and the chrome harness on both bar styles. Screenshots reviewed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> |
|||
| 39b48055ff |
Productionize WP Suite: auth, security hardening, sync, dashboard, PWA, email
Brings the Work Package Suite from a browser-local prototype to a multi-tenant, SQL-backed deployment hardened for customer IP. Auth & access control - Local username/password login (bcrypt + JWT in an HttpOnly cookie), admin-managed users, per-project membership, and project-scoped API access. - Admin console: change user roles, view the audit trail, manage settings. Security hardening - CSP / HSTS / X-Frame-Options / nosniff headers in nginx; Secure cookie via X-Forwarded-Proto; CSRF Origin check; attribute-safe output escaping. - Login lockout, token_version session revocation, stronger password policy, fail-closed secret loading, encrypted (AES-256) database backups. Persistence & schema - SOPs and Work Packages are now DB-backed and shared across users, written through a durable client sync outbox that queues offline edits. - Alembic migrations applied automatically on container start. New capabilities - Phase 2 dashboard (progress, gating, pagination, archive). - Phase 3 PWA "Field View" with offline caching and auth fallback. - WP owner assignment with OPTIONAL email notifications, OFF by default and toggled from the admin console. SMTP password is read only from the SMTP_PASSWORD env var (never stored); emails carry a WP number + deep link, never customer IP. Also: IBM Carbon restyle, Help section, and DEPLOYMENT.md brought up to date. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
|||
| fd668f0ea2 | Move static files into html/ for Docker bind mount |