# Verification record — 31 October 2026

Current checkpoint: **v0.25.0 guarded merge correction**, documented in [executed checks](merge-correction-checks.json). API/storage, client logic, actual React components with mocked HTTP, static rendering and fresh extracted recovery remain distinct evidence. The immutable package proof names the final commit, ZIP/checksum, independent tree/mode verification, older-release rejection and repeat archive/rebuild checks. No v0.25 native browser, phone, second-machine or live approval is claimed. The authorised v0.23 note walkthrough is completed and signed out, with an external supplement; its original package remains unchanged. Historical paragraphs below retain their original checkpoint status and are not current permission requests.

Preserved checkpoint: **v0.20.2**, the owner recurrence/calendar rehearsal and a small removed-holiday display correction. The actual v0.20.1 browser journey covered definition/preview/generation, conflict and stale review, overrides/cancellations, overlapping holidays, future rules, protected history, pause/retire/restore, month ends, spring clocks and second-business isolation. The display repair has passed client/render/build checks. Its final browser retest is pending: automatic approval review rejected signing into the fictional demo without fresh explicit permission. See [the recurrence walkthrough](RECURRENCE-WORKFLOW-WALKTHROUGH.md) and [checks](recurrence-milestone-checks.json). **60 targeted backend tests, 167 client tests, 88 structural renders and the build passed**, with the backend unchanged and its 694-case full run remaining historical. The saved browser dataset passed stopped backup/fresh restore and a second backup. All 45 capabilities remain tracked and all 14 original findings remain OPEN. Phone/PWA, staff/customer, photo-picker/format/licensing and full-product/live approval remain open.

Final v0.20 verification: **694 backend tests, 156 client tests, 88 structural render checks and the build passed**. Ten recovery reports cover the new format and the existing customer, document, portal, feedback, enquiry, field, recurrence and workflow families. The new-format disappearance drill restored exact original facts and normalized images, revoked old access, suspended sharing/review and preserved retry identity. Browser and phone proof remain separate and unverified. See [checks](enquiry-format-milestone-checks.json) and [recovery evidence](recovery-v0.20/enquiry-formats-recovery-drill.json).

Preserved historical checkpoint: v0.19. **662 backend tests, 125 client tests, 84 structural render cases and the production build pass.** Public date/photo selection, error retention, successful saving and owner reopening have actual browser evidence. Same-Mac format-12 recovery of that UI dataset and a four-business disappearance drill passed. Other-business denial was API-tested; its browser sign-in and final tab cleanup were blocked by the existing leave confirmation. Some protected files are cloud-only and could not be rehashed. See [checks](enquiry-milestone-checks.json) and [guide](ENQUIRY-DATE-AND-PHOTOS.md). No live SHS or production approval.

Preserved historical checkpoint: **v0.18**. The [website walkthrough](WEBSITE-WORKFLOW-WALKTHROUGH.md) and [checks](website-journey-milestone-checks.json) record actual draft/publication/history, service-to-enquiry, two matching-contact enquiries, sign-out clearing, stale-save and missing-page repairs. **612 backend tests, 113 client tests, 81 structural render checks and the build passed.** The backend run took 176.36 seconds; `test-results-v0.18.xml` records zero failures/errors/skips. The two UI-created jobs/customers, two image files, distinct draft/public website and four publications survived fresh same-Mac recovery across all 78 tables per business, allowing only documented recovery identity/audit changes. Old access was rejected. Restored login used test APIs, not the browser. Format 11/pinned 1–10, 207 policies, schema and dependencies are unchanged. Existing TestClient/image-limit warnings and the 586.09 kB Vite chunk advisory remain. All 14 original findings remain OPEN.

Historical v0.18 scope: independent local prototype 0.18.0, with bounded SHS helper reuse; not the original deployed SHS app.

## Preserved v0.13 result

The complete 521-case backend suite passes. 85 client tests, 53 structural render cases and the final build pass. New cases cover combined filters, exact-customer source eligibility, per-business rules, owner/staff/portal boundaries, concurrent/idempotent/stale decisions, archive guards, feedback preservation and material suspension, inclusive UK periods, receipt corrections and strict format-10 recovery. Nine new owner-only method/path policies bring the default-deny inventory to 194 routes. New source-purpose code does not import or run the original app.

The four-business/eight-customer disappearance drill preserves all six operational tables alongside older document, invoice, receipt-request, field/source, photo, recurrence and feedback histories. The source is deleted before restore; old credentials remain unusable and testimonials remain paused until explicit renewed approval. In-place format-9 migration preserves active permission and revoked state; archived format-9 and earlier strict migrations pass. Normal data and the frozen owner review were not migrated.

Actual browser checks at 689 CSS pixels cover job save/reload/filters, correct empty states, keyboard access to Materials, Keep editing, stale-save draft retention, local draft/snooze/dismiss/reopen/manual-completion histories, business settings persistence, inclusive period/current totals and business separation. A seeded feedback fixture survives a private note/priority edit and is hidden after a material stage change. Its initial customer response and billing were API fixtures, not a new browser journey. Screenshots 01–09 record the main rehearsal; 10 verifies the final clarified report wording after rebuild/restart. The added canonical-date guard is API tested. See [checks](workflow-milestone-checks.json), [guide](JOBS-FOLLOWUPS-AND-OPERATIONS.md) and [remaining UI work](ui-verification-pending.json).

The isolated QA server is stopped, its saved fictional records retained outside the package. Port 8788 stays on the frozen owner-review runtime with identical data and session files. Original ZIP, both 275-file source copies and older code archives are unchanged. No live SHS/provider/message/domain/payment action occurred. TestClient/httpx deprecation, the intentional oversized-image warning and Vite's 541.54 kB bundle advisory remain. No load/device/security certification or full product completion is claimed.

