---
name: live-inject-release-notes
description: Release notes are now injected straight into the production releasenotes table as unapproved
metadata: 
  node_type: memory
  type: project
  originSessionId: a919958d-8286-4e1d-89cd-4ca33d65672e
  modified: 2026-07-21T07:51:08.044Z
---

**Policy (from 2026-07-17):** release notes drafted in the repo are pushed **directly into the production `evolution.releasenotes` table as unapproved** (`status = 0`), rather than pasted into the *Support → Release Notes → Add* form. Extends [[feature_release_docs_policy]]. **Claude does this manually from the dev box via the mysql client** — NOT app code, no in-app live inject. Connection details: [[prod_mysql_access]] (creds stashed in the `sqlClaude*` .env trio for Claude to read).

**How to insert (matching the Add-form handler `support_releasenotes.php`):**
- Columns: `ticket_id, dev_id, feature_name, type, status, feature_description, module, support_doc_required, public, date_added`.
- `status` = approve flag → **0 = unapproved** (what we want; shows an Approve button in the UI).
- `public` column is written from `$notreleasenote`, but the two handlers in `support_releasenotes.php` set that var with **opposite logic for the same "Do not record release notes" checkbox**: the **add/save** path (`addreleasenote`, ~L15) does `ticked?1:0` (so `public=1` = do-NOT-record), while the **approve** path (`approvereleasenote`, ~L84) does `ticked?0:1` (so `public=1` = a real, shown note). Because customer-visible notes always go through *approve*, the operative meaning for anything with `status=1` is **`public=1` = show it** (data: of `status=1` release notes, 88 are `public=1` vs 7 `public=0`). So the `login2.inc` / customer filter `status=1 AND public=1` is **correct**, NOT an inverted bug (earlier note here was wrong). For a manual INSERT of a note you want shown, set `public=1`. **Fixed 2026-07-17:** the two handlers previously set `public` from `$notreleasenote` with *opposite* sign (add `?1:0`, approve `?0:1`) and the 3 edit-modal populate blocks (support_releasenotes.inc, support_ticketedit.inc ×2) ticked "Do not record" when `public==1` — all now consistent on the canonical meaning (renamed var `$isPublic = isset($_REQUEST['notreleasenote']) ? 0 : 1;` in both handlers; populate ticks only when `public==0`). Left as-is: 25 legacy `status=0` drafts hold pre-fix `public` values, so their approve-modal "Do not record" default is wrong until reviewed (Shane opted not to migrate); 7 approved notes at `public=0` remain hidden from the login splash.
- `dev_id` = `$_SESSION['userid']`; use **304** (Shane; 55+ existing rows).
- `type` = enum, normally `'Release Note'`. `ticket_id` = 0 when the draft names no ticket.
- `module` is **varchar(15)** — keep it short (a single keyword like `Purchasing`/`Inventory`); longer values silently truncate.
- `support_doc_required` tinyint: 1 when the draft says a new doc is needed ("Yes"), else 0.
- `date_added` = `CURDATE()`.

**Encoding gotcha:** the table is `CHARSET=latin1` but the app stores **raw UTF-8 bytes** in it (e.g. em-dash = `E2 80 94`) and serves pages as UTF-8. Insert with a **latin1 client connection** (`mysql --default-character-set=latin1`) so UTF-8 bytes pass through raw. A utf8mb4 connection would transcode and strip `—  ·  →  −`. Build the INSERT file programmatically (double `'`→`''`) to avoid escaping errors on apostrophe-heavy copy. **Cleaner alternative:** write copy with **HTML entities** (`&mdash; &ldquo; &middot;`) — pure ASCII, sidesteps the charset issue entirely and renders correctly (see HTML-embed note below).

