Wave 7 prep - record the Aug 18 decisions as D1-D10 and amend the waves

Nick answered 21 questions at the wave 6 exit and 11 follow-ups. Seven answers
are new build work, three amend acceptance criteria on tasks already scheduled,
and two close questions without work. None of it had an item ID, so none of it
could be built under CLAUDE.md's first rule.

New items D1-D10 in docs/waves/decisions-2026-08-18.md. A new prefix rather than
widened CR/F/S/A/B/C numbers - those are referenced in documents outside this
repo and CLAUDE.md forbids reinterpreting them. Every D entry names the item it
amends and quotes the criterion it replaces, so a reader of R2 can see what moved.

  D1  sample data returns to the creator                    B7, S7   T7.1
  D2  QA distribution list configured in the SOP            CR-014   T7.6
  D3  side navigation and collapsible sections, not tabs    F6       T7.2
  D4  Urgent surfaces the audited override, never bypasses  CR-003/A1 T7.3
  D5  usage data moves to the admin console                 B7       T7.10 (new)
  D6  material list uploads at SOP configuration            CR-013   T8.6  (new)
  D7  archived projects readable by project admins          B3, C1   T9.8  (new)
  D8  5MB a file, 2GB a project, PDFs and images, one DB    CR-007   T7.7
  D9  Ready for QA appears in Field View                    CR-014   T7.6
  D10 email switched on and off from the admin console      CR-011/14 T7.6, T8.3

Two decisions were mine to make and are recorded as such. D3: the written F6
criterion (no view over two screen heights) and the answer (one long form with
side nav) cannot both hold, so the criterion now reads 'at rest' and sections
collapse by default - tabs hide sections a first-time author does not know exist.
D8: keeping 5MB files in the same database means every encrypted backup carries
them; splitting them out was rejected because a backup without the drawings
cannot restore, so a 2GB per-project ceiling was approved instead.

Also corrected, not amended: CLAUDE.md and IMPLEMENTATION.md X2 both cited
wp-creation-app.js:1962-1972 as the protected logged-override path that T7.3 is
forbidden to remove. Those lines are deletePackage() and clearSaved(). The path
is confirmEarlyRelease() at :1002. Both documents now name it by function so the
reference survives the T7.1 rewrite that is about to move it.

Wave 9 gains T9.9, a sweep of the nine backlog entries that name wave 9 as their
home. Left unscheduled they surface at T9.7, which has no room to fix anything.
The four colour items in it (BL-004/005/008/009) are now approved work.

T9.5's help-tip count corrected from 15 to 18 and dated: three were added during
waves 5 and 6 by tasks reusing the component as designed, each unreachable for
the same reason. Scheduling a broken component late makes every reuse cost more.

Closed without work: the free-text location migration. Every location on record
is sample data because no real list has been loaded, so there is nothing to
migrate. Recorded with the condition that invalidates it - the first real
project - so it is a decision rather than a surprise.

Items: D1 D2 D3 D4 D5 D6 D7 D8 D9 D10
Task: T7.0 (wave 7 prep)

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-18 13:29:58 -05:00
parent d092db3920
commit 0dbc240900
6 changed files with 670 additions and 45 deletions

View File

@@ -5,13 +5,15 @@ any task, and read the wave file for the task you are on. Do not work from this
## The spec is the source of truth
Every change traces to an item ID (`CR-001`, `F1`, `S1`, `A1`, `B1`, `C1`). If you are about
Every change traces to an item ID (`CR-001`, `F1`, `S1`, `A1`, `B1`, `C1`, `D1`). If you are about
to make a change that has no ID, stop. Either it belongs to an existing item and you should
say which, or it is out of scope and should be logged in `docs/waves/backlog.md` instead of
built.
Do not renumber, merge, or reinterpret item IDs. They are referenced in documents outside
this repo that other people are reading.
this repo that other people are reading. New scope decided mid-build gets a **new** ID rather
than a widened old one - that is what the `D` prefix is for. See
`docs/waves/decisions-2026-08-18.md`.
## Scope discipline
@@ -51,7 +53,11 @@ These are recorded decisions, not oversights. Do not "clean them up":
- **Uppercase card headers in `console.css` stay (A5).** The sentence-case rule applies to
buttons and field labels only. The uppercase header idiom is deliberate.
- **The logged-override path for predecessors stays (A1).** It is an audited business rule,
not a bug. See `wp-creation-app.js:1962-1972`.
not a bug. It is `confirmEarlyRelease()` in `wp-creation-app.js`, called from the issue and
release paths. Named by function, not by line: this file and `IMPLEMENTATION.md` X2 both
cited `wp-creation-app.js:1962-1972` until Aug 18 2026, and those lines are
`deletePackage()`/`clearSaved()` - a different rule entirely. Corrected before T7.3, which
is the task told not to remove it.
- **Removed fields are hidden, not deleted (CR-002, CR-016).** Retain the data and the model.
Removal is expressed through the CR-006 section toggles.

View File

