diff --git a/docs/reference/file-map.md b/docs/reference/file-map.md index ae0da09..a2d7149 100644 --- a/docs/reference/file-map.md +++ b/docs/reference/file-map.md @@ -225,8 +225,14 @@ Wave 5 added more, for the same reason: ```bash python tests/stepper_check.py # A4/S9 — ten real buttons, keyboard operable 70 checks python tests/launcher_check.py # B3 — can a brand-new account get started? 58 checks +python tests/pipeline_check.py # B4 surface — server counts, shareable links 43 checks ``` +`pipeline_check.py` reads the dashboard's state out of the **iframe's DOM**, not its +globals: `dashFilter` and `currentView` are declared with `let` in a classic script, so +they are not properties of `window` and a cross-frame read of either comes back +`undefined` — which is indistinguishable from a filter that never applied. + `launcher_check.py` is the only probe that runs itself in **two subprocesses**, and both reasons are worth knowing before writing a third: diff --git a/docs/waves/backlog.md b/docs/waves/backlog.md index c607536..bbb5969 100644 --- a/docs/waves/backlog.md +++ b/docs/waves/backlog.md @@ -298,6 +298,32 @@ deliberately deferred. 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. + ### BL-014 — Four controls fall back to the browser's default focus ring - **Found during:** T3.4 diff --git a/html/index.html b/html/index.html index 742fceb..96ba243 100644 --- a/html/index.html +++ b/html/index.html @@ -317,6 +317,39 @@ .field-error { color: var(--cds-text-error); font-size: 12px; font-weight: 600; margin-top: 0.3rem; } .field-error:empty { display: none; } + /* PIPELINE STRIP — B4 surface (T5.3) + Four counts, every one of them from /api/wps/metrics. There is deliberately + no localStorage fallback anywhere in here: a per-browser number that looks + authoritative is the thing B4 removes, and a stale four beside a live four + is worse than an error. */ + .pipeline { margin-bottom: 1.5rem; } + .pipeline-head { font-size: 13px; font-weight: 600; text-transform: none; + color: var(--cds-text-secondary); margin-bottom: 0.6rem; } + .pipeline-strip { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 1px; + background: var(--cds-border-subtle); border: 1px solid var(--cds-border-subtle); } + .pipe-cell { display: flex; flex-direction: column; gap: 0.15rem; padding: 1rem 1.1rem; + background: var(--cds-layer); text-decoration: none; color: var(--cds-text-primary); + border-left: 3px solid transparent; transition: background 0.15s; } + .pipe-cell:hover { background: var(--cds-layer-hover); } + .pipe-num { font-size: 1.75rem; font-weight: 300; line-height: 1.1; } + .pipe-label { font-size: 13px; font-weight: 600; } + .pipe-sub { font-size: 12px; color: var(--cds-text-secondary); } + /* Each cell's accent says which slice it is, and the label says it in words — + the number alone is the same shape in all four (C1). */ + .pipe-cell.is-total { border-left-color: var(--cds-interactive-01); } + .pipe-cell.is-ready { border-left-color: var(--cds-support-success); } + .pipe-cell.is-onhold { border-left-color: var(--wp-status-warning-text); } + .pipe-cell.is-overdue { border-left-color: var(--cds-support-error); } + /* Zero is a real answer for one slice of a project that has work in it. Four + zeros on a project with no work packages at all is not a reading, it is a + broken-looking strip, so that case gets a sentence instead. */ + .pipe-empty, .pipe-error { grid-column: 1 / -1; background: var(--cds-layer); + padding: 1.1rem 1.2rem; font-size: 13px; color: var(--cds-text-secondary); } + .pipe-error { color: var(--wp-status-warning-text); } + .pipe-empty a { color: var(--cds-link-primary); } + .pipe-loading { grid-column: 1 / -1; background: var(--cds-layer); padding: 1.1rem 1.2rem; + font-size: 13px; color: var(--cds-text-secondary); font-style: italic; } + /* FIRST RUN A brand-new account has no projects, so it has no tool cards either — this is the whole page for that person, and it has to say what to do next. */ @@ -437,6 +470,16 @@ + + +