Files
Project-SDE-WP-Suite/docs/reference/file-map.md
n.siegfried fe8a27e022 T0.2 - baseline captured; all six rendering defects confirmed present
Runs the app from a clean database, captures the before images, and records
which of F1-F6 actually still reproduce. All six do.

Rather than eyeball screenshots, each defect is measured in a browser by
tests/f_items.py, which reports REPRODUCES / FIXED / INCONCLUSIVE and never a
silent pass. That makes it both the wave 0 record and the wave 1-3 regression
check: an item is done when its probe flips to FIXED.

  F1  hero says "Job A", app bar still says "Select a project", no reload
  F2  "Sign out" spans x382-432 in a 390px viewport - cut in half, 3 rows
  F3  chrome paints over the logo by 106x32px; .header-left collapses to 0
  F4  comments drawer overlaps the header by 380x91px in the standalone creator
  F5  5 of 5 ENABLED wizard inputs compute #f4f4f4 on #e0e0e0
  F6  11 cards in one 5,017px scroll, 0 tabs (review said ~4,700px; it grew)

Three probes needed care to avoid reporting a false pass, and the traps are
worth knowing before anyone verifies a fix:

  F1 disappears if localStorage is primed first, because then both sources of
  truth agree. The probe clears it and drives the real picker.
  F3 needs a long project name that is long IN THE DATABASE - any page reached
  with ?project= re-pulls it and overwrites a locally-faked one. It also cannot
  be measured by comparing .header-left to the chrome: under the long name
  .header-left (flex:1, min-width:0) collapses to clientWidth 0, so that
  comparison reports a tidy zero gap while the chrome paints across the logo.
  It measures against .logo, which is flex-shrink:0. My first two attempts at
  this probe both reported FIXED for those reasons; the screenshot did not.
  F5 must ignore genuinely disabled inputs or a fix looks done while real
  fields stay grey.

14 screenshots, not the 12 the plan asks for, because there are 7 pages
(file-map D1). Capture also measures horizontal overflow, which is how BL-001
was found.

Tooling: cdp.py gains viewport() and screenshot() - it could do neither, and
T0.2 requires 390px and 1440px images. 390px sets the mobile flag rather than
just narrowing the window, since every page declares width=device-width and
Chrome otherwise lays out at 980px and no media query under test fires. Both
new scripts reuse browser_check.py's seed() and start_server() instead of
growing a second fixture. Existing browser_check still passes 71/71.

No application code changed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-14 18:06:36 -05:00