## Preserved v0.12.2 portal result

79 client tests, 41 focused portal/PDF/customer API cases, 43 structural render checks and the build pass. The actual browser journey verified setup/sign-in/reissue, exact-customer projection, shared quote acceptance and decline, retained response history, stale-revision rejection and a downloaded customer PDF. An observed defect in v0.12.1 left cached customer content visible after revocation. The v0.12.2 browser retest verified that the home and two open quote pages clear without a manual reload; a valid check following another customer's contact edit preserves an unfinished response. See [the checkpoint](PORTAL-SESSION-CHECKPOINT.md) and [machine-readable evidence](portal-session-checks.json).

Only the disposable portal server was stopped. The existing owner review remains on frozen v0.12.1 at port 8788, with its data/session preserved. Normal data, original source references and previous archives remain unchanged. The full 473-case backend suite was not repeated. Recovery-password completion, customer-contact revocation UI, stage/invoice downloads, broader staff/customer switching, mobile/PWA and accessibility remain queued. Historical held-dialog statements below describe earlier checkpoints; they are no longer current blockers.

## Preserved v0.12.1 patch result

67 client tests, 32 focused Materials API tests, 43 server-render checks and the final build pass. The bounded visible owner walkthrough passes, including the downloaded private draft PDF and direct other-business denial. Normal data is byte-for-byte unchanged. The rehearsal is deliberately left running on loopback port 8788; the historical stopped-server statements below describe their original checkpoints. Read [the complete patch evidence and limits](MATERIALS-DIRTY-STATE-CHECKPOINT.md) and [machine-readable checks](materials-dirty-state-checks.json). Full backend regression was not repeated for this client-only patch.

## Preserved v0.12 result

**473 backend cases passed in one full run (115.20 seconds)**: 416 prior cases retained, 41 feedback cases and 16 feedback recovery cases. **59 JavaScript tests**, **41 server-render smoke cases**, the frontend build and **19 no-browser loopback routes** pass. See test-results.xml, feedback-client-tests.txt, feedback-render-check.json and feedback-milestone-checks.json. Built assets: index-CcEhUERe.js and index-CYykBvtc.css.

Coverage includes exact completed-job/customer eligibility, repeat-request/reissue guards, protected capture permissions and rollback, code hashing/expiry/revocation, no portal/document access via feedback codes, strict ratings/consent/acknowledgements, immutable private answers, valid-shaped owner/staff/portal/anonymous/other-business boundaries, duplicate-contact separation, private sentinel exclusion from API/HTML, explicit recommendation and all-response private averages, exact projection preview/publication, concurrent submission/retry/consent/withdrawal, contact/job/archive suspension, withdrawal after archive, website-history restoration and preservation of editable /reviews pages. Every owner route remains denied to staff under 185 explicit default-deny policies. Earlier valid-shaped foreign-customer quote accept/decline checks repeat in the full run.

Strict recovery writes format 9 and accepts pinned formats 1–8. The four-business/eight-customer loss drill keeps eight feedback requests/four responses and original consent/publication history alongside four quote responses, four issued invoices/sealed payment requests, private photos, field reservations/labour and recurrence. The source dataset is deleted before restore. Old tokens/hashes/captures/passwords/sessions are excluded or invalid, public testimonials are hidden, and explicit renewed codes, customer approval and owner publication are verified. The second backup validates. A populated format-8 migration preserves sealed documents/field/media/recurrence and adds empty feedback tables. Thirteen archive tamper cases and every permission/moderation transition are checked. See feedback-recovery-drill.json.

Both normal demo businesses remain empty and every prior tenant row is unchanged; setup is unacknowledged. A matching format-8 snapshot precedes migration and a stopped format-9 backup validates with three files. Runtime backup is refused; previews are stopped. No new dependencies were added. Known warnings: TestClient/httpx deprecation, the intentionally oversized-image fixture, and Vite's 518.07 kB main-chunk advisory (142.58 kB gzip). Performance/code splitting and device profiling remain open; no production performance claim is made.

**No browser or phone checks were performed.** The held native dialog was neither retried nor bypassed. Server render is structural evidence only. New screen interaction, navigation/dirty-state behavior, reload/reopen, keyboard/screen-reader/touch, installed PWA/offline and second-machine setup remain unverified. No live SHS, provider, email or production operations were performed. WEB04 has a local core; GROWTH01/TRAIN01/RECOVERY01 remain partial. All fourteen original findings remain OPEN. This checkpoint is not full product or production approval.

## Preserved v0.11 result

**416 backend cases passed in one full run**: 370 prior cases retained, 20 portal-access/response cases, 14 private-PDF/configuration cases and 12 recovery cases. **51 JavaScript tests** and **32 server-render smoke cases** pass. Final build and 13 no-browser loopback routes pass; all earlier normal tenant rows remain unchanged and operational tables remain empty. Default-deny inventory contains **165 routes**; every owner route is tested against staff. Current XML is test-results.xml; v0.10 XML is preserved separately. Known TestClient/httpx deprecation and synthetic oversized-image rejection warnings remain.

Coverage includes duplicate-name/email separation, valid cross-customer response attempts plus separate malformed-payload rejection, code expiry/single use/concurrent consumption, password/session revocation, generic recovery, stale/current quote snapshot checks, concurrent accept/decline, retained response history and source reservations, exact fractional pence, concurrent custom/year references, historical logos/wording, private PDF authorisation, original/current balances and stages, unsupported glyph errors and strict restoration/tampering.

