# Private job photos and reviewed galleries — v0.20.1

Current checkpoint: **v0.20.1**, a repaired private-gallery preview and a bounded owner-to-gallery browser rehearsal. Sign-out now clears unpublished text and the open image viewer. Owner review, separate customer/public text, publication, stale-save retention, withdrawal, archive/restore, history and republishing were checked with synthetic records. See [the gallery walkthrough](GALLERY-WORKFLOW-WALKTHROUGH.md) and [checks](gallery-milestone-checks.json). **167 client tests, 88 structural render checks, 41 selected backend regressions and the build pass**; the unchanged backend’s 694-case v0.20 pass remains historical. The saved browser dataset passed stopped-data recovery. Actual new-format file selection, staff/customer browser branches, phone/PWA use and wider parity remain open. All 45 capabilities remain tracked and all 14 original SHS findings remain OPEN. Live use is not approved.

## Keep the job record private

1. In **Jobs**, open the exact job and use **Job photos → Upload privately**. Choose a neutral test image. Uploading does not publish it or share it with a customer.
2. Add a private caption, description, category and photo stage, then **Save photo details**. Categories use lowercase words separated by hyphens, such as `shelving` or `garden-room`. Lower display-order numbers appear first.
3. For a comparison, choose Before and After on two different images and give them the same short pair name. The private record can hold an incomplete pair while work is underway.
4. Review the saved image and choose **Mark reviewed**. Editing private details clears the review flag. It does not change an existing separately authored customer caption or published project snapshot.
5. **View history** shows earlier captions, descriptions, stages and order. **Archive photo** hides it from current job/staff lists and stops customer sharing. Restore brings it back privately and unreviewed; it does not re-share or publish it.

Files must be single-frame PNG, JPEG or WebP, at most 2 MB and 12 megapixels. The app applies orientation, resizes to fit 1800 × 1800, and stores newly encoded PNG pixels without source metadata, including location metadata. It stores no original filename or remote URL. It does not blur faces, addresses or other information visible in the pixels: the owner must review the actual image before sharing. This authenticated job-photo control does not accept GIF, SVG, HEIC, video or animation. The separate public-enquiry intake accepts bounded HEIC/HEIF still images from v0.20; it does not change this control. GIF/TIFF/BMP and other source-format gaps remain open, not agreed exclusions.

Limits include archived records: 100 photos per job, 1,000 per business, 100 gallery projects per business and 60 selected images per project. Re-uploading the same cleaned pixels to the same job returns the existing photo; if archived, the owner must restore it. These are local prototype limits, not a production storage plan.

## Assigned staff

An active staff membership must be linked to an active Team resource that has an unarchived booking on the exact job. Historical unarchived bookings count under the current assignment model. Staff can view the job's current photos and upload privately. They can edit only their own unreviewed submissions while assigned. Owners review, share, archive and publish.

Removing the assignment or resource link, archiving the resource, disabling the membership or signing out prevents subsequent access according to the saved session/assignment rules. Resource-link changes revoke the old session. A replayed upload checks assignment again before returning a saved retry result. Staff cannot use the owner gallery, image library, customer directory or financial APIs.

Private image requests carry the active business context. A context switch discards in-flight responses, revokes displayed object URLs and clears affected image views, including a same-origin tab notification. Customer portal and staff contexts remain separate. Already viewed/downloaded bytes cannot be recalled.

## Share with the exact customer

In a reviewed photo's **Customer sharing** controls, write a separate customer-visible caption and image description. Choose **Share reviewed photo with this customer**. The customer must also have an active portal grant for this business. In their portal, **View / refresh shared photos** lists only the explicitly shared photos for that exact job and customer.

**Stop customer sharing**, archiving the photo or revoking the customer's portal access blocks new retrieval. Re-sharing requires a fresh owner action. Private captions, descriptions, uploader details, review history and gallery management data are absent from the portal photo projection. The limited bare-enquiry customer relink operation now treats all photos, including archived photos, and staff work notes as meaningful history and refuses reassignment. Broader merging remains deferred.