**HTML in release notes works** — `feature_description` is echoed **raw, unescaped** at every render point: `releasenotes_customers.inc` (Release Notes page), `login2.inc` (login splash, via `nl2br`), and the internal support tables. So `<p>/<ul>/<em>/<a>/<img>` all render. Customer view does **not** `nl2br`, so HTML copy must supply its own `<p>`/`<br>`. Embed images with an **absolute URL to the canonical public docs host `https://my.evolutionerp.com.au/docs/...`** (200, no login/`checkSession` — confirmed public; per-tenant hosts vary so never use a relative/tenant URL in a shared row). Caveat: prod docs sync via git, so a freshly-added image 404s until deploy — the `status=0` gate covers it (approve after deploy).

**`documentation_url` column (varchar 200, added 2026-07-17):** now **wired end-to-end** — Add/Edit forms (`support_releasenotes_add.inc`/`_edit.inc`), handler `support_releasenotes.php` (INSERT + both UPDATEs), edit-populate JS (`support_releasenotes.inc`, `support_ticketedit.inc` ×2), and rendered as a "View documentation" button in `releasenotes_customers.inc` + a link in `login2.inc`. Policy: set it to the related `my.evolutionerp.com.au/docs/...` page whenever a note has one. `login2.inc` filters `public = 1`, which is **correct** (public=1 = a shown release note on the approve path — see the `public` bullet above; this corrects an earlier wrong "inverted bug" note).

Batches: ids 115–122 (8 purchasing/inventory report notes) 2026-07-17; id 123 (back-order auto-fulfil, first note using `documentation_url` + embedded docs `<img>`) 2026-07-17; id 139 (AP bill due date auto-fills from supplier terms; Purchasing; `documentation_url`→ purchasing/bill-entry.php; `status=0` awaiting approve after docs deploy) 2026-07-21.

**Backfill (2026-07-17):** last-month notes 99–122 given `documentation_url` (mapped to the matching `docs/...` page) + a `docFigure` screenshot embedded via **`CONCAT(feature_description, '<img …>')`** where a relevant image existed. Using CONCAT (not a full rewrite) preserves the stored raw-UTF8 bytes untouched, so latin1-connection transcode risk only ever applies to the ASCII `<img>` literal appended. Guarded appends with `AND feature_description NOT LIKE '%<img%'` for idempotency. Report notes got URL-only (no per-report screenshots exist under `docs/reporting/`). **Superseded 2026-07-20:** the full `docs/reporting/images/**` set now exists (regen-docs.sh), so all reporting notes 115–135 lacking an image were backfilled with the matching per-report screenshot via the same `CONCAT(feature_description,'<img …>')` + `NOT LIKE '%<img%'` idempotent path (img style as below; URLs `my.evolutionerp.com.au/docs/reporting/images/<cat>/<file>.png`, all confirmed 200 before embedding). Notes 118/123/132 already had their own images. Skipped 106 (New Quotes), 113 (1 Invoice Many Jobs) — no matching docs page. **109/110 (Production Activities Calendar): docs page written 2026-07-17** at `docs/manufacturing/production-activities-calendar.php` (Manufacture→Calendar = `mfactivity.inc`; covers the week grid, status dots, the 5 filters + filters-persist-across-weeks behaviour of note 109, and note 110's special-char fix); added to nav Manufacturing section; image dir `docs/manufacturing/images/production-activities-calendar/`. NB new `docs/manufacturing/` section folder — Processes + Manufacturing Routes pages also moved here from `docs/modules/` 2026-07-17 (shared image dir now `docs/manufacturing/images/manufacturing/`; nav hrefs + cross-links updated) (placeholders: `calendar-overview.png`, `calendar-filters.png`). `documentation_url` NOT yet set on the two prod rows — do that (→ `my.evolutionerp.com.au/docs/modules/production-activities-calendar.php`) only AFTER the docs git deploy, since both notes are `status=1` (live) and the page would 404 until then. All docs pages/images confirmed live on prod (curl 200) before embedding, since these notes are mostly `status=1` (already customer-visible). img style used: `display:block;max-width:100%;height:auto;border:1px solid #ccc;border-radius:6px;margin-top:12px;`.
