Read this page in ten minutes. It tells you what we build, what you must decide, and what a person must do by hand.
Status in one line. The PRD is at revision 10 after ten review rounds. Your seven decisions of 25 September are now requirements with tests. The ten findings left open after round 9 are fixed. The round-10 check reports 0 blockers, 16 major, 0 minor. Eight of the sixteen are repeated split requests. No blocker remains, so conversion can start. See section 8 before you decide.
What "go" means. If you say go, we convert the PRD, which turns it into otto's task list. otto builds the twenty-three stories on a feature branch. Nothing reaches production until the release checklist in section 6 is complete. If you say no-go, nothing changes; stage 1 stays live.
We replace the fixed header menu with one menu that editors manage in WordPress. Stage 1 already shows the agreed design as fixed HTML files, which we call the bundle. Stage 2 renders the same HTML from one WordPress menu, item by item. A script, the parity check, compares the two outputs link by link, so Google sees no change. When the missing pages exist, editors un-hide menu items and the full design appears.
In production since 24 September 2026. Tag nav-2026-stage1-v1.1, accessibility hardened. Live links only.
The same design, rendered from one editable menu. Same links, same order, same landmarks as stage 1.
Frozen at ct-nav-full-design-2avs-yn35.pages.dev, tag nav-2026-full-design-v1. Editors reach it by un-hiding items.
Twenty-three stories. One commit each. otto, the build tool, runs them one at a time. The "depends on" column is the run order, not a plan for parallel work. A person does NAV-001 and the release steps. NAV-018, NAV-020 and NAV-023 run after the production switch, in a second conversion. The operator is the person who runs the release, Stevan or Phil.
| ID | Title | Executor | Depends on | Done when |
|---|---|---|---|---|
| NAV-001 | Human prerequisites.user.ini record, Safe SVG install, hub row id (done) | HUMAN | none | The release checklist file records each item with evidence. |
| NAV-002 | Admin prerequisites and the test runnercore edit walker, Description box, input-limit warning, PHP 7.2/8.3 lint | CODEX | none | The Menus screen shows the Description box and the lint exits 0. |
| NAV-003 | Tree builder and flush primitiveroles, hidden pruning, badges, mobile order | CODEX | NAV-002 | 73 items in, 52 visible out; tests pass on three PHP runtimes. |
| NAV-004 | Item markup, icons, SVG sanitiserattachment icons and https URL icons | CODEX | NAV-003, NAV-014 | All 15 icons pass the sanitiser; tile markup equals the bundle. A URL icon renders as an image with no server fetch. |
| NAV-005 | Per-item fields with guarded saveincluding the new Icon URL field | OPUS | NAV-003 | Each field round-trips, the Icon URL included; a save without the form marker writes nothing. |
| NAV-006 | Media picker for icons and images | OPUS | NAV-005, NAV-010, NAV-017 | An editor picks an icon, saves once, and the tile shows it. |
| NAV-007 | Wide panel markup (1200 px and up) | CODEX | NAV-004, NAV-014 | The parity check and a byte comparison against the bundle both pass for every tab. |
| NAV-008 | Medium panel markup (600 to 1199 px) | CODEX | NAV-004, NAV-007, NAV-014 | The same two checks pass against the bundle's medium blocks. |
| NAV-009 | Phone sheet markup (below 600 px) | CODEX | NAV-004, NAV-014 | The same two checks pass against the sheet; mobile order works. |
| NAV-010 | Render integration on stage 1fragment filter, composition, preview parameters | CODEX | NAV-007, 008, 009, 014, 016, 017 | Switch off: header identical to stage 1. Menu active: the parity check passes. |
| NAV-011 | Cache lifecycleone cached row, gated invalidation, preview bypass | CODEX | NAV-010 | A flush deletes the cached menu HTML. After a flush the first load rebuilds it and the second reuses it. Unrelated edits leave it in place. |
| NAV-012 | Section map for the body class | CODEX | NAV-011, NAV-022 | Eighteen URLs carry the expected section class. A blog post carries none. |
| NAV-013 | Cloudflare edge purge on every change | CODEX | NAV-011 | One purge per request that flushed; a label change shows on production within 60 seconds. |
| NAV-014 | Harness and baselinesfixtures, checker copy, states port, visual and Lighthouse tools | CODEX | NAV-002 | All fixtures committed; the baseline runs are quoted. |
| NAV-015 | Verification of the rendered menuscreenshots, link states, behaviour, Lighthouse | CODEX | NAV-011, 012, 014, 017, 021 | Screenshots within 1 % of stage 1; 33 link-colour probes pass; Google Lighthouse speed scores stay within tolerance. |
| NAV-016 | Seed the menu from Tools; activate and deactivate | OPUS | NAV-003, NAV-014 | Seed creates 73 items, 21 hidden; Activate and Deactivate set and clear the option. |
| NAV-017 | Icon import, full-design and launch presets | OPUS | NAV-016, NAV-004 | 15 icons imported once; the preset un-hides only targets that exist. |
| NAV-018 | Second critical-CSS set LATER | CODEX | NAV-019 | Seven pruned files hold no legacy menu selectors; first paint unchanged. |
| NAV-019 | Launch gate and release checklist | CODEX | NAV-006, 013, 015, 017, 021, 022 | The gate script reports zero failures on local and staging, one blog post included; the release checklist is written. |
| NAV-020 | Legacy removal and menu export LATER | OPUS | NAV-019, NAV-018 | Legacy render path and CSS gone; three legacy menus exported, none deleted by code. |
| NAV-021 | Full-design stylesheet, printed only when needed | CODEX | NAV-010, 014, 017 | Nothing prints in launch mode; full-design regions within 1 % of the archive. |
| NAV-022 | Blog posts and category pages: new header and sheetblog category links kept below the bar | CODEX | NAV-010, NAV-014 | A post and a category page pass the same parity check as the homepage. The blog category row keeps its styling. Switch off: the old blog header is byte-identical. |
| NAV-023 | Cleanup verified on staging LATERthe committed cleanup deployed by sha, retire action, gate, rollback rehearsal | CODEX | NAV-020 | Staging runs the deployed cleanup revision. The gate reports zero failures. The rollback rehearsal passes. |
You took these decisions on 25 September 2026. The PRD records each one as a requirement with a test.
industries on local, staging and production. The menu links to /business-foreign-exchange#industries. No exception is needed. Done./product and payplexo.com are not in the menu. Accepted./blog shows the new header today. Posts and the four category pages still show the legacy blog header. We checked production on 25 September. NAV-022 gives all of them the new header and the phone sheet. The blog category links stay below the bar and keep their styling. These pages carry no section class, because no tab leads to the blog. Decided: stage 2.The two Convert experiments are out of scope. You handle them outside this project. They are no longer a release step.
.user.ini sets max_input_vars=3000 on production and staging. Verified 24 September 2026./home/phil/bin/wp.ctw_nav_v2 switch.industries on local, staging and production. Done 25 September 2026.main, tag it, and deploy. Check that production reports that revision../deploy purge prod.| Risk | Mitigation |
|---|---|
| The rendered menu drifts from the bundle, so Google sees different links. | Every markup story must pass the parity check against the bundle, link by link. |
| Editors' changes stay invisible for hours because Cloudflare caches whole pages. | Every menu change deletes the cached HTML and sends one Cloudflare purge; a production probe proves it. |
| Unrelated edits, such as autosaves, purge the whole edge cache many times a day. | Only changes to the menu, its pages or its icons trigger a purge; unit tests cover the rest. |
| Code that runs on PHP 7.4 locally fails on PHP 7.2 in production. | Every PHP story runs its tests inside PHP 7.2 and PHP 8.3 containers. |
| A link to a private, missing or redirecting page ships in the menu. | Unpublished targets never render, and the launch gate probes every URL on each environment. |
| The new header breaks the blog post layout. | The switch-off state keeps the old blog header byte for byte. Probes and screenshots cover one post and one category page. |
Ten rounds ran. The checker, otto check, is an adversarial reviewer that reads the PRD as a builder would. FIX-FIRST means it wants changes before the build starts.
| Round | Result | Main change |
|---|---|---|
| 1 · otto check | 1 blocker, 35 major, 1 minor. FIX-FIRST. | Real frames cited; tooling moved into the theme repo; the old save handler unhooked. |
| SEO audit | Stage 2 "pass with fixes". | Tabs stay links; hidden items leave the HTML; the parity check becomes the acceptance test. |
| 2 · otto check | 2 blockers, 37 major. FIX-FIRST. | The PRD now builds on the stage-1 code with one filter line. |
| 3 · otto check | 1 blocker, 32 major. FIX-FIRST. | Symbol anchors; a new story for the full-design styles. |
| 4 · otto check | 1 blocker, 31 major, 1 minor. FIX-FIRST. | One base URL; a phone subline field; safer cache writes. |
| 5 · independent panel | 92 findings: 13 blocker, 42 major, 37 minor. 49 stood after refutation. | Extra editor fields so one menu reproduces the bundle; a race-free cache design. |
| 6 · otto check | 0 blockers, 28 major, 2 minor. FIX-FIRST. | Random cache tokens; a stable section key per tab. |
| 7 · otto check | 0 blockers, 23 major, 2 minor. FIX-FIRST. | Fifteen new findings fixed in revision 8, see below. |
| 8 · otto check | 0 blockers, 21 major, 0 minor. FIX-FIRST. | Eight are repeated split requests, declined. Thirteen new findings, fixed in revision 9. |
| 9 · otto check | 3 blockers, 18 major, 0 minor. FIX-FIRST. The three blockers were fixed in revision 9.1. | Owner decisions folded in; thirteen round-8 findings fixed; new story NAV-022. |
| 10 · otto check (final) | 0 blockers, 16 major, 0 minor. FIX-FIRST. No blocker: conversion can start. | Ten round-9 findings fixed in revision 10; new story NAV-023. Eight of the sixteen are repeated split requests. |
Fifteen findings from round 7, all fixed. The largest: a helper function was called before its story defined it. A WordPress catch-all rule made every path look valid, so missing pages could slip through. A custom link pointed the section map at the wrong id. A plaque without its card had no desktop position. The cache could store stale content during a cold start. The purge could run before the final cache reset.
Your seven decisions became requirements with tests. Icons gained an https URL option. The "All industries" link points at the hub row. Blog posts and category pages get the new header in a new story, NAV-022. The thirteen findings from round 8 are fixed. The largest: the cache dependency record is written before pages are read, so a late edit cannot be missed. A tab without an overview keeps its link on phones. The release checklist now merges the build into main and checks the deployed revision.
The ten findings from round 9, all fixed. The cache now trusts its dependency record only while the record matches the current cache token. Page status is read fresh from the database, not from a cache. A blog post placed in the menu invalidates the cache like a page. The blog category row keeps its styling under its new id. The archive comparison corrects for the September colour fix before it compares layout. The release steps keep evidence outside the code checkout, because the deploy tool refuses a dirty checkout. The cleanup story is verified locally; a new story, NAV-023, verifies the deployed cleanup on staging.
Eight new major findings from round 10 remain open. None is a blocker. They concern cache edge cases, taxonomy menu items, and two checklist details. We treat them as inputs to the conversion step, not as reasons to wait. Eight more findings are repeated split requests, declined.
Declined every round since round 2: splitting eight large stories. The task sizer splits stories at conversion time, and each story is one file or one tool set.
Prepared 25 September 2026 from docs/nav-audit-2026-09-22/prd-nav-2026.md, revision 10. Unlisted page: do not index.