@@ -5,6 +5,9 @@ Companion document for humans: `WorkPackageSuite_Consolidated_Change_Request_R2.
55 items, 9 waves, dependency-ordered. Read `CLAUDE.md` first.
Ten further items (`D1`-`D10`) were decided on August 18 2026 and are recorded in
`docs/waves/decisions-2026-08-18.md`. 65 items total.
---
## 1. Where this came from
@@ -33,9 +36,26 @@ decision. Do not reopen these without a spec revision.
| `B7` | **Dissolve the creator iframe.** Scheduled as `T7.1`, the first task of wave 7. This is the largest engineering item in the plan and most of wave 7 depends on it. |
| `C1` | **Accessibility is in scope.** Fold it into components already being rebuilt rather than treating it as a separate pass. Rules are in `CLAUDE.md`. |
> If the `B7` decision was meant as "schedule the decision" rather than "dissolve the
> iframe", that is the one line in this plan to correct before wave 7 starts. Everything
> before wave 7 is unaffected either way.
> `B7` was confirmed as "dissolve the iframe" on August 14 2026 and again at the wave 7
> handoff. It is not a scheduling decision.
**A second set of decisions closed on August 18 2026**, at the wave 6 exit, in answer to
twenty-one questions and eleven follow-ups. They are recorded in
`docs/waves/decisions-2026-08-18.md` as `D1`-`D10`. They add three tasks and amend
acceptance criteria on seven others; they do not reopen anything above.
| ID | Decision | Amends |
|---|---|---|
| `D1` | Sample data returns to the creator, visible on the unframed page | `B7`, `S7` |
| `D2` | The QA distribution list is configured in the SOP, not per work package | `CR-014` |
| `D3` | The creator stays one page: side navigation plus collapsible sections | `F6` |
| `D4` | `Urgent` surfaces the audited override; it does not bypass the constraint gate | `CR-003`, `A1` |
| `D5` | Usage data moves to the admin console | `B7` |
| `D6` | The material list uploads at SOP configuration | `CR-013` |
| `D7` | Archived projects are readable, by project admins only | `B3`, `C1` |
| `D8` | 5MB a file, 2GB a project, PDFs and images, stored in the same database | `CR-007` |
| `D9` | `Ready for QA` appears in Field View | `CR-014` |
| `D10` | Email is switched on and off from the admin console | `CR-011`, `CR-014` |
## 3. Cross-track dependencies
@@ -45,7 +65,7 @@ be built as written, or cannot be built once, until something else lands.
| ID | Item | Depends on | Type | Why |
|---|---|---|---|---|
| `X1` | `CR-011`, `CR-014` | `S3` | **Blocking** | Both promise an email containing a direct link to the work package. There is no `pushState` anywhere in the app, so no work package has an addressable URL. The emails cannot be built until URL state exists. |
| `X2` | `CR-015` | `A1` | **Blocking** | The hold-not-clearing bug and the transition model are the same code. `wp-creation-app.js:1962-1972` shows a deliberate logged-override path, so hold state is not purely derived from open constraints. Fixing them separately means fixing it twice. |
| `X2` | `CR-015` | `A1` | **Blocking** | The hold-not-clearing bug and the transition model are the same code. `confirmEarlyRelease()` in `wp-creation-app.js` is a deliberate logged-override path, so hold state is not purely derived from open constraints. Fixing them separately means fixing it twice. **Corrected Aug 18 2026:** this row and `CLAUDE.md` both cited `wp-creation-app.js:1962-1972`, which is `deletePackage()`/`clearSaved()`. The override path is `confirmEarlyRelease()`, called from the issue and release paths. Named by function so it survives the `T7.1` rewrite. |
| `X3` | `CR-014` | `A1` | Sequencing | `Ready for QA` is a new state and must land inside the guarded transition model, not beside it. |
| `X4` | `CR-006` | `B7` | Sequencing | Section toggles must suppress sections inside the creator, which is an iframe child until `T7.1`. |
| `X5` | `CR-004`, `CR-018` | `B4` | **Blocking** | Rollup by building, floor and sector cannot come from `localStorage`. Same data dependency that blocks the pipeline strip. |
@@ -66,9 +86,9 @@ be built as written, or cannot be built once, until something else lands.
| 4 | Platform prerequisites | `docs/waves/wave-4.md` | `B4` `S3` `S2` `B5` `S10` `S11` `S12` |
| 5 | Launcher and SOP wizard | `docs/waves/wave-5.md` | `A4` `B3` `S9` `CR-005` `CR-006` `CR-002` `CR-016` `S1`(wizard) |
| 6 | Work package general information | `docs/waves/wave-6.md` | `CR-001` `CR-003` `CR-004` `CR-018` `A7` |
| 7 | The creator | `docs/waves/wave-7.md` | `B7` `A1` `CR-015` `A2` `A6` `CR-014` `CR-007` `B6` `S1`(creator) `F6` |
| 8 | Kitting and material | `docs/waves/wave-8.md` | `CR-009` `CR-010` `CR-011` `CR-012` `CR-013` |
| 9 | Verification and cleanup | `docs/waves/wave-9.md` | `CR-008` `CR-017` `S6` `S7` `C1` `C2` `C4` |
| 7 | The creator | `docs/waves/wave-7.md` | `B7` `A1` `CR-015` `A2` `A6` `CR-014` `CR-007` `B6` `S1`(creator) `F6` `D1` `D2` `D3` `D4` `D5` `D8` `D9` `D10` |
| 8 | Kitting and material | `docs/waves/wave-8.md` | `CR-009` `CR-010` `CR-011` `CR-012` `CR-013` `D6` `D10` |
| 9 | Verification and cleanup | `docs/waves/wave-9.md` | `CR-008` `CR-017` `S6` `S7` `C1` `C2` `C4` `D7` |
**Waves 1 through 4 produce almost no field-visible change.** That is deliberate and it is
roughly the first third of the effort. It is called out here because the Micron team is
@@ -132,6 +152,19 @@ Every one of the 55 items appears in exactly one wave, except where noted.
| CR-018 | 6 | S12 | 4 | C4 | 4 and 9 |
| | | S13 | 1 | | |
### The August 18 items
| Item | Wave | Task | Item | Wave | Task |
|---|---|---|---|---|---|
| D1 | 7 | T7.1 | D6 | 8 | T8.6 |
| D2 | 7 | T7.6 | D7 | 9 | T9.8 |
| D3 | 7 | T7.2 | D8 | 7 | T7.7 |
| D4 | 7 | T7.3 | D9 | 7 | T7.6 |
| D5 | 7 | T7.10 | D10 | 7 and 8 | T7.6, T8.3 |
That is 65 items across 9 waves. `D10` is the only one split across two waves: the gate and
its console control are built once at `T7.6` and reused by `T8.3`.
`F5` and `A3` are the same defect from two sources. `C1` and `C2` are standing requirements
enforced per task under `CLAUDE.md`, with a dedicated audit in wave 9. `CR-017` is a
do-not-remove guard rather than a build.
@@ -161,3 +194,11 @@ them; do not invent placeholder values in their absence.
Where a list is required and not yet supplied, build the upload path (`CR-005`) and seed with
an obviously-fake sample. Do not hardcode guessed floor names.
**Status, August 18 2026.** None of the four has arrived. Confirmed at the wave 6 exit that
this does not change the plan: `D6` extends the `CR-005` upload pattern to the material list
so `CR-013` is buildable without Nate's spreadsheet, and the B100 list is still expected
through the same door. Because no real list has ever been loaded, every location on record
is sample data - which is why the free-text migration question was closed without work
(`decisions-2026-08-18.md`, "Closed without work"). That answer does not survive the first
real project.

View File

@@ -0,0 +1,273 @@
# Decisions — August 18, 2026
Approved by Nick Siegfried during wave 7 planning, in answer to twenty-one questions raised
at the wave 6 exit and eleven follow-ups.
These are **new items**, not reinterpretations of existing ones. `CLAUDE.md` forbids
renumbering or reinterpreting `CR`/`F`/`S`/`A`/`B`/`C` IDs because they are referenced in
documents outside this repo, so everything decided here gets its own `D` prefix and each
entry names the existing item it amends. Where a `D` item changes an acceptance criterion,
the original criterion is quoted and the replacement is stated, so a reader of the R2
document can see exactly what moved.
Ten items. Seven are new build work; three amend criteria on work already scheduled.
---
## D1 — Sample data returns to the creator
- **Amends:** `B7` (`T7.1`), `S7` (`T9.4`)
- **Surface:** `html/`
`wp-creation-styles.css` opens with `body.embedded .embed-hide { display: none }` — framed,
the creator hides its own header, its sample-data controls and its analytics button. `T7.1`
dissolves the frame, so "framed" stops being a state and those three controls need a home or
a deletion. Decided: **the sample-data control comes back and is visible.**
That collides with `S7`, which reduces four sample-data affordances under three names to one.
It is not a contradiction: `S7`'s complaint is that there are four of them, that they are
named inconsistently, and that one sits a click from live project data. A single, clearly
named, confirmed affordance on the creator satisfies both. `T9.4` owns making it the only
one; `T7.1` owns making it visible.
**Done when:** the creator's sample-data control is reachable on the unframed page, and
`T9.4` still ends with exactly one affordance app-wide.
---
## D2 — The QA distribution list is configured in the SOP
- **Amends:** `CR-014` (`T7.6`)
- **Surface:** `server/` + `html/`
`CR-014` says entering `Ready for QA` "emails the assigned QA representative and the QA/QC
members named on the work package". Asked who should receive it, the answer was **the QA
group only, and the group is set up once during SOP configuration as a dropdown** — not typed
per work package.
That makes the recipient list SOP configuration, which means a wizard field, which means
`T7.6` acquires a dependency on the wave 5 SOP wizard it did not have.
Rejection routing was also settled: **a rejection notifies the work package owner and the
distribution list**, not only the person who submitted it.
**Replaces the `CR-014` done-when** "entering it sends email to the QA rep and named QA/QC
members" with:
- the QA distribution list is chosen in the SOP wizard from project members
- entering `Ready for QA` emails that list and nobody else
- rejection emails the work package owner and the same list
---
## D3 — The creator stays one page: side navigation plus collapsible sections
- **Amends:** `F6` (`T7.2`)
- **Surface:** `html/`
`T7.2` as written says "section tabs or an equivalent that shows one coherent group at a
time", with the done-when "no single view exceeds roughly two screen heights at 1440px".
Asked to choose, the answer was **one long form with navigation down the side** — and then,
when the height conflict was raised, the decision was delegated: *"do what you recommend is
best to UX."*
**Recommended and adopted:** one page, persistent side navigation, and each section
collapsible with only the current one open by default, plus an `Expand all` control for
people who would rather scroll straight through. Tabs were rejected because they hide
sections a first-time author does not know exist; an uncollapsed long form was rejected
because it is the ~4,700px page `F6` exists to fix.
**Replaces the `F6` done-when** "no single view exceeds roughly two screen heights at 1440px"
with "no single view exceeds roughly two screen heights at 1440px **at rest**" — that is,
with the default collapse state, which is the state the page is actually in when it loads.
Expanding everything deliberately is allowed to exceed it.
**Replaces** "toggled-off sections have no tab" with "toggled-off sections have no entry in
the side navigation", and "tabs are keyboard operable and follow the tab pattern with
`aria-selected` and arrow keys" with "the side navigation is keyboard operable, marks the
current section with `aria-current`, and each section header is a real disclosure button with
`aria-expanded`".
---
## D4 — Urgent surfaces the audited override; it does not bypass the gate
- **Amends:** `CR-003` (`T6.2`, shipped) and `A1` (`T7.3`)
- **Surface:** `html/`
Asked whether an `Urgent` package should be issuable while a constraint is still open, the
first answer was "maybe, with a constraint active". The concern raised back: silently
bypassing the gate would undermine the delay-documentation use case that is the entire
business justification for the constraint workflow — a constraint log that can be shown to
have been skipped stops supporting a notice of delay. **Answer: yes, surface the audited
path instead.**
So `Urgent` does not create a new bypass. It makes the existing logged-override path —
`confirmEarlyRelease()` — prominent on an urgent package, so the release still produces a
history entry with who, when and why.
**Adds to the `T7.3` done-when list:**
- an `Urgent` package with an open constraint offers the logged override in the primary
action position rather than behind a secondary control
- taking it still writes actor, timestamp and reason to history
- no code path releases a constrained package without writing that entry
---
## D5 — Usage data moves to the admin console
- **Amends:** `B7` (`T7.1`)
- **Surface:** `html/`
- **New task:** `T7.10`
`docs/reference/creator-frame.md` §2b records that five of the nine colliding script globals
are usage analytics implemented twice, and that the wizard's `showAnalytics` has no caller
because the button lives on the creator. Asked where it belongs now that the creator is a
page in its own right, the answer was **the admin console**.
That deletes one of the two implementations rather than merging them, and takes an
operator-facing control off a package-authoring screen.
**Done when:** analytics is reachable from `admin.html`, no analytics control remains on the
creator or the wizard, and only one of the two implementations survives.
---
## D6 — The material list uploads at SOP configuration
- **Amends:** `CR-013` (`T8.5`)
- **Surface:** `server/` + `html/`
- **New task:** `T8.6`
`CR-013` is explicit that there is no parts catalog: line items are free text, because
Nate's spreadsheet and the master material workbook have not been supplied
(`IMPLEMENTATION.md` section 8). Asked whether to wait for them or build the upload path now,
the answer was **upload now**.
This is `CR-005` applied to a second list. The location upload built at `T5.4` — paste or
file, validated, rejected rows reported with the source line, editable afterwards — is the
pattern to reuse rather than re-invent.
**This does not open the out-of-scope parts catalog.** Section 7 defers "full material request
and inventory system, including the parts catalog and warehouse integration". A project-scoped
list uploaded by the project is not a catalog, carries no inventory levels and integrates
with nothing. If the diff grows a stock count, it has crossed the line.
**Done when:** a material list uploads during SOP configuration, `T8.5` line items select
from it, free text still works when the list is empty, and no inventory or warehouse field
exists in the diff.
---
## D7 — Archived projects are readable, by project admins
- **Amends:** `B3` (`T5.2`, shipped), `C1`
- **Surface:** `server/` + `html/`
- **New task:** `T9.8`
Archiving exists for both projects and work packages, and the server already refuses writes
to an archived project. What it does not do is show them: an archived project leaves the
launcher and there is no way back to it. Asked who should be able to open one, the answer
was **project admins only**, arrived at after considering everyone.
So this is a listing and a read-only render behind a role check, not a new archive
mechanism.
**Done when:** a project admin can list and open archived projects, everything renders
read-only, a non-admin sees no archived projects at all, and no write path is reachable from
the read-only view.
---
## D8 — Upload limits: 5MB a file, 2GB a project, PDFs and images, one database
- **Amends:** `CR-007` (`T7.7`)
- **Surface:** `server/` + `html/`
Four answers, taken together:
| Question | Answer |
|---|---|
| Per-file size limit | 5MB |
| Accepted types | PDFs and images |
| Where files live | the same database as everything else |
| What caches offline | only the work packages assigned to that person |
The follow-up was the consequence: 5MB files in the same database means every encrypted
backup carries them, and a busy project reaches a gigabyte without anyone noticing until a
restore is slow. Splitting the files out was **rejected** on the grounds that a backup which
excludes the drawings is a backup you cannot restore from. **A per-project ceiling of 2GB was
approved instead**, with a warning before it is reached.
**Adds to the `CR-007` done-when list:**
- a file over 5MB is refused before upload, with the limit stated
- a type outside PDF and image is refused before upload, with the accepted types stated
- a project at 80% of 2GB warns, and at 2GB refuses with a message naming the ceiling
- the running project total is visible where uploads happen
- offline caching covers the requesting user's assigned packages, not the whole project
---
## D9 — Ready for QA appears in Field View
- **Amends:** `CR-014` (`T7.6`)
- **Surface:** `html/`
`CR-014` puts the new state on "a dashboard view or filter". Asked whether the field needs to
see it too, the answer was **yes, it shows in field view**.
**Adds to the `T7.6` done-when list:** Field View shows which packages are in `Ready for QA`,
and the state is legible at 390px without colour being the only signal.
---
## D10 — Email is switched on and off from the admin console
- **Amends:** `CR-011` (`T8.3`), `CR-014` (`T7.6`)
- **Surface:** `server/` + `html/`
All new outbound email in this plan ships **gated off** — a standing instruction from the
wave 5 kickoff, verified against a capture sink rather than a real mail server. Asked who
turns it on, the answer was **the admin controls email**.
That makes the gate a stored setting with a console control, not an environment variable
somebody edits on the server. Existing hardening constraints stand: the SMTP password lives
in the environment and nowhere else, and no customer IP appears in an email body.
**Done when:** the send path is complete and exercised against a capture sink, the default is
off, an administrator can turn it on from `admin.html`, a non-administrator cannot, and
changing it is written to the audit log.
---
## Closed without work
**Free-text location migration.** Work packages created before `CR-004` recorded their
location as typed text, and those values match nothing in the structured list, so they do not
roll up. The question was whether somebody should match them by hand or leave them as
history. Answer: *"It is just sample data at this point so that is ok."* No migration is
built and no item is opened. If this plan ever runs against a database with real
pre-`CR-004` packages in it, this decision has to be revisited before `T6.4`'s rollups can be
trusted — noted here so that is a decision and not a surprise.
**The off-palette colours.** `BL-004`, `BL-005`, `BL-008` and `BL-009` were each deferred by
their own wave on the same grounds: fixing them changes a rendered colour, and the wave that
found them was forbidden to change one. Together they are the help centre's separate
52-colour palette, two dialogs styled by inline attributes, a second brand blue and a ninth
amber. Asked whether to unify them or leave them, the answer was **change them**. They stay
in wave 9 under `C4`, but they are now approved work rather than four open questions.
---
## What this changes in the wave table
| Wave | Was | Now also |
|---|---|---|
| 7 | `B7` `A1` `CR-015` `A2` `A6` `CR-014` `CR-007` `B6` `S1`(creator) `F6` | `D1` `D2` `D3` `D4` `D5` `D8` `D9` `D10`(half) |
| 8 | `CR-009` `CR-010` `CR-011` `CR-012` `CR-013` | `D6` `D10`(half) |
| 9 | `CR-008` `CR-017` `S6` `S7` `C1` `C2` `C4` | `D7`, and `C4` now carries the four approved colour items |
Three new tasks — `T7.10`, `T8.6` and `T9.8`. Everything else is an amended criterion on a
task that already existed.

View File

@@ -1,20 +1,29 @@
# Wave 7 — The creator
**Items:** `B7`, `A1`, `CR-015`, `A2`, `A6`, `CR-014`, `CR-007`, `B6`, `S1` (creator), `F6`
**Items:** `B7`, `A1`, `CR-015`, `A2`, `A6`, `CR-014`, `CR-007`, `B6`, `S1` (creator), `F6`,
and from August 18: `D1`, `D2`, `D3`, `D4`, `D5`, `D8`, `D9`, `D10`
**Depends on:** wave 6 merged, and `T4.2` (URL state) merged
**Why last among the build waves:** most of it depends on the iframe decision, and `CR-014`
depends on `X1`.
`T7.1` is the largest engineering item in the plan. Decided and approved: the iframe gets
dissolved. Do it first everything after it is cheaper on the far side.
dissolved. Do it first - everything after it is cheaper on the far side.
**Read `docs/waves/decisions-2026-08-18.md` before starting.** Eight of its ten items land in
this wave, four of them as changes to done-when lists written below. Where a task carries an
`Amended` block, that block wins over the text above it.
**Read `docs/reference/creator-frame.md` before `T7.1`.** It is the measurement of the
boundary: 21 colliding stylesheet selectors, 9 colliding script globals, 0 colliding markup
ids, 28 cross-frame call sites, and the three questions no count can answer.
---
### T7.1 — B7: Dissolve the creator iframe
- **Items:** `B7`
- **Items:** `B7`, `D1`
- **Depends on:** T6.5
- **Blocks:** T7.2 through T7.9
- **Blocks:** T7.2 through T7.10
- **Surface:** `html/`
- **Files:** parent page hosting the creator, the creator itself, cross-frame messaging
@@ -32,6 +41,28 @@ reviewable as a structural change.
**Do not:** bundle any other item into this task, and do not leave a dormant iframe path
behind a flag.
**Amended August 18 (`D1`, `D5`).** `creator-frame.md` section 4 lists three questions this
task has to settle. Two are now answered:
- **Does the creator get the app bar back?** Yes. It is the only page in `html/` that loads
neither `wp-chrome.css` nor `wp-chrome.js`, and a page without the app bar is not a page.
Expect to redo the wizard's layout arithmetic - `chromeHeight()`, `--wp-chrome-h`, the
`embed-full` sizing.
- **What happens to `body.embedded`?** It goes, and with it `.embed-hide`. The three controls
it was hiding: the creator's own header stays hidden because the app bar replaces it; the
**sample-data control becomes visible** (`D1`); the analytics button stays visible here and
**moves to the admin console at `T7.10`** (`D5`). Do not delete it in this task - a task
that both dissolves the frame and deletes a feature is two diffs.
- **One sequence component or two?** Still open, and still this task's call. Nine of the 21
colliding selectors are the sequence editor. The wizard authors the sequence and the creator
renders it; confirmed August 18 that those stay two adjustable things, which argues for
scoping rather than merging. If you scope, say so and leave `BL-015` open for wave 9.
**Do not** fold `BL-001`, `BL-006`, `BL-007` or `BL-013` into this diff. They are logged
against this file and they are cheaper now than ever, and they are still separate items. What
this task owes them is a **measurement**: record whether each still reproduces after the
rebuild, so `T7.2` and `T9.5` inherit a fact rather than an assumption.
**Done when:**
- [ ] no iframe remains in the creator; grep confirms
@@ -40,12 +71,16 @@ behind a flag.
- [ ] `CR-006` section toggles propagate into the creator with no special-casing
- [ ] browser back and forward still behave with the `T4.2` URL state
- [ ] no cross-frame messaging code remains
- [ ] the creator renders the standard app bar, and the wizard's chrome arithmetic still holds at 390px and 1440px
- [ ] `body.embedded` and `.embed-hide` are gone; grep confirms
- [ ] the sample-data control is visible and reachable on the creator (`D1`)
- [ ] `BL-001`, `BL-006`, `BL-007` and `BL-013` are each re-measured after the rebuild and their entries updated with the result
---
### T7.2 — F6 + A4 pattern: Section tabs replace the 4,700px form
### T7.2 — F6 + D3: Side navigation and collapsible sections replace the 4,700px form
- **Items:** `F6`
- **Items:** `F6`, `D3`
- **Depends on:** T7.1, T5.5, T3.5
- **Blocks:** T7.9
- **Surface:** `html/`
@@ -58,24 +93,41 @@ jump links standing in for structure.
group at a time. Respect the `CR-006` toggles: a section toggled off has no tab. Retire the
jump links. Section identity belongs in the URL (`T4.2`) so a section is linkable.
**Amended August 18 (`D3`) - the answer is not tabs.** Asked to choose, the decision was one
page with navigation down the side; asked about the height conflict that creates, the call was
delegated. Adopted: **one page, persistent side navigation, sections collapsible, only the
current one open by default, plus an `Expand all`.** Tabs were rejected because they hide
sections a first-time author does not know exist. An uncollapsed long form was rejected
because it is the 4,700px page `F6` exists to fix. The done-when list below is the amended
one; it replaces four of the six entries as written.
`BL-013` is folded in here and only here: the creator's inputs have `outline: none` with a
1.05:1 glow as their only focus cue, which is a standing `CLAUDE.md` violation on the page
with the most form controls in the app. A rebuilt form ships the `T3.4` ring or it is not
done.
**Done when:**
- [ ] no single view exceeds roughly two screen heights at 1440px
- [ ] toggled-off sections have no tab
- [ ] no single view exceeds roughly two screen heights at 1440px **at rest** - that is, in the default collapse state the page loads in
- [ ] toggled-off sections have no entry in the side navigation
- [ ] each section is addressable by URL and survives refresh
- [ ] jump links are gone
- [ ] tabs are keyboard operable and follow the tab pattern with `aria-selected` and arrow keys
- [ ] the side navigation is keyboard operable and marks the current section with `aria-current`
- [ ] each section header is a real `<button>` with `aria-expanded`, not a `<div>` with a click handler
- [ ] `Expand all` and its inverse exist and are keyboard reachable
- [ ] deep-linking to a collapsed section expands it
- [ ] unsaved work is preserved when moving between sections (`T4.3`)
- [ ] every focusable control on the page draws a ring of at least 3:1 against its background (`BL-013`)
---
### T7.3 — A1 + CR-015: Guarded transitions and the hold bug
- **Items:** `A1`, `CR-015`
- **Items:** `A1`, `CR-015`, `D4`
- **Depends on:** T7.1
- **Blocks:** T7.4, T7.6
- **Surface:** `server/` + `html/`
- **Files:** status handling, constraint handling, `wp-creation-app.js:1962-1972`
- **Files:** status handling, constraint handling, `confirmEarlyRelease()` in `wp-creation-app.js`
**This is one task, not two (`X2`).** The hold-not-clearing bug and the transition model are
the same code. `CR-015` is the highest-severity item in the plan and it was reproduced live
@@ -85,9 +137,15 @@ in front of the Micron team.
derived from open constraints, which is why it goes stale.
**Do:** Model transitions explicitly. For each state, define the primary next action **plus
an audited override**. "One legal next action" is too narrow `wp-creation-app.js:1962-1972`
contains a deliberate logged-override path for predecessors, and Issue (Hold) is a branch
from several states, not a single linear step.
an audited override**. "One legal next action" is too narrow - `confirmEarlyRelease()` in
`wp-creation-app.js` is a deliberate logged-override path for predecessors, and Issue (Hold)
is a branch from several states, not a single linear step.
**Corrected August 18.** This task's file list, `IMPLEMENTATION.md` `X2` and `CLAUDE.md` all
cited `wp-creation-app.js:1962-1972` as the protected override path. Those lines are
`deletePackage()` and `clearSaved()` - a different rule entirely. The path to preserve is
`confirmEarlyRelease()`, called from the issue and release paths. All three documents now name
it by function so the reference survives this rebuild.
Derive hold state from open constraints rather than storing it independently, or if it must
be stored, recalculate it on every constraint change. Write every hold and release to the
@@ -107,6 +165,17 @@ the symptom by recalculating on page load only.
- [ ] a regression test covers the clear-last-constraint path specifically
- [ ] the exact root cause is stated in the PR
**Amended August 18 (`D4`) - what `Urgent` does.** Asked whether an `Urgent` package should be
issuable with an open constraint, the first answer was "maybe". The concern raised back was
that a silent bypass destroys the delay-documentation use case that justifies the whole
constraint workflow: a constraint log that can be shown to have been skipped stops supporting
a notice of delay. Confirmed: **surface the audited path, do not add a new one.**
- [ ] an `Urgent` package with an open constraint offers `confirmEarlyRelease()` in the primary action position, not behind a secondary control
- [ ] taking it still writes actor, timestamp and reason to history
- [ ] no code path releases a constrained package without writing that entry; verify by grep and by driving it
- [ ] a `Normal` or `High` package is unchanged - the override is no more prominent than it is today
**Why the history matters:** the delay-documentation use case is the business justification
for the whole constraint workflow. If a constraint log can be shown to have been wrong, it
stops supporting a notice of delay or a change order claim.
@@ -161,8 +230,8 @@ They open it on a phone and need the answer without scrolling or clicking.
### T7.6 — CR-014: Ready for QA gate
- **Items:** `CR-014`
- **Depends on:** T7.3, T4.2 (blocking dependency `X1`)
- **Items:** `CR-014`, `D2`, `D9`, `D10`
- **Depends on:** T7.3, T4.2 (blocking dependency `X1`), and the wave 5 SOP wizard for the `D2` field
- **Blocks:** nothing
- **Surface:** `server/` + `html/`
- **Files:** status model, notification service, QA section
@@ -183,15 +252,35 @@ reject; rejection returns the package to In Progress with a required comment.
**Do not:** build the email with a link to the app root. That is the failure `X1` exists to
prevent.
**Amended August 18 (`D2`, `D9`, `D10`).** Three answers change this task:
- **Who receives it.** Not "the QA rep and the QA/QC members named on the work package" -
**the QA group, configured once during SOP setup as a dropdown of project members.** That
makes the recipient list SOP configuration, so this task acquires a wizard field and a
dependency on wave 5 that it did not have.
- **Who hears about a rejection.** The **work package owner and the same distribution list**,
not only the submitter.
- **Where the state is visible.** The dashboard **and Field View**.
- **Who turns email on.** An administrator, from `admin.html`. The gate is a stored setting,
not an environment variable. It ships **off** and is verified against a capture sink; no
real mail is sent from this branch.
**Done when:**
- [ ] the new state exists in the transition model, not beside it
- [ ] entering it sends email to the QA rep and named QA/QC members
- [ ] the QA distribution list is chosen in the SOP wizard from project members and persists
- [ ] entering `Ready for QA` emails that list and nobody else
- [ ] rejection emails the work package owner and the same list
- [ ] the link in the email opens that specific work package, including for a signed-out recipient via login
- [ ] a dashboard view or filter lists all packages in Ready for QA
- [ ] Field View shows which packages are in `Ready for QA`, legibly at 390px, with something other than colour carrying the state (`D9`)
- [ ] rejection returns to In Progress and requires a comment
- [ ] the transition is written to history
- [ ] email failure is logged and surfaced, not silent
- [ ] the send path is exercised end to end against a capture sink, with the count of captured messages asserted
- [ ] the default is off; an administrator can turn it on from `admin.html` and a non-administrator cannot (`D10`)
- [ ] changing the setting is written to the audit log
- [ ] no SMTP password appears anywhere outside the environment, and no customer IP appears in a message body
**Note:** QA currently runs off a comprehensive Excel questionnaire being migrated into
CxAlloy. Integration with CxAlloy is explicitly out of scope. This gate is notification only.
@@ -200,7 +289,7 @@ CxAlloy. Integration with CxAlloy is explicitly out of scope. This gate is notif
### T7.7 — CR-007: Upload drawings for offline access
- **Items:** `CR-007`
- **Items:** `CR-007`, `D8`
- **Depends on:** T7.2, T4.3 (blocking dependency `X8`)
- **Blocks:** nothing
- **Surface:** `server/` + `html/`
@@ -221,14 +310,31 @@ Show file size limits and supported types in the UI before upload, not after fai
**Do not:** build this before `T4.3`. Uploading into a form with no autosave risks losing
both the upload and the surrounding work.
**Amended August 18 (`D8`) - the numbers.** `CR-007` says "show file size limits and supported
types" without saying what they are. They are: **5MB a file, PDFs and images, stored in the
same database as everything else.** Offline caching covers **only the packages assigned to
the requesting user**, not the whole project.
The consequence was raised and settled rather than discovered later: files in the database
means every encrypted backup carries them. Splitting them out was rejected - a backup that
excludes the drawings is a backup you cannot restore from - so a **2GB per-project ceiling**
was approved instead, with a warning at 80%.
**Done when:**
- [ ] PDF upload works and the file is retrievable
- [ ] image upload works and the file is retrievable
- [ ] links still work alongside uploads
- [ ] attachments are included in export or download
- [ ] an uploaded drawing opens on a tablet with the network disabled
- [ ] offline caching covers the requesting user's assigned packages only; verify a package assigned to someone else is not cached
- [ ] the description field persists and prints on the export
- [ ] limits and supported types are stated before upload
- [ ] a file over 5MB is refused before upload, naming the limit
- [ ] a type outside PDF and image is refused before upload, naming the accepted types
- [ ] a project at 80% of 2GB warns; at 2GB it refuses, naming the ceiling
- [ ] the running project total is visible where uploads happen
- [ ] both limits are enforced on the server, not only in the browser
- [ ] upload failure is clear and recoverable, with no loss of surrounding form work
- [ ] the whole flow works at 390px
@@ -287,11 +393,47 @@ errors is marked. Remove the creator's native dialogs.
---
### T7.10 — D5: Usage data moves to the admin console
- **Items:** `D5`
- **Depends on:** T7.1
- **Blocks:** nothing
- **Surface:** `html/`
- **Files:** `admin.html`, `admin.js`, `wp-creation-app.js`, `work-package-suite-app.js`
**Problem:** Usage analytics exists **twice**. `creator-frame.md` section 2b counts five of
the nine colliding script globals as this one feature - `ANALYTICS_KEY`, `analyticsLoad`,
`analyticsSave`, `downloadAnalytics`, `showAnalytics` - and records that the wizard's copy has
no caller, because the button that opens it lives on the creator. Once the frame is gone, two
implementations sit in one document.
**Do:** Keep one implementation and put its entry point in the admin console, where an
operator-facing report belongs. Remove the analytics control from the creator and from the
wizard. This is not a rewrite of what analytics collects - the same data, reached from one
place instead of a button on a package-authoring screen.
**Do not:** change what is tracked, and do not leave the second implementation behind
unreferenced. An unreferenced duplicate is what produced this item.
**Done when:**
- [ ] usage data is reachable from `admin.html`
- [ ] no analytics control remains on the creator or the wizard; grep confirms
- [ ] exactly one implementation of `analyticsLoad`/`analyticsSave`/`downloadAnalytics` survives; grep confirms
- [ ] the data recorded before this task is still readable after it
- [ ] the console entry point is behind the same role check as the rest of `admin.html`
- [ ] the report is usable at 390px
---
## Wave 7 exit criteria
- [ ] no iframe; `F4` resolved structurally
- [ ] `CR-015` fixed with a regression test and a stated root cause
- [ ] hold state is derived, and history records every transition
- [ ] the QA gate emails a working deep link
- [ ] drawings upload and open offline on a tablet
- [ ] the QA gate emails a working deep link, to the SOP-configured list, with the gate off by default
- [ ] drawings upload and open offline on a tablet, inside the 5MB and 2GB limits
- [ ] creator native dialog count is 0
- [ ] the creator is a page: app bar, side navigation, collapsible sections, focus rings, at rest under two screen heights
- [ ] `D1` through `D5`, `D8`, `D9` and `D10` are each satisfied or explicitly carried with a reason
- [ ] no real email left this branch; the capture sink count is recorded

View File

@@ -1,7 +1,12 @@
# Wave 8 — Kitting and material
**Items:** `CR-009`, `CR-010`, `CR-011`, `CR-012`, `CR-013`
**Items:** `CR-009`, `CR-010`, `CR-011`, `CR-012`, `CR-013`, and from August 18: `D6`, `D10`
**Depends on:** wave 7 merged
**Build order is not task order.** `T8.6` is numbered last because it was added on August 18,
but it **blocks `T8.5`** - the request cannot select from a list that does not exist yet. Build
`T8.1`, `T8.2`, `T8.3`, `T8.4`, `T8.6`, then `T8.5`. Task IDs are referenced outside this repo
and are not renumbered to make the order read left to right.
**Decision, approved Aug 14 2026:** keep and extend Kitting, then ship it **toggled off** for
the Micron EUV SOP. Kitting is not happening at Micron today — it is complicated by
journeyman ratio and warehouse cost — but the team expects to get there, and prefab arguably
@@ -59,12 +64,17 @@ everything assigned to them.
- [ ] the dashboard filters by warehouse owner
- [ ] removing someone from the project does not break existing work packages referencing them
**Confirmed August 18.** Asked whether the warehouse owner belongs to the project or to each
work package, the answer was **on the work package**. A project default is fine as a
convenience, but the field that notifications read is the one on the package, so a package
retargeted to a different warehouse notifies the right person without touching the project.
---
### T8.3 — CR-011: Kitting status change notification
- **Items:** `CR-011`
- **Depends on:** T8.2, T4.2 (blocking dependency `X1`)
- **Items:** `CR-011`, `D10`
- **Depends on:** T8.2, T7.6 (the email gate), T4.2 (blocking dependency `X1`)
- **Blocks:** nothing
- **Surface:** `server/` + `html/`
- **Files:** notification service, kitting status handling
@@ -87,6 +97,8 @@ convention, not two.
- [ ] formatting and tone match the existing status-change email
- [ ] rapid consecutive changes do not produce a burst of near-identical emails
- [ ] send failures are logged and surfaced
- [ ] the gate built at `T7.6` is reused, not re-implemented; kitting email is off by default and turned on from `admin.html` (`D10`)
- [ ] the send path is exercised against a capture sink with the captured count asserted; no real mail leaves this branch
---
@@ -118,8 +130,8 @@ printout.
### T8.5 — CR-013: Lightweight material request
- **Items:** `CR-013`
- **Depends on:** T8.4, T7.2 (sequencing dependency `X6`)
- **Items:** `CR-013`, `D6`
- **Depends on:** T8.4, T8.6, T7.2 (sequencing dependency `X6`)
- **Blocks:** nothing
- **Surface:** `server/` + `html/`
- **Files:** Materials section, request model, dashboard
@@ -145,20 +157,72 @@ delivery location and status.
card on a 4,700px form. And do not invent a parts list — Nate's spreadsheet and the master
material workbook have not been supplied yet (see `IMPLEMENTATION.md` section 8).
**Amended August 18 (`D6`).** Two answers change this task. Line items **select from a
project material list uploaded during SOP configuration** (`T8.6`) rather than being typed
from nothing - and free text still works when no list has been loaded, which is the state
every project is in today. And submitting a request **notifies the warehouse owner named on
the work package** (`CR-010`), which is the routing that replaces the informal funnel through
one person.
`D6` does not open the deferred parts catalog. A project-scoped list the project uploaded is
not a catalog: no inventory levels, no warehouse integration, no shared master data. If a
stock count appears in the diff, it has crossed the line `IMPLEMENTATION.md` section 7 draws.
**Done when:**
- [ ] line items can be added, edited and removed, with quantity and unit
- [ ] line items select from the `T8.6` list when one exists, and accept free text when it does not
- [ ] needed-by date, requestor, delivery location and status all persist
- [ ] requests appear on the dashboard, filterable by location and status
- [ ] submitting a request notifies the warehouse owner named on the work package
- [ ] no parts catalog, inventory count or warehouse integration exists in the diff
- [ ] the section respects `CR-006` toggles
- [ ] the whole flow works at 390px, since requests originate in the field
---
### T8.6 — D6: Upload the material list at SOP configuration
- **Items:** `D6`
- **Depends on:** T8.1
- **Blocks:** T8.5
- **Surface:** `server/` + `html/`
- **Files:** SOP wizard, material list model and routes, `T5.4`'s upload component
**Intent:** `CR-013` was written to accept free text because Nate's spreadsheet and the master
material workbook had not been supplied, and they still have not been. Asked on August 18
whether to wait for them, the answer was **build the upload path now**, the same call that was
made for the location list at `CR-005`.
**Do:** Reuse `T5.4`. The location upload already does paste-or-file, validates, reports
rejected rows with the source line number, and stays editable afterwards. Build the material
list the same way and against the same component, not beside it.
Keep the field set small and obvious: description, unit, and an optional code. That is what
`T8.5` needs to populate a line item.
**Do not:** add inventory levels, stock counts, pricing or a warehouse identifier. Do not make
the list mandatory - a project with no list must still be able to raise a request. Do not
hardcode sample part numbers; seed with an obviously-fake sample as `CR-005` does.
**Done when:**
- [ ] a material list uploads or pastes during SOP configuration and persists per project
- [ ] rejected rows are reported with the source line and a reason
- [ ] the list is editable after upload, and entries can be deactivated rather than deleted
- [ ] `T8.5` line items select from it
- [ ] a project with no list can still raise a request with free text
- [ ] the upload component is the `T5.4` one; grep confirms there is not a second implementation
- [ ] no inventory, pricing or stock field exists in the diff
- [ ] the flow works at 390px
---
## Wave 8 exit criteria
- [ ] Kitting is fully built and toggled off for Micron EUV
- [ ] notifications carry working deep links and a delivery location
- [ ] notifications carry working deep links and a delivery location, and are off by default
- [ ] the material request is the lightweight scope, nothing more
- [ ] a material list uploads at SOP configuration and feeds the request (`D6`)
- [ ] no guessed part numbers or floor names anywhere in the code
- [ ] no real email left this branch; the capture sink count is recorded

View File

@@ -1,7 +1,13 @@
# Wave 9 — Verification and cleanup
**Items:** `CR-008`, `CR-017` (guard), `S6`, `S7`, `S8`, `C1` audit, `C2` audit, part of `C4`
**Items:** `CR-008`, `CR-017` (guard), `S6`, `S7`, `S8`, `C1` audit, `C2` audit, part of `C4`,
and from August 18: `D7`
**Depends on:** wave 8 merged
**Build order is not task order.** `T9.8` and `T9.9` were added on August 18 and are numbered
after `T9.7`, but `T9.7` is the reconciliation and stays last. Build `T9.1` through `T9.6`,
then `T9.8`, `T9.9`, then `T9.7`. They appear in that order in this file. Task IDs are
referenced outside this repo and are not renumbered to make the order read left to right.
**Why last:** `CR-008` reviews the PDF export against the final structure rather than
reviewing it twice. The audits confirm the standing requirements actually held across eight
waves rather than being asserted per PR.
@@ -117,6 +123,11 @@ sample project only.
- [ ] it cannot touch live project data; verify by attempting it against a real project
- [ ] the other three affordances are removed; grep confirms
**Reconciled with `D1`.** `T7.1` makes the creator's sample-data control **visible** - it was
hidden only because the page was framed. That is not a fourth affordance returning; it is the
one that should survive becoming reachable. This task still ends with exactly one, and if the
survivor is the creator's, the launcher and wizard copies go.
---
### T9.5 — C1 + S8: Accessibility audit
@@ -128,9 +139,15 @@ sample project only.
- **Files:** all pages, `.help-tip` elements
**`S8` is finished here** because tooltips are the one accessibility item with no natural
host component earlier in the plan. 15 `.help-tip` badges are `<span>` elements with no
host component earlier in the plan. `.help-tip` badges are `<span>` elements with no
`tabindex`, so the `:focus` half of their CSS rule is dead code and there is no touch path at
all on tablets, which is exactly what Field View targets.
all - on tablets, which is exactly what Field View targets.
**The count went up, not down.** Wave 0 measured 15. At the wave 6 exit there were **18**:
three were added during waves 5 and 6 by tasks reusing the component as designed, and every
one of them is unreachable for the same reason. That is the cost of scheduling a broken
component late - each wave that reuses it makes this task bigger. Measure again before
starting and record the number; do not inherit 15 or 18.
**Do:** Make every `.help-tip` reachable by keyboard and by touch. Then audit the whole app
against the `CLAUDE.md` rules and record results in `docs/reference/accessibility-audit.md`.
@@ -140,7 +157,7 @@ Compare against the wave 0 baseline counts:
| Metric | Baseline | Target |
|---|---|---|
| `<div>` / `<span>` with `onclick` | 12 / 2 | 0 |
| `.help-tip` unreachable by keyboard | 15 | 0 |
| `.help-tip` unreachable by keyboard | 15 at wave 0, 18 at the wave 6 exit | 0 |
| `aria-live` regions | 0 | at least 1 per toast system and banner |
| Text below 4.5:1 | present | 0 |
| `outline: none` without replacement | present | 0 |
@@ -159,7 +176,7 @@ Compare against the wave 0 baseline counts:
- **Items:** `C2`
- **Depends on:** T9.5
- **Blocks:** nothing
- **Blocks:** T9.8
- **Surface:** `html/`
- **Files:** all pages
@@ -180,15 +197,95 @@ after screenshots and compare against the wave 0 baseline.
---
### T9.8 — D7: Archived projects, readable by project admins
- **Items:** `D7`
- **Depends on:** T9.6
- **Blocks:** T9.7
- **Surface:** `server/` + `html/`
- **Files:** launcher project list, project routes, role checks
**Problem:** Archiving already exists for projects and for work packages, and the server
already refuses writes to an archived project. What does not exist is a way back in: an
archived project leaves the launcher and there is no route to it, so archiving reads as
deletion to everyone using it. Asked on August 18 who should be able to open one, the answer
was **project admins only**.
**Do:** List archived projects behind a role check, separately from the live list so nobody
opens one by accident. Opening one renders read-only. The server is the enforcement point:
the read-only view is a courtesy, the role check and the write refusal are the rule.
**Do not:** build a second archive mechanism, add an unarchive button, or show archived
projects to anyone below project admin - including in counts, pickers or aggregate totals
where the number would leak their existence.
**Done when:**
- [ ] a project admin can list archived projects and open one
- [ ] everything in the opened project renders read-only, with no reachable write control
- [ ] the server refuses writes to it regardless of what the browser sends; verify by calling the endpoint directly
- [ ] a non-admin sees no archived projects anywhere, including in counts and pickers
- [ ] the archived list is visually distinct from the live list and says what it is
- [ ] it works at 390px
---
### T9.9 — Backlog sweep: the items wave 9 was told to own
- **Items:** `C4`, plus `BL-004`, `BL-005`, `BL-008`, `BL-009`, `BL-011`, `BL-012`, `BL-016`, `BL-018`, `BL-019`
- **Depends on:** T9.5
- **Blocks:** T9.7
- **Surface:** `html/` + `tests/`
- **Files:** as listed per entry in `docs/waves/backlog.md`
**Why this is a task and not a list.** Nine backlog entries name wave 9 as their home. Left
unscheduled they get discovered at `T9.7`, which is a reconciliation task with no room to fix
anything. Each was deferred for a stated reason and every one of those reasons has now
expired.
**The four colour items are approved work, not open questions.** `BL-004` (the help centre's
separate 52-colour palette, loaded on four pages), `BL-005` (35 colour literals in two
JS-built dialogs), `BL-008` (a second brand blue, `#2563d6`) and `BL-009` (a ninth amber, four
points from the eighth) were each deferred on the same grounds: fixing them changes a rendered
colour and the wave that found them was forbidden to change one. Asked on August 18, the
answer was **change them**. That is the `C4` half of this task and it is the only part with a
visible diff.
The rest are correctness and tooling:
| Entry | What it is | Note |
|---|---|---|
| `BL-011` | Three overlays race to append on the SOP page | Re-check first - `T7.1` may have removed the cause |
| `BL-012` | `admin` and the creator are not stable enough to screenshot-diff | Freeze the clock in the fixture; the creator half may be gone after `T7.2` |
| `BL-016` | Back to a URL with no `step` leaves the wizard where it was | One expression. `stepper_check.py` already pins the wrong behaviour, so the test flips with the fix |
| `BL-018` | The work package gate is the last localStorage-derived status | `T7.1` may have resolved it; if not, it is `B4`'s last hole. Fix `browser_check.py`'s fixture either way |
| `BL-019` | A cost code that left the list is silently blanked on edit | The four lines already written for `gov_wosize` |
**Do not:** fix anything here that a wave 7 or 8 task already fixed. Re-measure each before
touching it - four of these were logged against code that has since been rewritten, and a fix
applied to a problem that no longer exists is worse than the problem.
**Done when:**
- [ ] every colour literal outside `theme-light.css` is gone, help centre and JS-built dialogs included; grep confirms
- [ ] one accent blue and one warning amber app-wide
- [ ] each of `BL-011`, `BL-012`, `BL-016`, `BL-018`, `BL-019` is fixed, or closed with a recorded measurement showing it no longer reproduces
- [ ] `stepper_check.py`'s pinned `BL-016` check is inverted to assert the fixed behaviour
- [ ] `browser_check.py`'s fixture uses the production SOP shape
- [ ] `docs/waves/backlog.md` has no entry still pointing at wave 9
---
### T9.7 — Final reconciliation
- **Items:** all
- **Depends on:** T9.6
- **Depends on:** T9.8, T9.9
- **Blocks:** nothing
- **Surface:** documentation
- **Files:** creates `docs/reference/completion.md`
**Do:** Walk all 55 items from `IMPLEMENTATION.md` section 6 and record, for each: built /
**Do:** Walk all 65 items - the 55 in `IMPLEMENTATION.md` section 6 and the 10 in
`docs/waves/decisions-2026-08-18.md` and record, for each: built /
deferred / not applicable, the PR that delivered it, and any deviation from its written
acceptance criteria.
@@ -200,7 +297,7 @@ criteria turned out wrong, inputs still outstanding, and follow-ups logged along
**Done when:**
- [ ] all 55 items are accounted for with a PR reference or a stated reason
- [ ] all 65 items are accounted for with a PR reference or a stated reason - the 55 from section 6 and the 10 from `decisions-2026-08-18.md`
- [ ] every deviation from written acceptance criteria is documented
- [ ] the four out-of-scope items are confirmed unbuilt
- [ ] outstanding inputs from `IMPLEMENTATION.md` section 8 are restated with current status
@@ -214,4 +311,6 @@ criteria turned out wrong, inputs still outstanding, and follow-ups logged along
- [ ] one icon system, one sample-data affordance
- [ ] accessibility metrics hit target or are documented
- [ ] the primary flow works at 390px
- [ ] every item is reconciled
- [ ] archived projects are readable by project admins and invisible to everyone else (`D7`)
- [ ] the backlog has no entry still pointing at wave 9
- [ ] every item is reconciled - all 65