Four PDF samples contain 11 pages, all independently extracted with pypdf, bounded with pdfplumber, rendered with Poppler and visually inspected. Page margins, repeated headers, page numbers, Welsh characters, final rows/terms, amounts, old branding and original/current sections are checked. This is PDF artifact verification, not browser printing or a phone test. See pdf-output-check.json.

Format 8 plus pinned formats 1–7 pass. Four invented businesses and eight exact customer accounts survive loss/restoration with four quote responses, four issued invoices/payment requests, private photos, field sources and recurrence history. Password hashes/tokens/captures are absent from the archive; old customer passwords/sessions/setup/reset codes fail, and no customer credentials enter RESTORED-LOGIN.txt. A second backup passes. See portal-recovery-drill.json and portal-milestone-checks.json.

The normal demo was upgraded only after preserving its format-7 snapshot and v0.10 code checkpoint. It starts empty, setup unacknowledged, and is now stopped. The original SHS ZIP and both reference copies remain unchanged. All 14 original findings remain open. New screens, actual click/download flows, mobile/keyboard/accessibility, PWA/offline and second-machine installation/recovery remain pending. The held browser was not retried.

## Preserved v0.10 result

**370 backend cases passed in one full run** (320 prior + 37 recurrence + 13 recurrence recovery), **41 JavaScript cases** and **24 server-render smoke cases** passed. The final frontend build passed. Current XML: test-results.xml; client evidence: recurrence-client-tests.txt and recurrence-render-check.json. Built assets: /assets/index-Bt752DWS.js, /assets/index-Ddddm52t.css. No dependency was added. Known TestClient/httpx deprecation and the synthetic oversized-image rejection warning remain.

Coverage includes concurrent different-key duplicate prevention, same-key replay, exact tenant/staff/customer boundaries, demoted-owner replay denial, stale calendar/resource/job/customer/date previews, whole-window conflict rollback, immutable visit identity, future revisions outside the new-generation window, overrides/cancellation tombstones, overlapping exclusions/removal, reversible pause/retire, protected past/ambiguous current work, reviewed future rescheduling/cancellation/holiday exclusion without changed field/time/photo/issued-document history, final-assignment timer interruption and retained alternative assignments. Calendar arithmetic covers weekly/fortnightly anchors, month end, leap/quarter/year rules, clock gaps/repeated hours, all-day time and upper bounds. Every owner route is denied to staff by the complete 152-route policy inventory.

Strict format 7 and exact pinned formats 1–6 pass. Tamper tests reject cross-customer/rule/block links, state/snapshot/history/run/author inconsistencies and altered calendar history. A populated format-6 snapshot restores with empty recurrence tables. The four-business loss drill deletes only its disposable source, then preserves 4 contracts, 8 rules, 24 stable occurrences, 4 protected past visits, 4 overrides, 4 cancellations, 8 excluded visits and 8 overlapping blocks, with field/photo/labour/issued-document history. Old credentials/sessions/portal grants fail; four open clocks restore interrupted at last confirmation, with no generation/billing. A second backup verifies. See recurrence-recovery-drill.json.

Client tests cover rule input transitions, exact single-visit versions, explicit history acknowledgement, inclusive windows and month/range/clock boundaries. Server rendering covers five pattern forms, month controls and ready/conflict/protected previews, alongside prior checks. These are not browser interaction or visual-layout evidence.

The no-browser launcher passed ten loopback route checks. It rejected backup while running and preserved every pre-v0.10 tenant row; both normal businesses remain empty with setup unacknowledged. A matching format-6 snapshot was retained before upgrade and a stopped format-7 backup verifies (three files). Ports 8787/8788 are stopped before packaging. See launcher-check.json; earlier launcher/XML evidence is retained separately.

**No browser or phone checks were performed.** The native dialog remains held while Sandy is away; no retries or alternate controls. New owner/staff/customer interactions, focus/accessibility, print/mobile/PWA and second-machine recovery remain unverified. Original SHS runtime/tests/data/providers remain untouched. The 14 original findings are open and overall parity/production approval is not claimed. The next candidate is customer portal completion and private document/PDF export.

## Preserved v0.9 result

**320 backend cases passed in one complete final run** (267 prior cases plus 39 field-work and 14 field-recovery cases), **32 JavaScript cases passed**, and **14 server-render smoke cases passed**. The production frontend build passed. Preserved XML: test-results-v0.9.xml. Client evidence: field-client-tests.txt and field-render-check.json. Built assets: index-D9emhQ51.js and index-CF-i2fUi.css. No dependency was added. The known Starlette TestClient/httpx deprecation and synthetic oversized-image warning remain.

The new cases cover exact staff/job/person access including revoked cached starts; private cost/reason projection; one/two-person capture and resource locks; pause/resume/stop and concurrent retries; lost heartbeats/restarts/clock jumps; London midnight/DST; invalid/overlapping/corrected intervals; saved rates and archived people; tiny captures; atomic daily-capacity failure; source selection/rounding/private customer prices; generated-entry exclusion; reservation concurrency/rollback; quote revisions and invoice/replacement transfers; stale-source share/accept/issue/conversion blocking; source release; paid/unpaid lifecycle; strict acknowledgement; exact customer/business binding; and closed-job history access. Every owner route remains covered by the complete default-deny inventory of 145 method/route pairs.

The four-business field loss drill preserves four private photos and log/material links, four corrected two-person/two-date conversions, 16 active and 16 archived generated time entries, four manual entries, original rates/names, archived resources, disabled staff links and quote/invoice replacement/reservation history. Four open timers restore as interrupted at their exact last confirmations; no clocks resume, resource locks remain, or new labour/documents/payments appear. Old credentials/sessions/portal grants fail and a second-generation backup verifies. See field-recovery-drill.json. Strict formats 1–6 and populated pinned format-5 upgrade are tested; prior media/identity/recovery cases repeat in the final run.