## Publish a project deliberately

1. Open **Gallery** and create a private draft. Select the exact source job, or select the website image library for a standalone project. A job has at most one project, including archived projects. Its source cannot later be changed.
2. Write a fresh public title, page web address, description and category. No customer name, address, job message or private caption is copied automatically.
3. Choose images. A job project can select only its exact job's photos; a standalone project selects existing images from this business's website library. Write separate public captions and image descriptions. Choose stages/pair names again for the public story, and use Move earlier/later to reorder.
4. Optionally associate published services, set gallery order and decide whether to appear in index/service/related-project listings. A project omitted from these lists is **still public at its URL once published**. An unpublished draft is private.
5. **Save gallery draft**, then **Preview saved draft**. Preview uses the saved version; unsaved text is not included. It requires owner access. From v0.20.1, sign-out/account changes clear the whole private preview, including text and an open viewer; unavailable or changed-session checks also clear access.
6. Review all visible pixels, text, descriptions and permissions. Tick the permission acknowledgement and choose **Publish reviewed project locally**. This records a local acknowledgement, not proof of ownership, consent or legal compliance. All selected job photos must be reviewed and active; every public image needs a description; each named public comparison needs one Before and one After image.

The public gallery is `/site/<business>/work/projects`; project detail is below that address. Existing website gallery sections display published project cards alongside the earlier website images. Service pages show explicitly associated projects. Public detail includes related projects in the same category, Before/After pairs and an image viewer with labelled controls, Escape and arrow-key handling. Desktop viewer Next/ArrowRight, Escape and focus return passed in the bounded v0.20.1 journey. Assistive-technology and phone checks remain open.

The server emits escaped title/description/social metadata and a no-JavaScript gallery fallback. Sitemaps include indexed current project pages unless excluded. Every local page still has noindex protection. There is no automatic navigation rewrite: a website button may link to `/work/projects`, or to a currently published project, after saving and publishing the website itself.

## Withdraw, inspect and restore

**Withdraw publication** stops new access through the project's public page, JSON, image links and sitemap entry. Downloads and previously viewed copies cannot be recalled. A website-library image independently published on another page stays available there. Historic invoice/quote logos remain available through their exact private document routes.

A photo used by a published project cannot be archived until that project is withdrawn. After withdrawal, **Archive project** is reversible. **Restore as private draft** does not publish. Under Project history, **Inspect** shows the saved text and retained images. **Restore to draft** restores the selected content for review without changing the current publication. Retained media is not destructively cleaned up. The owner must review and explicitly publish again when appropriate.

Version checks reject stale edits. Retried writes reuse operation identities; file retries include a hash of the bytes. An upload failure removes only the new file created by that failed transaction. A process crash can leave an unreferenced private file, which is neither served nor included in recovery; automatic deletion of such files is deliberately absent.

## Recovery and next checks

Current recovery format 12 validates exact media relationships and history. Visitor-submitted photos retain original facts and cleaned bytes, while restore suspends review/customer sharing and withdraws affected gallery publication. Original publication history and saved drafts survive. The v0.20.1 browser-created gallery state passed a stopped backup, fresh restore, old-session/foreign-business denial and second-generation verification. The original synthetic source is retained at a separate path. This terminal/API evidence is separate from browser recovery and off-device certification. See [the walkthrough](GALLERY-WORKFLOW-WALKTHROUGH.md) and [backup guide](BACKUP-AND-RECOVERY.md).

Owner/private/public flows listed in the walkthrough now have actual desktop browser proof. Initial photo fixtures were seeded through an explicit API setup; the new-format picker and selected-file retention/Clear/discard remain unverified. Staff/customer browser branches, wider gallery variants, phone/assistive-technology/offline use and full source parity remain open. No unlock, password, dialog or security-setting request is pending.

The independent implementation avoids copying customer names into public captions or publishing automatically. Useful purposes behind original AI copy, richer formatting, cross-project moves and other actions remain tracked gaps or decisions. These differences are not blanket approval to exclude source features. No original SHS finding is closed by this local checkpoint.