314 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# File map — `html/` and the references the plan depends on
**Task:** `T0.1` · **Produced:** August 14, 2026 · **Branch:** `feat/wp-suite-r2-implementation`
Wave 0 exists because the item IDs and line numbers in this plan came from a UX review of
branch `users/directory-super-user`, not from a fresh read. This document is the fresh read.
Everything below was verified against the working tree, not quoted from the review.
**Read the [Discrepancies](#discrepancies) section before starting any wave.** Four things the
plan asserts are wrong, and one of them (`A1`) guards a code path CLAUDE.md says must not change.
---
## 1. Pages
`html/` holds **7 pages, 6 stylesheets, 11,867 lines**. The plan says "6 pages, 4 stylesheets,
roughly 11,900 lines" — the line count is right, the other two are not. See D1.
| Page | Called in this plan | Lines | Stylesheets (load order) | Scripts (load order) | Iframe |
|---|---|---|---|---|---|
| `html/login.html` | login | 163 | `theme-light.css` | `login.js` | neither |
| `html/index.html` | launcher | 703 | `theme-light.css`, `wp-chrome.css` | `auth-guard`, `wp-format`, `feedback-config`, `project-data`, `help`, *(inline 418700)*, `wp-chrome` | neither |
| `html/work-package-suite.html` | SOP wizard | 437 | `theme-light.css`, `wp-chrome.css`, `work-package-suite-styles.css` | `auth-guard`, `wp-format`, `feedback-config`, `project-data`, `help`, `work-package-suite-app`, `wp-chrome` | **hosts** |
| `html/wp-creation-index.html` | creator | 401 | `theme-light.css`, `wp-creation-styles.css` | `auth-guard`, `wp-format`, `feedback-config`, `project-data`, `help`, `wp-creation-app` | **child** |
| `html/admin.html` | admin | 219 | `theme-light.css`, `wp-chrome.css`, `console.css`, `wp-sidenav.css` | `auth-guard`, `wp-format`, `console-util`, `admin`, `wp-chrome`, `wp-sidenav` | neither |
| `html/users.html` | directory/users | 106 | `theme-light.css`, `wp-chrome.css`, `console.css`, `wp-sidenav.css` | `auth-guard`, `wp-format`, `console-util`, `users`, `wp-chrome`, `wp-sidenav` | neither |
| `html/field.html` | field view | 93 | `theme-light.css`, `wp-chrome.css`, `wp-sidenav.css` | `auth-guard`, `wp-format`, `project-data`, `help`, `field`, `wp-chrome`, `wp-sidenav` | neither |
No script anywhere in `html/` uses `defer`, `async`, or `type="module"`. Every one is a
render-blocking classic script. `auth-guard.js` and `wp-format.js` load in `<head>`; the rest
load at the end of `<body>`.
### The iframe boundary (matters for `B7` / `T7.1`)
`work-package-suite.html:383` declares `<iframe id="wp-frame">` with **no `src`**.
`work-package-suite-app.js:581` sets it at runtime to
`wp-creation-index.html?embedded=1&project=<id>`.
The creator is the only page that loads **neither `wp-chrome.css` nor `wp-chrome.js`** — that is
why it has no app bar of its own and why it looks like part of the wizard. `T7.1` has to give it
that chrome back, or deliberately not.
Three scripts branch on `window.top !== window.self` and will change behaviour when the frame
goes away:
| Script | Line | Framed behaviour |
|---|---|---|
| `auth-guard.js` | 15 | redirects `window.top` to `login.html` |
| `wp-chrome.js` | 17 | returns early, renders no chrome |
| `help.js` | — | suppresses the Help FAB in the child |
### Pages that are not what the plan implies
- **`field.html` is 93 lines.** It is a shell; `field.js` (178 lines) builds it. A task that says
"edit the field view page" almost always means `field.js`.
- **`index.html` carries ~296 lines of inline `<style>`** (lines 16312) — the bulk of the
launcher's CSS is not in any stylesheet. `T3.x` token work has to reach inside it.
- **`admin.html` and `users.html` are near-identical shells** over `console.css` + `console-util.js`.
---
## 2. Stylesheets
| File | Lines | Purpose | Consumed by |
|---|---|---|---|
| `theme-light.css` | 249 | Carbon light (g10) token set, base type, shared dark appbar | **all 7 pages** (always first) |
| `console.css` | 191 | dense admin tables and toolbars | admin, users |
| `work-package-suite-styles.css` | 610 | SOP wizard page sheet | SOP wizard |
| `wp-chrome.css` | 237 | injected chrome: project switcher, global search | launcher, SOP wizard, admin, users, field |
| `wp-creation-styles.css` | 884 | creator page sheet | creator |
| `wp-sidenav.css` | 90 | off-canvas nav drawer | admin, users, field |
**No `@import` exists in any stylesheet.** Composition is entirely `<link>` order.
`theme-light.css` is linked first on all 7 pages.
### The four parallel token systems (`S5`, consolidated in wave 3)
| Sheet | Prefix | Root tokens |
|---|---|---|
| `theme-light.css` | `--cds-*` (2117) and `--wp-appbar-*` (169176) | 114 + 6 |
| `console.css` | **unprefixed** (1220) | 28 |
| `work-package-suite-styles.css` | **unprefixed** (118) | 16 |
| `wp-creation-styles.css` | **unprefixed** (931) | 21 |
| `wp-chrome.css` | `--wpc-*`, scoped to `.wp-chrome`, **not `:root`** (1836) | 0 at root |
| `wp-sidenav.css` | none — every colour is a hardcoded hex | 0 |
The three unprefixed sheets are the collision risk. They are **mutually exclusive per page**
today (console on admin+users, wizard sheet on the wizard, creator sheet on the creator), so
nothing currently breaks — wave 3 must not assume that stays true once chrome is unified.
Redeclared across sheets with the **same** value: `--bg`, `--border`, `--border-strong`,
`--text`, `--accent`, `--red`, `--shadow`, `--text-dim`.
Redeclared with **different** values — these are the real defects:
- `--shadow-lg``0 4px 16px rgba(0,0,0,0.16)` (wizard) vs a different blur in the creator
- `--mono``console.css` drops `ui-monospace` and `Segoe UI Mono` from the stack
- `--surface``#fff` (console) vs `#ffffff` (creator); same colour, two notations
- `--appbar: #161616` (wizard) duplicates `--wp-appbar-bg: #161616` (theme) under another name
Set at runtime on `documentElement`, declared in no stylesheet:
`--wp-chrome-h` (`work-package-suite-app.js:528`), `--rail-top` (`wp-creation-app.js`).
Declared on `body` rather than `:root`: `--nav-w` (`wp-creation-styles.css:673,674,805`).
Only cross-file dependency: `work-package-suite-styles.css` consumes `--cds-hover-primary`,
which only `theme-light.css` declares.
---
## 3. Verified references
Every reference the plan relies on, checked against the working tree.
| Reference | Claim | Status | Actually |
|---|---|---|---|
| `wp-creation-app.js:1144` | `alert()` "Subject and WP Type are required" | **CONFIRMED** | exact |
| `wp-creation-app.js:1962-1972` | logged-override path for predecessors | **MOVED — see D2** | that range is `dashIssue()`, which *refuses*. Real path: **`967984`** |
| `work-package-suite-app.js:326` | only `beforeunload`, analytics dwell | **CONFIRMED** | exact; sole `beforeunload` in all of `html/` |
| `work-package-suite-styles.css:322-328` | `outline:none` + pale 3px glow | **CONFIRMED** | block runs 321328, `outline:none` on 325 |
| `work-package-suite-styles.css:336` | comment about `.field-hint` unstyled | **CONFIRMED** | comment 336338, rule on 339 |
| `console.css:85-87` | contrast fix not propagated | **CONFIRMED** | comment 8587, fix on 88 |
| `admin.js:484-517` | language and time localization | **CONFIRMED** | exact — "Localization defaults" block |
| `server/seed_demo.py` | seeds without authenticating | **CONFIRMED** | zero auth code in 178 lines |
Notes that change what a later task should do:
- **`work-package-suite-styles.css`** — `outline: none` appears **three** times, not once: 325
(the cited block), 347 (`.user-pick:focus`, same pale glow), and 501
(`.seq-step input.seq-label:focus`, *no* replacement glow at all). `A3`/`F5` should fix all three.
- **`console.css:85-87`** — the comment measures `#8d8d8d` on white at **3.3:1**. Wave 4's `T4.6`
says "about 2.9:1". They disagree; `T4.6` should re-measure rather than quote either. Both
other sheets still bind helper text to the rejected `#8d8d8d`
(`work-package-suite-styles.css:10`, `wp-creation-styles.css:17`), and the creator applies it
at **10px** (`wp-creation-styles.css:374`), worse than the 12px the comment measures.
- **`seed_demo.py`** fails *loudly but confusingly*: `call()` swallows the `HTTPError` and returns
the error body, so line 101's `proj["id"]` raises `KeyError` instead of reporting a 401.
Only `/api/health` is unauthenticated, so the health check passes and it dies immediately after.
---
## 4. Baseline counts
Recorded so wave 9 can prove they went down. Run from `html/` unless stated.
| # | Metric | Review | Actual | Command |
|---|---|---|---|---|
| 1 | native dialogs app-wide | 79 | **79** ✓ | `grep -ohE '\b(alert\|confirm\|prompt)\(' *.js *.html \| wc -l` |
| 2 | …of those, in the creator | 43 | **43** ✓ | same, over `wp-creation-app.js` |
| 3 | `<div onclick>` | 12 | **12** ✓ | `grep -oE '<div[^>]*onclick' *.html *.js \| wc -l` |
| 4 | `<span onclick>` | 2 | **2** ✓ | `grep -oE '<span[^>]*onclick' *.html *.js \| wc -l` |
| 5 | `#0f62fe` accent systems | 4 | **4** ✓ | see below |
| 6 | `.help-tip` badges | 15 | **15** ✓ | `grep -oE 'class="help-tip"' *.html \| wc -l` |
| 7 | `aria-live` regions | 0 | **0** ✓ | `grep -ohE 'aria-live' *.html *.js \| wc -l` |
| 8 | `pushState` calls | 0 | **0** ✓ | `grep -ohE 'pushState' *.html *.js \| wc -l` |
Dialogs by file: `wp-creation-app.js` 43 · `work-package-suite-app.js` 14 · `users.js` 10 ·
`admin.js` 6 · `index.html` 6.
`<div onclick>` by file: `work-package-suite.html` 10 · `wp-creation-index.html` 1 ·
`work-package-suite-app.js` 1. Both `<span onclick>` are in `wp-creation-app.js`.
**Metric 5 needs its definition stated**, or wave 9 will measure a different thing.
`#0f62fe` appears **31** times across `html/`, and **14** of those are custom-property
declarations. The review's "4" is the number of **stylesheets that declare their own accent
token** — the four parallel systems:
```
grep -nE '^\s*--[a-zA-Z0-9-]+\s*:\s*#0f62fe' *.css # 14 declarations, in 4 sheets
```
| Sheet | Token |
|---|---|
| `theme-light.css` | `--cds-interactive-01` (+ 6 Carbon aliases) |
| `work-package-suite-styles.css:2` | `--primary` |
| `wp-chrome.css:25` | `--wpc-accent` |
| `wp-creation-styles.css:18` | `--accent` |
**The wave 9 target is 1.** Track the sheet count, not the raw occurrence count.
**Metric 6:** 15 in page markup (`work-package-suite.html` 3, `wp-creation-index.html` 12).
A 16th lives in `help.js:261` inside the help centre's own copy, demonstrating the component —
it is not a page badge. None carries `tabindex`, and `help.js:26` styles only `:hover`/`:focus`,
so a `<span>` with no tabindex is **keyboard-unreachable**. That is the `C1` defect, and it
confirms "unreachable" literally.
**Metric 7:** `login.html:99-100` uses `role="alert"` and `role="status"`, which are implicit
live regions. The count of the literal `aria-live` attribute is 0, matching the review. CLAUDE.md
points at these two lines as the pattern to copy — they are real and correct.
**Metric 8:** `pushState` is 0. There is **one** `history.replaceState` at `login.js:196`,
cleaning the URL after sign-in. It is not routing, so `X1`'s premise holds: no work package has
an addressable URL.
---
## 5. Running it
```bash
uvicorn server.app:app # against a throwaway SQLite database
python server/smoketest.py # API; needs WP_SMOKE_USER / WP_SMOKE_PASSWORD
python tests/browser_check.py # pages boot and render, self-contained
python tests/baseline_shots.py # screenshots, self-contained
python tests/f_items.py # does each of F1-F6 still reproduce?
```
`server/smoketest.py` is the one that is **not** self-contained: it drives a server you point
it at and aborts unless `WP_SMOKE_USER` and `WP_SMOKE_PASSWORD` are set, because every route
but `/api/health` needs a session. Use an admin account — it creates and deletes a project.
`tests/browser_check.py` and `tests/baseline_shots.py` are self-contained: each creates a
throwaway SQLite database, seeds a fixture, starts its own uvicorn on a free port, drives
headless Edge or Chrome over CDP, and tears everything down. **Your real `wpsuite.db` is never
touched.** Both need Edge or Chrome on the machine; set `WP_BROWSER` to override discovery.
Screenshots for a before/after pair:
```bash
python tests/baseline_shots.py --out docs/reference/baseline # before (committed)
python tests/baseline_shots.py --out /tmp/after --label after # after
python tests/baseline_shots.py --pages creator --widths 390,768,1024,1440
```
`tests/` is referenced by **no wave file**. `T0.2` needs screenshots and `T7.3` requires "a
regression test covers the clear-last-constraint path" without naming a home for it — both
belong here.
`tests/f_items.py` is both halves of the same measurement: it recorded that all six defects
reproduce before wave 1, and it is how waves 13 prove each one stopped. An item is done when
its probe flips from `REPRODUCES` to `FIXED`. It never reports a silent pass — a probe that
cannot decide says `INCONCLUSIVE`.
---
## Discrepancies
Things the plan asserts that the repo contradicts. Listed per T0.1's fourth done-when.
### D1 — "6 pages, 4 stylesheets" is wrong; it is 7 and 6
`wave-0.md:21` says 6 pages and 4 stylesheets. There are **7 pages and 6 stylesheets**. T0.1's
own parenthetical at `wave-0.md:24-25` lists **seven** names, so the document contradicts itself
in the same task. The missing stylesheets are `wp-chrome.css` and `wp-sidenav.css`.
**Consequence:** every "all 6 pages" done-when in waves 2, 3, 5 and 9 is off by one, and `T0.2`
asks for "12 baseline screenshots (6 pages x 2 widths)" when the correct number is **14**.
14 were captured. Treat "6 pages" as "all pages" wherever it appears.
### D2 — `A1`'s protected line reference points at the wrong function
This is the important one. CLAUDE.md, under *Things that must not change*, says:
> **The logged-override path for predecessors stays (A1).** It is an audited business rule, not
> a bug. See `wp-creation-app.js:1962-1972`.
`IMPLEMENTATION.md`'s `X2` cites the same range. **Lines 19621972 are `dashIssue()`**, which is
the *opposite* code: the dashboard guard that **refuses** to issue and tells you to
"Open the package to release it early with a logged reason". The reviewer read that sentence and
correctly concluded a logged override exists — but cited the mention, not the implementation.
The actual audited path is:
| Part | Location |
|---|---|
| **`confirmEarlyRelease()` — the override itself** | **`wp-creation-app.js:967-984`** |
| state `pkgGateOverride` | `:392`, reset at `:481`, `:488`, `:1744` |
| call site — status change | `:998-1002` |
| call site — save | `:1149-1150` |
| persisted onto the package | `:1117` (`gateOverride:`) |
| rendered in the printed package | `:1215` |
| rehydrated when loading a package | `:1674` |
| the *guard* the plan actually cited | `:1960-1974` (`dashIssue`) |
The comment at `:967-969` states the rule plainly: *"Releasing with an unclosed predecessor is
allowed but must be explained. The reason rides on the package (`data.gateOverride`) and the
server writes it to the audit log."*
**Consequence for `T7.3`:** the "do not remove" instruction must be applied to **`967-984` and
its seven satellites**, not to `1962-1972`. A task that preserved only 19621972 would delete the
audited business rule while believing it had protected it. `dashIssue()` must also survive — it
is what stops the dashboard becoming a quiet way around the gate — but it is a second thing to
protect, not the same thing.
`X2`'s reasoning is unaffected: hold state genuinely is not purely derived from open
constraints, so `CR-015` and `A1` remain the same code and the same task.
### D3 — four documents the plan reads from do not exist yet
Not errors; they are deliverables not yet produced. Recorded so no task treats one as a missing
input and goes looking for a rename.
| Path | Created by | Also read by |
|---|---|---|
| `docs/reference/file-map.md` | `T0.1` | `T0.2`, `T1.1`, `T2.1`, `T2.3` |
| `docs/reference/tokens.md` | `T3.1` | `T3.2`, `T3.5`, `T9.3` |
| `docs/reference/accessibility-audit.md` | `T9.5` | — |
| `docs/reference/completion.md` | `T9.7` | — |
`backlog.md:16` contains `path/to/file.js:120` inside a fenced format template. It is a
placeholder, not a reference — do not resolve it.
### D4 — the creator overflows horizontally at 1440px, not just at 390px
Measured during `T0.2`, not in the review. Content width against the viewport it was given:
| Page | 390px | 1440px |
|---|---|---|
| launcher | 425 | ok |
| SOP wizard | 429 | ok |
| creator | 485 | **1551** |
| field view | 432 | ok |
| login, admin, users | ok | ok |
Four pages overflow at 390px, which is `F2` and its neighbours. The creator also overflows by
111px **at desk width**, which no `F` item covers. Logged to `docs/waves/backlog.md`; not fixed
here, since wave 1 is scoped to `F1``F5` and `T7.x` rebuilds this page anyway.