Client cases cover repeated-hour offset selection, spring gaps, invalid season/date choices, London midnight independent of host timezone, correction input validation and blank customer prices without private-cost copying. A runtime-specific GMT+0 label issue was found and fixed by deriving the actual numeric offset. Server rendering checks owner/staff Account and Field workspace, four timer states with owner-only costs, blank source prices and the initial source editor. These are not browser interactions. The former Account undefined dirty reference is fixed, account unsaved state reaches workspace navigation, and datetime-local follows the existing date/time input event pattern; interactive verification is still pending.

The no-browser launcher passed nine loopback route checks, rejected backup while running, and preserved every pre-upgrade tenant row. Both normal businesses have zero operational/photo/field/source records, with setup unacknowledged. A matching format-5 backup was saved before upgrade; a stopped format-6 backup verifies with three files. The server is stopped. See launcher-check-v0.9.json; v0.8 evidence is retained separately.

**No browser or phone checks were performed.** The earlier native discard dialog remains unresolved and Sandy is away. No browser retry or alternative control was used. Owner/staff/customer/public interaction, refresh/reopen of new screens, print, mobile/PWA, keyboard/focus, recovery screens and second-machine setup remain unverified. Source ZIP/runtime/tests, live SHS records, provider calls and production operations remain untouched. All fourteen original findings remain OPEN; full white-label parity and production readiness are not claimed.

## Preserved v0.8 result

**267 backend cases passed in one complete run** (228 prior cases plus 39 photo/gallery cases), **26 JavaScript cases passed**, and the frontend build passed. Preserved XML: test-results-v0.8.xml. Client evidence: photo-client-tests.txt. Built assets: index-8ML24USn.js and index-B71-R_d4.css. No dependencies were added. The known Starlette TestClient/httpx deprecation warning and a synthetic oversized-image rejection warning remain.

Coverage includes private uploads/re-encoding/metadata removal, unsupported/animated/oversized images, duplicate and concurrent retries, partial-upload rollback, exact assigned-staff permissions and revocation including old retry keys, original customer binding, private/shared/archived lifecycle, strict publication acknowledgement, private versus public projection, separate drafts, immutable public-image identity, comparisons/order, cross-business and portal gates, source-history relink blocks, current-publication direct bytes, withdrawal, escaped server metadata/sitemaps, standalone images and historical document logos, concurrent publication, history-to-draft, symlink denial, strict formats 1–5 and media recovery tampering. Every owner route is tested against staff under the complete default-deny inventory.

The dedicated four-business loss drill preserved 16 private images, four logos, all five media tables, private/shared/published/archived states, comparisons and disabled staff resource links. It rejected old passwords/sessions/customer grants and verified a second-generation backup. See photo-recovery-drill.json. Earlier membership recovery retains the invitation/reset revocation evidence. Format-4 pinned migration is separately tested.

The no-browser loopback launcher passed eight route checks, rejected a live backup and preserved every pre-upgrade tenant row. Both normal businesses remain empty; new photo/gallery tables are empty; setup is unacknowledged. The app was stopped. See launcher-check-v0.8.json; the previous record is preserved as launcher-check-v0.7.json.

**No new browser/UI checks were performed.** The earlier native discard dialog remains unresolved and Sandy is away. No browser retries, alternative controls, mobile/print/accessibility/PWA or live SHS checks were attempted. The new UI is implemented and built, with interactive verification pending. This checkpoint is not full white-label completion or production approval.

## Preserved v0.7 result

**228 backend cases passed in one complete run** (190 prior cases plus 38 identity/setup cases), **18 JavaScript cases passed**, and the final production frontend build passed. v0.7 XML: test-results-v0.7.xml. The build contains index-CQLKRx8k.js and index-BgvIkQkg.css. No dependencies were added. One known Starlette TestClient/httpx deprecation warning remains.

New coverage includes dynamic third/fourth businesses, safe/reserved slugs, duplicate/concurrent creation and retries, staged/renamed/pre-commit failure cleanup, crash-journal recovery and missing-store failure; complete endpoint-policy inventory/default deny; every owner route denied to staff; exact resource assignment and limited projections; work-note retries and owner retrieval; cross-tenant/customer/cookie boundaries; last-owner concurrent removal; role/disable/resource/session revocation; existing-account invitation proof, reissue/expiry/reuse/concurrent consumption; generic reset responses, rate limits, private capture failure rollback, single use, expiry and global password revocation; resumable setup; v0.6 migration exactly once; strict formats 1–4; four-business recovery retaining inactive identity and rejecting credentials/tokens/invalid links. Client tests reject stale responses and isolate staff/portal contexts.

The separate handoff drill deleted only its newly generated four-business source after backup verification and restored exact business records and membership/resource state. Old credentials, sessions, invitations, resets and portal grants were excluded, one fresh password was issued per eligible global account, disabled membership access stayed disabled, cross-business switches stayed scoped, and a second-generation backup verified. See membership-recovery-drill.json.

The normal no-browser launcher returned healthy loopback responses for health, workspace, account access and both starter websites. A running-app backup was blocked. Both normal starter businesses still have zero operational records; every pre-v0.7 tenant row was compared unchanged, with only the new setup/work-note tables added. Legacy owners gained exactly one owner membership each, with no resource link. A matching format-3 snapshot was saved before the upgrade. See launcher-check-v0.7.json. The app was stopped after those checks.

**No v0.7 screens were browser-verified.** Sandy is away and the earlier native discard dialog remains unresolved; no retries or alternate browser controls were used. Phone, print, installed PWA, accessibility, second-machine install and production checks remain pending. Source preservation, package inventory and stopped ports are checked at final packaging.

## Build and runtime

