A guard, not a build. Removal was floated in the meeting and rejected - Marlena tracks actual hours so they can be measured - and CLAUDE.md carries that as a recorded decision. Verified after eight waves of change: - Actual Hours exists in Closeout (wp_actual_hrs), persists through collect, and prints on the export - it is OPTIONAL: a package closes with it empty (driven, not assumed) - it rolls up per T6.4: rollup_check has pinned actual-hours aggregation at every level since wave 6, and /api/wps/metrics carries actual_hours in its buckets The follow-up the done-when requires is logged as BL-023: a productivity factor (actual / estimated) - the rollup endpoints already carry both sums, so it is a presentation task awaiting its own item id and a placement call. Verification: export_check.py extended to 20/20 (the CR-017 section). Items: CR-017 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
510 lines
32 KiB
Markdown
510 lines
32 KiB
Markdown
# Backlog
|
||
|
||
Anything noticed during implementation that is real but not in the plan goes here instead of
|
||
into the current PR. `CLAUDE.md` requires this: every change traces to an item ID, so
|
||
unplanned work gets logged rather than built.
|
||
|
||
Add an entry, do not fix it inline. This file is reviewed at `T9.7` and feeds the next spec
|
||
revision.
|
||
|
||
## Format
|
||
|
||
```markdown
|
||
### BL-001 — Short title
|
||
|
||
- **Found during:** T3.2
|
||
- **Where:** path/to/file.js:120
|
||
- **What:** one or two sentences on the problem
|
||
- **Why not now:** out of scope for the current wave / needs a product decision / larger than the task
|
||
- **Suggested wave or follow-up:** wave 9 / next revision / needs Nick
|
||
```
|
||
|
||
## Known follow-ups already identified in the spec
|
||
|
||
These are logged from the source documents, not discovered in code. They are real but
|
||
deliberately deferred.
|
||
|
||
### BL-000a — Validated P6 activity lookup
|
||
|
||
- **From:** `CR-001`
|
||
- **What:** `CR-001` accepts free text for the P6 Activity ID. A validated lookup against an imported P6 activity list was identified as the eventual want.
|
||
- **Why not now:** the Micron schedule is actively being reworked, so importing an activity list now would import churn.
|
||
- **Suggested:** next revision, once the schedule stabilizes.
|
||
|
||
### BL-000b — Field-level toggles in General Information
|
||
|
||
- **From:** `CR-006`
|
||
- **What:** `CR-006` toggles whole sections. General Information may need per-field toggles, since projects differ in which identifiers they use.
|
||
- **Why not now:** section-level toggles cover every removal request currently on the list.
|
||
- **Suggested:** next revision, if a second project needs a different field set.
|
||
|
||
### BL-000c — Estimated versus actual hours productivity factor
|
||
|
||
- **From:** `CR-017`
|
||
- **What:** Actual Hours is retained and rolls up. Comparing it against estimated hours would produce a productivity factor, which was the stated reason for wanting the field.
|
||
- **Why not now:** estimated hours capture is not in scope this round.
|
||
- **Suggested:** next revision.
|
||
|
||
### BL-000d — Attachment merge versus list on export
|
||
|
||
- **From:** `CR-008` / `T9.1`
|
||
- **What:** whether the PDF export merges attachments into one package or lists them separately.
|
||
- **Why not now:** product decision, raised in the `T9.1` PR.
|
||
- **Suggested:** needs Nick.
|
||
|
||
## Found during implementation
|
||
|
||
### BL-001 — The creator overflows horizontally at 1440px
|
||
|
||
- **Found during:** T0.2
|
||
- **Where:** `html/wp-creation-index.html` / `html/wp-creation-styles.css`
|
||
- **What:** the creator lays out 1,551px of content inside a 1,440px viewport, so the page
|
||
scrolls sideways at desk width. Measured by `tests/baseline_shots.py`, which compares
|
||
`documentElement.scrollWidth` against `clientWidth` at each capture. `F2` covers narrow
|
||
widths; no item covers this one. The other three overflowing pages (launcher 425px, SOP
|
||
wizard 429px, field view 432px, all at a 390px viewport) are `F2` and are already scheduled.
|
||
- **Why not now:** wave 1 is scoped to `F1`–`F5`, and `T7.1` dissolves this page's iframe and
|
||
rebuilds its layout regardless — fixing it in wave 1 would be thrown away.
|
||
- **Suggested wave or follow-up:** verify it is gone at `T7.1`; if it survives the rebuild,
|
||
it needs its own item in the next revision.
|
||
- **Root cause, found at T1.4 — not fixed, T7.1 owns it.** `wp-creation-styles.css:815` has
|
||
`@media (max-width: 860px) { body { --nav-w: 56px; } }`, which is correct. But
|
||
`wp-creation-app.js:1389` injects `body{--nav-w:288px;}` into a runtime `<style>` with no
|
||
media query. Injected last, same specificity, so it **wins over the media query** and
|
||
`--nav-w` stays 288px at every width. Everything keyed off it then reserves 288px of
|
||
rail that is not there: `.main` padding-left `calc(288px + 28px)`
|
||
(`wp-creation-styles.css:109`), `.ctx-bar` (`:452`), `.release-banner` (`:488`),
|
||
`.section-nav-bar` (`:596`) and `.sticky-save { left: var(--nav-w,288px) }` (`:820`).
|
||
At a 390px screen that forces the initial containing block to 485px.
|
||
The fix is to give the injected rule the same breakpoint, or to stop injecting the
|
||
value that the stylesheet already declares — one line, but it belongs with the creator
|
||
rebuild rather than in a wave 1 rendering task.
|
||
- **Consequence for `F4`:** every `position: fixed; right: 0` element on this page sits at
|
||
the right edge of that 485px box, which is 95px off the visible 390px screen. The
|
||
comments drawer is placed correctly relative to its containing block; the containing
|
||
block is wrong. `T1.4` reports this as an attributed note rather than a drawer defect,
|
||
so nobody is sent to the wrong file.
|
||
- **Update, T7.1 - the recorded root cause no longer applies.** Measured after the
|
||
rebuild by `tests/frame_check.py` section 6: at a 390px viewport the creator's
|
||
`scrollWidth` is still **485** against a `clientWidth` of 390, so the overflow
|
||
survives - but `--nav-w` now computes to **56px**, which is the media query
|
||
winning. The injected `body{--nav-w:288px}` explanation above is spent; whatever
|
||
fixed it, it was not this task.
|
||
What is left is a different thing entirely: the widest in-flow boxes are the
|
||
creator's **data tables**. `#asset-body`'s table lays out at **520px** with no
|
||
scroll container around it, and the other card tables do the same. The probe
|
||
reports the offending boxes by selector each run, and deliberately skips
|
||
anything inside a `position: fixed` subtree - the comments drawer is parked
|
||
off-screen by `translateX(100%)` and its five static children sit out at
|
||
`right: 844`, which made the first measurement blame the drawer. That is how
|
||
this entry got attributed to the wrong file once already; twice would be a
|
||
pattern.
|
||
**Not fixed here.** `T7.1` bundles nothing, and the fix is a layout decision -
|
||
a scroll container, a stacked card at narrow widths, or fewer columns - which
|
||
belongs with `T7.2` laying the form out again. `frame_check.py` **pins** the
|
||
current failure, so the check turns red the moment it is fixed and whoever
|
||
fixes it is told to close this entry.
|
||
- **Update, T1.2:** the 1440px half of this is **resolved as a side effect**, not by intent.
|
||
The unbreakable `#wp-usermenu` run that `T1.2` fixed was the cause of four of the five
|
||
overflows recorded in wave 0 — launcher, SOP wizard and field view at 390px, and the
|
||
creator at 1440px. Capture now reports overflow on 1 of 14 shots instead of 5. What
|
||
remains is the creator at **390px** (485px of content), which is its own layout rather
|
||
than the shared chrome. Left open so `T7.1` still checks it.
|
||
- **Update, T7.2 - the data-table attribution is spent as well, and the cause has
|
||
moved a third time.** T7.2 collapses every section at rest, and a collapsed card's
|
||
tables are `hidden` - they lay out nothing. The at-rest overflow is now
|
||
**scrollWidth 481 vs 390**, and the widest box is `help.js`'s `.help-tip::after`
|
||
tooltip, which is rendered (not `display:none`) even when idle and escapes its
|
||
16px badge to the right. The tables still overflow **when their section is
|
||
expanded** - that half of the T7.1 note stands and still belongs to a layout
|
||
decision (scroll container, stacked card, or fewer columns).
|
||
**Deliberately not fixed in T7.2:** the help tip is the `S8` component, rebuilt
|
||
whole at `T9.5` - a fix here would be thrown away with the component. `T9.5`
|
||
owns this entry now. `tests/form_structure_check.py` reports the measurement on
|
||
every run, and `tests/frame_check.py` keeps the failure pinned so the entry
|
||
cannot be closed by silence.
|
||
|
||
### BL-002 — `outline: none` appears three times in the wizard sheet, not once
|
||
|
||
- **Found during:** T0.1
|
||
- **Where:** `html/work-package-suite-styles.css:325`, `:347`, `:501`
|
||
- **What:** `A3`/`F5` cite the focus-ring removal at `322-328` only. The same
|
||
`outline:none` + pale 3px glow is repeated at `:347` (`.user-pick:focus`), and `:501`
|
||
(`.seq-step input.seq-label:focus`) removes the outline with **no** replacement at all,
|
||
which is a straight CLAUDE.md violation.
|
||
- **Why not now:** it is in scope for `T3.4`, not a separate item — recorded so the task
|
||
fixes all three rather than the one the review cited.
|
||
- **Suggested wave or follow-up:** fold into `T3.4`.
|
||
|
||
### BL-003 — User-menu links are 16px tap targets
|
||
|
||
- **Found during:** T1.2
|
||
- **Where:** `html/auth-guard.js:186-191` (`buildUserMenu`'s `link()`)
|
||
- **What:** every link in the app bar's user menu — including `Sign out` — renders 16px
|
||
tall, from `font:400 13px/1.2`. `T1.2` made them all reachable at 390px, but reachable is
|
||
not the same as comfortably tappable on the gloved-hands surface. Well under the usual
|
||
24–44px guidance.
|
||
- **Why not now:** `T1.2` is explicitly triage and `T2.2` replaces this markup with the
|
||
drawer, which has its own tap targets. Enlarging them here would change the 1440px layout
|
||
the task must leave byte-identical, and would be thrown away in wave 2.
|
||
- **Suggested wave or follow-up:** `T2.2` should ship the drawer with adequate targets;
|
||
`C1`'s audit at `T9.5` confirms it app-wide.
|
||
|
||
### BL-004 — `help.js` ships a 52-colour palette in a different design language
|
||
|
||
- **Found during:** T3.1
|
||
- **Where:** `html/help.js:79` (the injected `<style>`)
|
||
- **What:** the help centre injects its own stylesheet with **52 colour literals and zero
|
||
`var()`**. It is not a fourth copy of the suite palette — it is a different one: slate
|
||
(`#27313f`, `#334155`, `#e2e8f0`), violet (`#7c3aed`, `#f3e8ff`), its own blue
|
||
(`rgba(37,99,214,.15)`, see BL-008) and its own greys (`#fafbfc`, `#eef1f6`, `#f4f6f9`,
|
||
`#f7f8fa`). It loads on the launcher, SOP wizard, creator and field view.
|
||
- **Why not now:** `T3.2`'s contract is "no rendered change", and converting this palette is a
|
||
restyle, not a consolidation — it would change the help centre on four pages and break the
|
||
empty-screenshot-diff done-when. The token rule in `CLAUDE.md` does reach it, so it is real
|
||
work, not a non-issue.
|
||
- **Suggested wave or follow-up:** wave 9, alongside `C4`. Documented in
|
||
`docs/reference/tokens.md` §1.
|
||
|
||
### BL-005 — Two modals are styled entirely by inline `style=` attributes
|
||
|
||
- **Found during:** T3.1
|
||
- **Where:** `html/auth-guard.js:67-92` (change-password) and `html/wp-format.js:120-150`
|
||
(preferences)
|
||
- **What:** 35 raw colour literals between them — `#0f62fe`, `#8d8d8d`, `#e0e0e0`, `#defbe6`,
|
||
`#fff1f1`, `#0e6027`, `rgba(20,30,50,.5)` and so on — written into `style=` strings, so no
|
||
stylesheet can reach them and no token can either.
|
||
- **Why not now:** they are markup built by JS, not a stylesheet, so they are outside `T3.2`'s
|
||
four-sheet surface. Both dialogs are rebuilt as accessible components under `C1`.
|
||
- **Suggested wave or follow-up:** `T9.5`, with the `C1` audit.
|
||
|
||
### BL-006 — Seventeen half-pixel font sizes
|
||
|
||
- **Found during:** T3.1
|
||
- **Where:** `html/wp-creation-styles.css` (14) and `html/wp-chrome.css` (3)
|
||
- **What:** `9.5px`, `10.5px`, `11.5px`, `12.5px`, `13.5px` sit inside an otherwise integer
|
||
type scale of 27 distinct sizes. They round inconsistently between engines and there is no
|
||
reason for any of them.
|
||
- **Why not now:** retiring them moves text on every creator screen; `T3.2` forbids rendered
|
||
change and `T7.1` re-lays-out this page anyway.
|
||
- **Suggested wave or follow-up:** `T7.1`. See `docs/reference/tokens.md` §6a.
|
||
- **Re-measured at T7.1, unchanged.** `frame_check.py` counts **15** half-pixel
|
||
sizes in `wp-creation-styles.css` by `\d+\.5px`, which is the whole sheet
|
||
rather than the font-size subset this entry counted, so the two numbers are not
|
||
the same measurement and the difference is not a change. `T7.1` re-laid out the
|
||
page's chrome, not its type. Carried to `T7.2`, which lays out the form.
|
||
|
||
### BL-007 — `--radius: 0` is contradicted 45 times in the sheet that declares it
|
||
|
||
- **Found during:** T3.1
|
||
- **Where:** `html/wp-creation-styles.css:26` and 45 raw `border-radius` values in the same file
|
||
- **What:** the creator declares `--radius: 0` and honours it 23 times, then writes `2px 3px
|
||
4px 5px 6px 8px 9px 10px 12px 14px 20px 50%` directly in 45 other places, plus two
|
||
asymmetric CTA radii at `:707` and `:716`. Square corners are the Carbon idiom and the
|
||
intent everywhere else in the suite; this one sheet drifted.
|
||
- **Why not now:** changing 45 radii is the most visible diff available, and `T3.2` must
|
||
produce none.
|
||
- **Suggested wave or follow-up:** `T7.1`. See `docs/reference/tokens.md` §6c.
|
||
- **Re-measured at T7.1: 68, not 45.** `frame_check.py` counts raw
|
||
`border-radius:` declarations that do not resolve through a `var()`. The rise is
|
||
the counting method rather than 23 new radii - this entry counted values, the
|
||
probe counts declarations - but the direction is the point: nothing has reduced
|
||
it in four waves, and it is measured every run now instead of once. Carried to
|
||
`T7.2`.
|
||
|
||
### BL-008 — There is a second brand blue: `#2563d6`
|
||
|
||
- **Found during:** T3.1
|
||
- **Where:** `html/wp-creation-styles.css:565`, `html/help.js`, `html/wp-creation-app.js:1257`
|
||
- **What:** `.sop-inherited` — the highlight on every field a work package inherited from its
|
||
SOP — fills with `rgba(37,99,214,0.07)`, which is **`#2563d6`**, not the suite's `#0f62fe`.
|
||
`help.js` carries the same blue at `.15` alpha and the print window uses it solid for
|
||
headings. At 7% nobody has noticed, but "one accent colour" is not currently true even after
|
||
the four token systems collapse to one.
|
||
- **Why not now:** swapping it changes a rendered fill, which `T3.2` forbids. It is the same
|
||
conversation as the green action buttons.
|
||
- **Suggested wave or follow-up:** wave 9, with `C4`. `T3.5` is scoped to buttons; this is a field fill. See `docs/reference/tokens.md` §8-E.
|
||
|
||
### BL-009 — A ninth amber, four points from the eighth
|
||
|
||
- **Found during:** T3.2
|
||
- **Where:** `html/field.html:35` (`.pill.warn`)
|
||
- **What:** the field view's warn pill uses `#8a6d00`; every other warning text in the app is
|
||
`#8e6a00`. Four points apart, doing the same job, on the surface that is read through a
|
||
face shield. Almost certainly a typo rather than a decision — `field.html`'s inline `<style>`
|
||
was missed by the `T3.1` inventory, which is why it survived this long.
|
||
- **Why not now:** merging it moves a rendered colour, which `T3.2` forbids. `T3.2` named it
|
||
`--wp-status-warning-text-alt` so it is visible rather than hidden in a hex.
|
||
- **Suggested wave or follow-up:** wave 9, with `C4`. `T3.5` is scoped to buttons; this is a status pill. See `docs/reference/tokens.md` §8-K.
|
||
|
||
### BL-010 — 829 raw spacing, type and radius values remain inside rules
|
||
|
||
- **Found during:** T3.2
|
||
- **Where:** all five page stylesheets; 492 of them in `html/wp-creation-styles.css`
|
||
- **What:** `T3.2` removed every raw **colour** from the page sheets, but 483 spacing values,
|
||
281 font-sizes and 65 radii are still written literally in rules. The token *declarations*
|
||
are aliased — `--s1`…`--s6`, `--ctl`, `--radius`, `--mono`, `--sans` all resolve from
|
||
`theme-light.css` — but the rules that should consume them do not.
|
||
- **Why not now:** not effort — arithmetic. The creator's spacing is every integer from 1px to
|
||
14px, which is a histogram rather than a scale, so there is no token `padding: 9px 11px` maps
|
||
to without changing one of the two numbers. `T3.2` forbids changing a rendered value, so
|
||
tokenising these and honouring that constraint are mutually exclusive. This is the one `T3.2`
|
||
done-when not met, and it is recorded as not met rather than quietly skipped.
|
||
- **Suggested wave or follow-up:** `T5.x` and `T7.1`, where these pages are re-laid-out and the
|
||
values are being chosen again anyway. See `docs/reference/tokens.md` §6b and §11.
|
||
|
||
### BL-011 — Three JS-injected overlays race to append on the SOP page
|
||
|
||
- **Found during:** T3.2
|
||
- **Where:** `html/work-package-suite.html` — `#wp-sync-badge`, `.wp-navscrim`, `#wp-sidenav`
|
||
- **What:** the sync badge, the drawer scrim and the drawer are appended to `<body>` by three
|
||
different scripts after async work, so their DOM order varies run to run. Nothing is painted
|
||
differently — all three are `position: fixed` with their own `z-index` — but any test that
|
||
keys elements by sibling index sees dozens of phantom differences on this page. It cost real
|
||
time in `T3.2` before the cause was found, and `tests/token_check.py` now keys by identity
|
||
to avoid it.
|
||
- **Why not now:** invisible to users, and the fix is ordering in three separate scripts, which
|
||
is a change with no observable benefit while `T7.1` is still going to move this code.
|
||
- **Suggested wave or follow-up:** wave 9, if it is still true after `T7.1`.
|
||
|
||
### BL-012 — `admin.html` and the creator at 1440px are not stable enough to screenshot-diff
|
||
|
||
- **Found during:** T3.2
|
||
- **Where:** `tests/baseline_shots.py` output for `admin-390`, `admin-1440`, `creator-1440`
|
||
- **What:** the task brief's trap 2 says `admin.html` and `users.html` are not byte-stable.
|
||
Measured by capturing wave 2 against itself: **`users` is stable at both widths**, and the
|
||
unstable third is the **creator at 1440px** (344,272 px differ, bbox 288,14→1439,4924).
|
||
`admin` is worse than "live timestamps" suggests — its captured page *height* varies by about
|
||
600px between runs, so the two images cannot even be compared pixel-for-pixel.
|
||
- **Why not now:** the screenshots are a review aid, not a gate; `tests/token_check.py` now
|
||
covers what the diff was being asked to prove, and covers it better.
|
||
- **Suggested wave or follow-up:** wave 9, alongside `C2`. Either freeze the clock in the
|
||
fixture or exclude the live regions from capture — otherwise every later wave re-learns this.
|
||
|
||
### BL-013 — The creator's inputs have no visible focus ring at all
|
||
|
||
- **Found during:** T3.4
|
||
- **Where:** `html/wp-creation-styles.css:168` (`outline: none` on every input, textarea and
|
||
select) and `:171` (`:focus` replaces it with `box-shadow: 0 0 0 3px var(--accent-dim)`)
|
||
- **What:** the same defect `BL-002` recorded in the wizard sheet, in the sheet next door.
|
||
Measured in the browser with focus emulation on: a focused creator input reports
|
||
`outline-style: none`, and its only focus cue is a 3px `#edf5ff` glow against a `#ffffff`
|
||
field — a 1.05:1 edge. `.wp-nav-search:focus` (`:765`) is the same. That is `CLAUDE.md`'s
|
||
"outline: none without a replacement of at least equal visibility", on the page with the
|
||
most form controls in the app.
|
||
- **Why not now:** `T3.4`'s files are the SOP wizard stylesheet, and `BL-002` scoped the
|
||
three sites it folded in to that sheet. The creator is rebuilt at `T7.1`/`T7.2`.
|
||
- **Suggested wave or follow-up:** `T7.2`, or `T9.5` with the `C1` audit if it survives the
|
||
rebuild. The fix is the ring `T3.4` established: `outline: 2px solid var(--cds-focus);
|
||
outline-offset: -2px`, which `console.css`, `wp-chrome.css` and now the wizard all use.
|
||
- **CLOSED at T7.1 - it was already fixed, and this entry was stale.**
|
||
`wp-creation-styles.css:209` carries an `S12` comment naming this entry, and
|
||
`:219` sets exactly the ring prescribed above. So it was closed in **wave 4**,
|
||
by `S12`, and nobody came back to say so - the same way `BL-014`'s launcher half
|
||
turned out to be closed by `T4.7`.
|
||
Measured rather than read, twice: `frame_check.py` reports a focused creator
|
||
input as `outline solid 2px`, and `a11y_check.py` walks **120 focusable elements
|
||
on the creator** and finds every one of them ringing at 3:1 or better.
|
||
Worth saying plainly, because this entry was quoted as a live `CLAUDE.md`
|
||
violation while planning wave 7 and it had not been true for four waves: a
|
||
backlog entry is a claim with a date on it. Re-measure before acting on one.
|
||
The first thing `frame_check.py` does with focus is assert
|
||
`document.hasFocus()`, because an earlier draft called `page.call` instead of
|
||
`page.ws.call` inside a `try/except` and silently measured nothing at all -
|
||
which reported "no ring" for every control and looked exactly like a finding.
|
||
|
||
### BL-015 — The creator's stepper tabs are still forced uppercase
|
||
|
||
- **Found during:** T3.5
|
||
- **Where:** `html/wp-creation-styles.css:105` (`.step-tab`)
|
||
- **What:** `A5` scopes sentence case to buttons and field labels, and `T3.5` removed the
|
||
forced uppercase from both. `.step-tab` is neither — it is a stepper tab — so it was left,
|
||
and it is now the only uppercase interactive text on the page.
|
||
- **Why not now:** out of `A5`'s stated scope, and `A4`/`S9` rebuild the stepper.
|
||
- **Suggested wave or follow-up:** `T7.x`, with the stepper rebuild.
|
||
|
||
### BL-016 — Back to a URL with no `step` leaves the wizard on the step it was on
|
||
|
||
- **Found during:** T5.1
|
||
- **Where:** `html/work-package-suite-app.js`, the `WPUrl.onChange` handler
|
||
- **What:** the popstate handler reads `parseInt(state.step, 10)` and acts only when
|
||
the result is `>= 1`. Going from `?project=X&step=6` back to `?project=X` yields
|
||
`NaN`, so nothing happens and the wizard stays on step 6 while the address bar says
|
||
step 1. `T4.2`'s own probe never caught it because it moves between two URLs that
|
||
both carry a `step`, so the NaN branch was never taken. The fix is one expression —
|
||
treat a missing `step` as 1 — but it is `S3`'s code and `T4.2`'s done-whens, not
|
||
`A4`'s.
|
||
- **Why not now:** `CLAUDE.md` — do not fix what you notice in passing. `T5.1`'s rail
|
||
makes it easier to reach (ten keyboard-reachable buttons instead of ten chips) but
|
||
does not cause it, and folding an `S3` correction into an `A4` diff makes both
|
||
unreviewable.
|
||
- **Suggested wave or follow-up:** wave 9, with `C2`. `tests/stepper_check.py` pins the
|
||
current behaviour with a named check so the fix has a test waiting for it.
|
||
|
||
### BL-017 — The native-dialog baseline metric counts prose
|
||
|
||
- **Found during:** T5.1
|
||
- **Where:** `docs/reference/file-map.md` §4, metric 1
|
||
- **What:** the metric is `grep -ohE '\b(alert|confirm|prompt)\(' *.js *.html`, which
|
||
matches those words inside comments as readily as inside code. Four comments written
|
||
during `T5.1` — every one of them *about* removing a dialog — pushed the count from
|
||
80 to 82 while the task was deleting two real calls. They were reworded, but the next
|
||
person to explain a dialog in a comment will move the number again, and `T5.8` and
|
||
wave 9 both have to drive it to a target.
|
||
- **Why not now:** redefining a wave 0 baseline mid-plan is worse than the noise; the
|
||
count is comparable to itself as long as everyone measures it the same way.
|
||
- **Suggested wave or follow-up:** `T5.8`, which owns the wizard's count, should record
|
||
a comment-stripped figure alongside the raw one and state both. Wave 9 sets the
|
||
target against the stripped figure.
|
||
|
||
### BL-018 — The Work Package tab's gate is the last localStorage-derived status
|
||
|
||
- **Found during:** T5.3
|
||
- **Where:** `html/work-package-suite-app.js` — `restoreSavedSOP()` sets `sopComplete`,
|
||
`renderWPTab()` shows the gate or the creator on it
|
||
- **What:** `T4.1` moved the *launcher's* SOP status onto `/api/projects/{id}/summary`, and
|
||
`aggregates_check.py` proves the card reports the server's answer over a lying cache. The
|
||
SOP **wizard page** still decides whether to show the creator or the "complete the SOP
|
||
Configuration first" gate from `localStorage.wp_suite_sop_complete` plus a `wp_suite_state`
|
||
blob. `ProjectData.pullProject()` refreshes both from the server on load, so a signed-in
|
||
user with a working connection is fine — but the two answers come from different places,
|
||
and the fallback is silent rather than an error state, which is the shape `B4` objects to.
|
||
- **Also:** `project-data.js:210` writes `wp_suite_sop_complete = '1'` whenever
|
||
`/api/sops/latest` returns any row, including one whose `data` carries no `state`. The
|
||
wizard then holds a browser that believes the SOP is complete and has nothing to restore,
|
||
so `restoreSavedSOP()` bails and `sopComplete` stays false — the flag is written and never
|
||
read consistently. Found because `browser_check.py`'s fixture seeds exactly that shape, and
|
||
a pipeline-strip link consequently landed on the gate. `pipeline_check.py` seeds the
|
||
production shape (`{sop, state}`) instead.
|
||
- **Why not now:** `T5.3` is the strip. Changing which source the WP gate trusts changes what
|
||
the SOP wizard does when offline, and `B7`/`T7.1` dissolves that iframe and rewrites this
|
||
hand-off wholesale.
|
||
- **Suggested wave or follow-up:** `T7.1`, or wave 9 with `C2` if the gate survives the
|
||
rebuild unchanged. Either way `browser_check.py`'s fixture should adopt the `{sop, state}`
|
||
shape so it stops being the only place this discrepancy is visible.
|
||
- **Update, T7.1 - the gate survived, and this has now cost a fourth probe.**
|
||
The WP tab is no longer a frame swap, but it is still gated on `sopComplete`,
|
||
which is still `restoreSavedSOP()`'s answer. `frame_check.py` had every
|
||
creator route land on the gate panel until it seeded a readable SOP; it now
|
||
**imports `set_sop` from `sections_check.py`** rather than writing a fifth
|
||
copy, so the workaround is in one place and disappears when the fixture is
|
||
fixed. Four probes is enough evidence: `T9.9` owns it.
|
||
|
||
### BL-019 — A cost code that has left the list is silently blanked on edit
|
||
|
||
- **Found during:** T5.6
|
||
- **Where:** `html/wp-creation-app.js` — `buildCostCodes()` at `:185`, consumed by
|
||
`loadPackageIntoForm()`
|
||
- **What:** `wp_cost` is a `<select>` built from the hardcoded `COST_CODES` array, and
|
||
`loadPackageIntoForm` sets `.value` from the saved package. Setting `.value` to something
|
||
with no matching `<option>` does nothing at all — silently — so opening a package whose
|
||
cost code has since been removed from the array, or that was imported from elsewhere,
|
||
clears the field. The next save writes the blank back over the record.
|
||
- **The same class of bug was already fixed once**, for `gov_wosize` in
|
||
`work-package-suite-app.js:490-495`, by adding the stored value as an option when it is not
|
||
a preset. The comment there names the reason: "if a saved value isn't one of the presets,
|
||
add it as an option so the round-trip preserves it." Cost code never got the same treatment.
|
||
- **Found because** a `T5.6` probe used an invented cost code to prove that hiding a field
|
||
does not delete its value, and the value came back empty — which looked like the toggle
|
||
eating data and was not. The probe now uses a real code and says why.
|
||
- **Why not now:** `T5.6` hides two fields; it does not own how one of them round-trips, and
|
||
a fix here changes what is written back to existing records — which wants its own diff.
|
||
- **Suggested wave or follow-up:** wave 9. The fix is the four lines already written for
|
||
`gov_wosize`.
|
||
|
||
### BL-014 — Four controls fall back to the browser's default focus ring
|
||
|
||
- **Found during:** T3.4
|
||
- **Where:** `html/index.html` `.proj-row select`, `.proj-form-grid input`, `.link-like`;
|
||
`html/field.html` `.fld-search`
|
||
- **What:** these have no focus rule, so they get the UA default (`1px auto #111`). Visible,
|
||
so not a `C1` violation — but it is a fourth focus idiom beside the app's 2px `--cds-focus`
|
||
inset ring, and it does not follow the accent if the accent ever changes.
|
||
- **Why not now:** adding rings to the launcher and field view is outside `T3.4`, whose files
|
||
are the wizard stylesheet, and both surfaces are touched by later waves anyway.
|
||
- **Suggested wave or follow-up:** `T9.5`, with the `C1` audit.
|
||
- **Update, T5.2 — two of the four sites no longer exist.** `.proj-row select` and
|
||
`.link-like` went with the project-picker card (`B3`). The third, `.proj-form-grid input`,
|
||
survives in the rebuilt create form and was **measured rather than assumed**: with CDP
|
||
focus emulation on it draws `2px var(--cds-focus)` from the app-wide `:where()` floor
|
||
`T4.7` added, which post-dates this entry. So the launcher half of BL-014 is closed;
|
||
what is left is `field.html`'s `.fld-search`, and `T9.5` should re-measure that one the
|
||
same way rather than inheriting this entry's wording.
|
||
|
||
### BL-020 — Switching from the SOP wizard to the creator can now prompt to leave
|
||
|
||
- **Found during:** T7.1
|
||
- **Where:** `html/wp-autosave.js:96` (the `beforeunload` guard), reached from the
|
||
tool tabs in `html/work-package-suite.html`
|
||
- **What:** the Work Package Creation and Dashboard tabs used to swap an iframe
|
||
inside one document. Since `B7`/`T7.1` they are links to another document, so
|
||
leaving the wizard with unsaved SOP edits fires `T4.3`'s unsaved-work guard and
|
||
the browser asks whether to leave. The guard is behaving exactly as designed;
|
||
what changed is that a routine tab switch is now a page exit.
|
||
Nothing is lost either way - the guard writes the draft before prompting, and
|
||
`T4.3`'s recovery restores it on return - so this is friction, not data loss.
|
||
Note that `sopIsDirty()` compares against the fingerprint taken at load and at
|
||
`completeSOP()`, so **typing anything at all** makes the wizard dirty until the
|
||
SOP is completed. On a twelve-step form that is most of the time somebody spends
|
||
on it.
|
||
- **Why not now:** suppressing a deliberate guard for one navigation is a product
|
||
decision with a real downside - it is the same mechanism that stops a closed tab
|
||
losing work - and `T7.1` is forbidden to bundle anything. Found by
|
||
`frame_check.py`, which filters the console line rather than hiding it, and says
|
||
why in the comment.
|
||
- **Suggested wave or follow-up:** wave 9, with `C2`. If it is to be suppressed,
|
||
the honest version is an in-app navigation that flushes the draft and marks the
|
||
departure intentional, not a blanket disabling of the guard. If it is to be
|
||
kept, `T7.2`'s side navigation is the place to make saving obvious enough that
|
||
the prompt stops being a surprise.
|
||
|
||
### BL-021 — `project_sop_team()` reads a path `pushSOP` never writes
|
||
|
||
- **Found during:** T7.6
|
||
- **Where:** `server/app.py`, `project_sop_team()`
|
||
- **What:** the function reads `sop.data["project"]`, but `ProjectData.pushSOP`
|
||
stores every SOP row as `data = {sop: ..., state: ...}` — the project block
|
||
lives at `data["sop"]["project"]`. The lookup therefore always returns `[]`,
|
||
and the critical-constraint-reopened email (Phase S wave) has never actually
|
||
reached the PM or CM it names as recipients; only the owner and distribution
|
||
got it. Found while writing `project_qa_group()` for `CR-014`, which reads the
|
||
correct path (and tolerates the flat one for safety).
|
||
- **Why not now:** T7.6 is scoped to the QA gate; fixing another feature's
|
||
recipient list inside it is the drive-by CLAUDE.md forbids. The fix is one
|
||
line, but it deserves its own verification against the capture sink.
|
||
- **Suggested wave or follow-up:** wave 9 backlog sweep (`T9.9`), verified with
|
||
the `tests/qa_gate_check.py` sink pattern.
|
||
|
||
### BL-022 — F6's "roughly two screen heights": 2.17 against a strict 2.0
|
||
|
||
- **Found during:** T7.2, re-measured at the wave 7 exit
|
||
- **Where:** `html/wp-creation-index.html` page chrome; `tests/form_structure_check.py`
|
||
- **What:** the creator at rest measures **1,954px against a 900px viewport at
|
||
1440px** — 2.17 screens. `D3` amended `F6`'s criterion to "no single view
|
||
exceeds roughly two screen heights at rest"; the probe encodes "roughly two"
|
||
strictly as 2.0 and is red by ~154px. The remainder is page chrome, not form:
|
||
the context bar (~67px), the release banner (~45px + margin), and header/
|
||
toolbar spacing. The form itself went from 5,399px to this.
|
||
- **Why not now:** the criterion was amended once already, in writing (`D3`).
|
||
Deciding that 2.17 "is roughly 2" — or trimming chrome that other items placed
|
||
deliberately (`A2` made the banner the ONE warning; the context bar is the
|
||
SOP identity strip) — is a product call, not an implementation detail.
|
||
- **Suggested wave or follow-up:** needs Nick. Either bless 2.17 (one-line probe
|
||
change, criterion satisfied as written) or name the chrome to compress and it
|
||
becomes a small T9 task. The strict check stays red so the question cannot be
|
||
forgotten.
|
||
|
||
### BL-023 — Productivity factor: actual against estimated hours
|
||
|
||
- **Found during:** T9.2 (logged as that task's done-when requires)
|
||
- **Where:** future — dashboard / rollups
|
||
- **What:** Actual Hours is tracked (CR-017, deliberately kept) and estimated
|
||
hours exist on every package; nothing yet compares them. A productivity
|
||
factor (actual ÷ estimated, rolled up by discipline / building / type the way
|
||
CR-018 rolls cost) is the measurement Marlena's tracking exists to enable.
|
||
The rollup endpoints (`/api/wps/metrics`, `/api/projects/{id}/summary`)
|
||
already carry both sums, so this is a presentation task, not a data one.
|
||
- **Why not now:** new scope — needs its own item id per the working rules, and
|
||
a product conversation about where it displays and who reads it.
|
||
- **Suggested wave or follow-up:** next revision; needs Nick for placement.
|