Initial clean local dependency installation completed in a new .venv and frontend/node_modules. Public package registry access was required; no source or customer data was uploaded. The original requirements and frontend environment were not installed.

- Python 3.12.14, Node 24.19.0, pnpm 11.25.0, macOS arm64.
- FastAPI 0.162.0, Starlette 1.10.0, Uvicorn 0.54.0, Pillow 13.0.0.
- React / React DOM 19.3.1, Vite 8.3.5.
- Exact transitive versions: requirements.lock and frontend/pnpm-lock.yaml.
- Frontend production build passed. Approximate v0.7 output: 409 KB JavaScript before compression, 42 KB CSS. These are local app bundles; there are no remote fonts or scripts.

The environment used these bundled tools because system Node was not on PATH:

- /Users/sandyrestall/.cache/codex-runtimes/codex-primary-runtime/dependencies/python/bin/python3
- /Users/sandyrestall/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/bin/node
- /Users/sandyrestall/.cache/codex-runtimes/codex-primary-runtime/dependencies/bin/fallback/pnpm

The package sources were checked through [PyPI's FastAPI metadata](https://pypi.org/pypi/fastapi/json), [PyPI's Uvicorn metadata](https://pypi.org/pypi/uvicorn/json) and [Vite's official guide](https://vite.dev/guide/). Installing current versions does not establish an exhaustive dependency security audit.

## Automated result

**Historical v0.6 result (preserved in test-results-v0.6.xml): 190 backend cases passed:** 147 existing cases (including the seven London-date cases) plus 43 website cases. **Fourteen JavaScript tests passed:** the ten existing cases plus four website route/reordering/template/contrast checks. The final frontend build passed. Historical report: test-results-v0.6.xml. Command: .venv/bin/python -m pytest -q --junitxml=docs/test-results.xml. JavaScript command: node --test src/material-csv.test.js src/calendar-dates.test.js src/document-rules.test.js src/site-rules.test.js.

The previous v0.5 result remains a historical split run: 140 main cases plus seven focused date cases, and two overlapping legacy repeats. It did not include a full rerun after its date guard. Its main report is preserved as test-results-v0.5.xml, with the original date/legacy reports. Version 0.6 now reruns all 147 of those cases together.

Coverage includes:

- Fresh empty businesses and independent database files.
- Unauthenticated access, CSRF, wrong Origin/Host, ignored forwarded host and request-size limit.
- Cross-business job, quote, invoice, payment, portal and image access; forged business_id payload.
- Public enquiry tenant routing and same-operation retry deduplication.
- Same-name/same-email customer separation and portal-to-staff separation.
- Draft quote hiding, portal acceptance, grant replacement and immediate disable.
- Integer-pence totals/VAT rounding, quote-to-invoice uniqueness, payment replay/conflict and overpayment rejection.
- Concurrent payments retaining every accepted entry without exceeding balance.
- Stale job and website edits, draft/publish/restore, image type validation and current publication access.
- Password-change and logout session invalidation, login throttling and validation response redaction.
- Unique .test mapping, unknown-host failure and own-business disconnect.
- Records retained after reconstructing the app with the same data directory.
- Outbound socket guard rejecting a connection attempt before network access.
- Static server excluding private files and unknown API/asset paths.

One dependency deprecation warning was emitted: Starlette currently recommends httpx2 for TestClient instead of httpx. The existing httpx test adapter worked and all tests passed. This is recorded rather than suppressed. Migration of that test adapter can accompany the next dependency maintenance pass.

The tests never import the original backend, run its conftest, read .env, call a live SHS URL or access a live database. They use invented records under pytest temporary directories. The socket-guard check is a short child process with no connection allowed.

## Materials verification

The new tests exercise staff-only reads and CSRF writes; portal exclusion; business/job/customer/catalogue separation; same-name customer filters by ID; stale versions and concurrent edits; exact fractional totals and half-penny rounding; invalid/negative/NaN/bool/exponent amounts; quantity/date/URL validation; archive/restore and active-name conflicts; immutable catalogue snapshots; reference charge prices leaving quotes/invoices/payment metrics unchanged; private order projections; all-or-nothing bulk/CSV writes; idempotent retries; price-history isolation; and additive schema/restart persistence.

The parser tests cover quoted commas/newlines/escaped quotes, CRLF, malformed quotes, file/row limits, header mapping, explicit price overrides and rejection of invalid values without silently substituting defaults. The original SHS test fixtures were not copied or run.

Desktop Materials QA used work/materials-browser-qa-data, separate from the empty delivered databases. Through the UI it:

1. Created an invented Alex Demo / Hallway shelving job.
2. Created Oak shelf edging at £12.34/m with supplier/SKU and £19.99 charge reference.
3. Added 2.5 m to the job for £30.85 and saved a manual supplier URL/price/date.
4. Changed the catalogue cost to £15 and later £16; the original job cost and v1 snapshot remained £12.34/m and £30.85 total.
5. Previewed and imported two CSV rows totalling £10.10, then used bulk ordered/received stages and a supplier filter.
6. Added a £3.20 one-off material inside the job. Create quote and Save job were disabled while its material form was open.
7. Restarted the server and saw the existing rows in the job's Materials panel.
8. Opened the private supplier order view and verified that no costs, charge references, private notes or customer contact details appeared.
9. Signed in to Harbour Homeworks and saw empty materials and an empty catalogue.
10. Edited catalogue and material costs in a second tab; **Refresh records** in the first tab retrieved both changes. The one-off cost was restored to £3.20 after this check.

The final invented material total is £44.15: £3.20 to order, £30.85 ordered and £10.10 received. Materials and order-page console logs contained no errors or warnings. Saved screenshots are Prototype-Materials-v0.2.png and Prototype-Supplier-Order-v0.2.png beside the project. UI CSV testing used the paste-and-preview path; the local file chooser path was implemented but not separately exercised. Actual browser printing/PDF saving and mobile/device checks remain unverified.

No live SHS administrative screen was verified. An earlier read-only attempt reached the Admin Sign-in page and native capture failed; Sandy then explicitly selected the downloaded code only. The feature comparison and implementation use that source, with no later live access attempts. Original ZIP and both 275-file reference copies were hash-checked unchanged; see source-preservation-check.json. Original code-review findings remain open.

## Earlier core-workflow browser observations

Checked the built app at http://127.0.0.1:8787 using a separate QA data path, work/browser-qa-data. That dataset is excluded from the runnable package. Both demo businesses in the delivered normal data path begin empty, including catalogue and material tables.

The desktop UI successfully:

1. Submitted an invented Alex Demo enquiry from Oak & Vale's website.
2. Showed that enquiry in the owner workspace.
3. Exercised job date/note entry. A later 0.3 browser check found and fixed the shared date-input state issue; the save-and-refresh evidence below supersedes the earlier date assertion.
4. Saved a £180 quote and shared it in the demo portal without email.
5. Issued a local access code, opened the customer portal and accepted the quote there.
6. Refreshed the owner workspace, created its invoice and recorded £60, leaving £120 outstanding.
7. Rendered the private invoice with the correct snapshot and payment figures.
8. Saved and published a changed website headline, then displayed it on the local public page.

The inspected workspace browser log contained no error/warning entries. Screenshots were saved beside the project as Prototype-website.png and Prototype-workspace.png. The workspace screenshot contains only the invented QA records.

An optional phone viewport check was delayed while a developer capability was being approved. It eventually returned a DOM width/content-width of 390/390, but the screenshot was visibly scaled incorrectly. This is **not sufficient visual verification**. Both viewport overrides were reset. Mobile layout, actual phone testing, installation behaviour and browser print/PDF export remain unverified. No permission restriction was bypassed.

The finished launcher was also started with --no-browser, returned a healthy local-demo response, and both normal business databases were checked to contain zero customers, jobs, quotes, invoices, payments, materials and catalogue records. The wrapper's automatic default-browser opening was not exercised; the same local URL was already browser-tested. See launcher-check.json.

The QA server is stopped at handoff. The local launcher is the intended way to start the empty product demo. Closing the QA server does not delete its local synthetic evidence.

## Limits

No independent penetration test, full route fuzzing, OS-level network audit, load test, accessibility audit, second-machine clean installation or production backup recovery was performed. No real DNS, HTTPS, billing, Google, email, AI or storage accounts were used. No statement here approves the original review findings, real customer data or production deployment.

## Version 0.3 checks

Customer tests cover selected-record reuse, separate public submissions, current-version checks, immutable document names, payment/history totals, immediate portal revocation after contact edits, guarded bare-enquiry links, audit retention and business/portal/CSRF boundaries. Workforce tests cover snapshots, half-penny rounding, strict duration/date/rate input, explicit corrections, daily capacity across jobs, concurrent additions, archive/restore, retry, restart, and financial separation. Calendar tests cover adjacency, multi-day overlap, separate people, whole-team blocks, 23/25-hour all-day dates, missing/repeated London hours, concurrent conflicts, history, archive/restore, team retirement, unchanged billing/status and every new route's business/portal boundary.

The 0.3 browser used a new invented dataset, work/core-expansion-qa-data. Normal delivered databases remain empty. Staff created Alex Sample once and added Fitted hallway shelves and Repair garden gate against that selected customer. History displayed two linked jobs. Jamie Demo was added at £20/hour; 90 minutes saved as £30. After changing the team default to £25/hour, the saved labour still showed £20/hour and £30.

Browser scheduling saved a timed visit, displayed a rejected overlapping booking, saved a 29–30 October all-day block and a 31 October whole-team closure, then archived/restored that closure using day view. The UI keeps multi-day work continuous and explicitly says overnight time is reserved.

### Shared date/time regression

During normal browser entry, native date/time controls displayed the edited values but onChange alone did not update React's saved form state in this preview. The shared Field input now also handles native onInput for date/time controls. It uses the same state callback for both events. No programmatic record writes were used to conceal or bypass this failure.

After the fix, normal save followed by page refresh and reopening verified:

| Flow | Deliberate change verified after reopening |
| --- | --- |
| Job target date | 28 October 2026 |
| Manual labour work date | 26 October 2026, with 90 minutes and £30 retained |
| Calendar visit | 28 October 2026, 09:30–11:15 GMT |
| Multi-day booking | Start 29 October, end 30 October, All day |

The saved multi-day edit-form screenshot is Prototype-Calendar-Dates-v0.3.png. All three new feature checkpoints are recoverable in local Git. Calendar recurrence, drag/drop, Google sync, notifications and share/print remain unimplemented. Team login/invitation, payroll, real profit, mobile/PWA and external-service tests remain outside this local milestone.

### Browser limitation at final polish

After the multi-day save/reopen evidence was captured, clicking Cancel on the unchanged edit form opened a native discard confirmation and browser automation stalled. Browser commands could not dismiss it; no restriction was bypassed. A fresh local tab loaded but its controls did not respond while that dialog remained. The user was asked once to dismiss the visible prompt. This did not undo the saved data or earlier UI checks. Final cosmetic adjustments (seven-day fit, v0.3 label and simplified customer/help wording) built successfully but were not re-inspected interactively after the stall. Saved screenshots show earlier milestone builds; the job-cost screenshot predates the work-date correction. Optional phone checks remain incomplete. The temporary tabs are not retained as deliverables.

## Version 0.4 recovery verification

26 new recovery cases and all 84 previous regressions pass (110 total). Six JavaScript cases and the frontend production build pass. No dependency additions were needed. The existing Starlette TestClient deprecation warning remains. The final normal launcher started with --no-browser, reported ready, and held the runtime lock; a direct exclusive-lock attempt was rejected. It then stopped cleanly. Both normal businesses retain zero customer/job/document/payment/material/catalogue/team/time/calendar records. Only additive recovery identity/lock metadata was introduced.

The disposable two-business drill saved a customer/job, quote, invoice and £25 recorded payment against £100 invoiced in each business. It retained catalogue v1 snapshots after a catalogue price change, £30 labour at a saved £20/hour after the team default changed to £25, received materials, calendar work, separate website draft/published images, publication history, audit/history and reserved .test mappings. After backup, source sessions/portal codes still worked. The test then deleted only its temporary source directory and restored from the archive. Every original business-table row was compared, except intended identity/grant changes and one new restore audit entry. Four PNG images matched byte-for-byte.

New owner passwords were different for the two restored businesses. Both old public demo passwords and changed source passwords failed. Old staff/portal cookies and portal codes failed; new passwords worked. Cross-business job/document/image/time/calendar access returned 404. Published images remained public under the correct local business; unpublished images remained private. A second-generation backup from the restored dataset verified successfully. See recovery-drill.json for non-secret evidence.

Rejection tests cover unsafe/absolute/traversal/backslash paths, symlinks, duplicate entries, unknown format, missing/swapped databases, wrong business/slug/file mappings, wrong hashes, corrupt SQLite files, unexpected schema/triggers, foreign-key corruption, active portal grants, oversize metadata, missing/corrupt images, existing/nested/protected targets and absent explicit confirmation. Running-process locks and active SQLite writers block snapshots. A committed-WAL test verifies committed journal data is recovered. A simulated mid-publication failure cleans only the newly created destination, and an incomplete marker prevents startup. An additive v0.3-style schema upgrade preserved existing business rows; mixed dataset identities were rejected. CLI verify/restore tests passed without printing generated passwords.

All data came from local API test clients in dedicated temporary folders under work/ or pytest's temporary area. No real records, original runtime, live site, external service or browser were used. The earlier native discard dialog remains unresolved: Sandy reported being away, and this milestone made no retry or alternate-tool attempt. Final v0.3 cosmetic, phone/PWA and recovery-screen checks remain pending. The v0.4 sidebar version label is built but not visually rechecked. This evidence does not establish production backup approval, a second-machine recovery, encrypted/off-device recovery, power-loss resilience, arbitrary archive authenticity, selective tenant merge or a rollback into the original folder.


## Version 0.5 document verification

The full regression run passed 140 tests in 21.49 seconds; seven additional focused date/timezone cases subsequently passed. Ten JavaScript tests and the Vite production build pass. No dependency was added; the existing Starlette TestClient/httpx deprecation warning remains. The new document suite covers draft edits/version conflicts, publication/privacy, expiry and old revision rejection, acceptance retries, immutable invoice issue/replacement, cancellation and closure of old voided-invoice stage requests, single-invoice stage totals, request-versus-receipt separation, exact fractional line/VAT rounding, strict penny inputs, receipt correction rollback/history, and business/customer/origin/CSRF boundaries.

Concurrency tests exercise separate workers recording the same targeted stage, replaying the same receipt operation, accepting versus revising a quote, converting an accepted quote, reserving distinct invoice numbers, correcting versus recording a receipt, voiding versus payment, and replacing a schedule versus recording a receipt. Current balances and allocations remain consistent. Staff/customer history, overview, job-cost and portal projections use net receipts and issued invoices. A malformed ownership link with otherwise valid foreign keys is rejected by backup validation.

A synthetic legacy fixture uses the exact pinned v0.4 schema with an accepted quote, visible invoice and £25 receipt in each fictional business. Upgrade keeps references, snapshots, the £75 balance, portal access and a valid existing staff session. Repeating setup does not duplicate history. New references advance beyond legacy Q-0042/I-0042. The old regression's immediate-visibility expectation was intentionally changed only for newly created drafts; the separate legacy test proves old access is retained. Format-1 restore is tested into a fresh folder without changing the source schema, then its format-2 backup verifies.

The final loss drill populated both businesses with a £100 issued invoice, £30/£20/£50 stages, a £40 receipt and an atomic correction to £20. It deleted only that disposable source, restored both businesses and compared each complete document/ledger/schedule/history projection exactly. Both retain £20 net received, £80 balance, the reversed original receipt and stage allocations of £20/£0/£0. See document-lifecycle-drill.json. The broader two-business image/record/access-revocation drill also passes with the new tables; recovery-drill.json is updated from the current run.

Before upgrading the normal empty demo, the preserved v0.4 tool created and verified a format-1 snapshot in the technical workspace. The v0.5 launcher then migrated its schema and reported ready with --no-browser. The initial sandbox loopback bind failed; the identical loopback-only command succeeded with tool approval. Its runtime lock blocked backup, and all ordinary and new document record counts remained zero. The server stopped cleanly. See launcher-check.json. No browser opening, live site, production runtime, external transport or original tests were used.

All v0.5 UI flows, final document/print layouts, recovery screens, mobile/phone/PWA and second-machine installation remain unverified. The earlier native discard dialog remains unresolved and Sandy is away; no browser retry or alternate control occurred. Older UI evidence is retained only for those earlier versions/flows. No production deployment, tax/legal compliance, full SHS parity or production recovery approval is established.


### Focused London business-date check

`documents.today()` uses `datetime.now(ZoneInfo('Europe/London'))`. Quote expiry, invoice/quote default dates and default receipt dates use that helper. Supplied dates are validated as strict ISO calendar dates, not converted through a host timezone. Document/validity/due dates may be deliberately backdated/future calendar dates in this demonstration; recorded receipts and correction replacements cannot be dated after the London business day.

Seven focused cases passed after the received-date guard: six simulated UTC-host clocks cover a summer 23:30 UTC crossing into the next London day, the spring-change day, the midnight before the autumn change, both occurrences of the repeated autumn hour, and a winter control. They verify London defaults, yesterday/today quote expiry, portal acceptance, today/tomorrow receipt checks and atomic rejection of a future-dated replacement. A seventh case rejects impossible dates, compact YYYYMMDD strings and timestamp input for date-only fields. No operating-system clock/timezone was changed. See document-date-test-results.xml and the named tests in tests/test_documents.py. The build was repeated after the matching date/correction form changes; browser interaction remains unverified.

A final focused legacy check also seeds an actual v0.4-style operation fingerprint for an already-recorded £25 receipt. Replaying that old operation ID after upgrade is rejected without adding money. The two legacy upgrade/format-1 cases were rerun after strengthening that fixture; they are repeated cases, not added to the 147 distinct count. See document-legacy-test-results.xml.


## Structured website and branding milestone (0.6)

The 43 new cases cover independent pages/services/settings/assets for both businesses; hidden pages/sections/service/gallery-image privacy; menu visibility versus publication; ordering and FAQ persistence; unsafe/reserved slugs and URL schemes; duplicate/invalid structures; foreign asset ownership; staff/portal/CSRF/origin boundaries; stale and concurrent save/publication; retry deduplication and restore history; server-rendered escaped HTML/social metadata, canonical origins, XML sitemap, noindex/robots and real 404s; unchanged tenant enquiry routing; business identity and private historical logos; invoice conversion retaining the accepted quote brand; forward v0.5 site conversion; strict format-2 recovery and format-3 roundtrip; malformed recovery content; and a two-business source-loss/recovery drill.

The new loss drill deletes only its invented temporary source. Restored draft/public content and complete invoice snapshots match, and both hidden and historical images remain owned. See website-recovery-drill.json. Current document/recovery proofs also come from the 190-case run. Version 0.6 writes recovery format 3 and checks exact supported schemas and website content contracts for formats 1/2/3. No original SHS tests/runtime are imported.

Before normal startup, the preserved v0.5 tool made a verified format-2 snapshot in work/normal-demo-pre-v06.zip. The no-browser launcher upgraded both old five-page sites while preserving their draft/public fields, original version numbers and all zero operational record counts. It served health, a homepage, a service route, a second-business sitemap and robots over loopback. Its runtime lock blocked backup; the server then stopped cleanly. See launcher-check.json. This is an HTTP/API check, not interactive screen verification.

No new dependencies or external service calls were needed. All v0.5/v0.6 interactive editor/public/portal/document layouts, browser printing, recovery screens, phone/PWA and second-machine setup remain pending while the native discard dialog is unresolved. No browser retries or alternate controls were attempted. Full SHS route parity, production indexing/domains, real legal/consent/invoice policy, subscriber provisioning, staff roles and production recovery are not established.

## Reporting checkpoint (0.15)

Full regression: 603 backend tests pass with no failures, errors or skips (`test-results-v0.15.xml`); 94 client tests pass (`reporting-client-tests.txt`). Prior 65 plus new 14 structural SSR checks pass; these do not establish interactive UI behaviour. The Vite build passes with the existing bundle-size warning; the expected image-limit test and Starlette httpx deprecation warnings remain. No dependencies, schema or recovery format changed. Both new owner-only GET endpoints are covered by the 207-route policy manifest.

Independently expected fictional totals include 15000 pence issued, 4000 October receipts, 1000 September receipts, 10000 current balance, 12000 current open estimates, 8000 void, 6000 reversed, 91 person-minutes, 3001 labour and 2468 received materials. Tests cover exact grouping, original identities after merge/reassignment, unknown/UK-boundary dates, mixed field/manual generations, source/document non-duplication, coherent cohort observation, explicit limits, formula-safe UTF-8 exports, stale/context guards, role/tenant denial and query-only failure. Four businesses reproduce report objects and CSV bytes after source disappearance and fresh restore.

Actual browser evidence covers the listed desktop and 690×720 report journeys, with three downloaded files parsed and retained outside the code ZIP. Both tenant database copies in verified backups are byte-identical before and after these read-only interactions. Normal demo data and preserved owner-review data/sessions are unchanged. The old owner review continues on its frozen v0.12.1 runtime; it was not reloaded, upgraded, signed out or discarded. The separate reporting server is stopped. See `reporting-milestone-checks.json` and `reporting-preservation-check.json`.

Overview retains broader all-draft/shared quote semantics (160 GBP fixture total) while Reports/Operations show their defined unexpired open estimate (120 GBP). This older headline alignment is outstanding. Limits and unverified browser/device/integration journeys are recorded explicitly; no full-product or live approval follows from this checkpoint.

## v0.20 execution environment and limitations

The final full suite ran in the fresh `/private/tmp/shs-v20-venv` with `PYTHONPYCACHEPREFIX=/private/tmp/shs-v20-pycache`. The first run had one incorrect new portal-version expectation and two existing recovery timeouts. The assertion now accounts for intentional portal revocation. A later retest stalled while reading an old cloud-only bytecode file; its identified test process was stopped. All 694 cases then passed using the separate cache. No protected review dependency, cache, process or dataset was changed. The build retains its 597.86 kB main-bundle advisory; the test run reports two warnings. These results do not certify production performance or hard macOS memory isolation.
