GENERATED VIEW · PRIVATE ARCHIVE · DO NOT SERVE
RobCo Industries · Archive Museum

dev-branch-workflow.md



MEMORY

memory/localmode-1060dea9-0dc3-42b4-958d-2a6f7a6c0c58/dev-branch-workflow.md

sha256 5e160c0868947471 · 5054 bytes · original held in the private archive

--- name: dev-branch-workflow description: "Branch model — ALL unreleased work pushes to `dev`; `main` is release-only. Plus the incidents that shaped it: the --no-ff release-merge lesson, the two root-causes of 'staging never updates', and the hard credential boundary on API tokens." metadata: node_type: memory type: project originSessionId: dev-branch-workflow --- Set 2026-06-29 by the owner. RobCo has a **dev/prod branch split**: - **`dev` = all unreleased work.** Every unit / fix / change commits and pushes to `dev`. Dispatch build sessions push to **`origin dev`, NOT `main`.** - **`main` = release-only.** `main` only receives changes via a `dev → main` merge at a version release. Production is gated so pushing `dev` never touches the public site. - **All rules and protocols apply IDENTICALLY on `dev`** — full gate, cache bump, docs, changelog ordering ([[changelog-rules]]), everything. **`dev` is held to the same bar as `main`; there is no looser branch.** The authoritative rule set is `CLAUDE.md` + `rules/`. - `[Unreleased]` changelog entries are maintained on `dev` and consolidate into the dated release block at the release merge. *Rewritten 2026-07-20: protocol numbers, a reference to the deleted RULES.md, version numbers, a commit hash, and claims about which deploy workflows exist have been removed — the workflow files, `CHANGELOG.md` and git log are the source of record for all of that. What remains is the model and the incidents.* ## ⚠️ RELEASE-MERGE: USE `--no-ff` (learned the hard way, 2026-06-30) **Do the `dev → main` release merge with `--no-ff` (a real merge commit), NOT a fast-forward.** A fast-forward makes `main`'s tip a commit **shared with `dev`**, and GitHub then attributes the production Pages deploy to `dev` — which the `github-pages` environment's main-only branch policy **REJECTS**. The failure is nasty because it is partial: the tag and the release still get created, but **prod never publishes**, so it looks like it worked. The workaround used at the time was to trigger the deploy manually on `main` (which re-attributes it and passes the env policy). **The permanent fix is `git merge --no-ff dev` on `main`** so the tip is a main-only merge commit and auto-deploy works on the first try. ⛔ **NEVER disable branch protection or the environment policy to work around this.** ## STAGING — why it lives on its own origin Staging is a free **Cloudflare Pages** site on a **separate origin**, deliberately. Same-origin GitHub subpaths would have made staging share PWA storage with the real production save — and corrupt it. It is renamed and given a distinct DEV icon so it can never be confused with prod on the phone home screen. See `planning/DEPLOY_STAGING_PLAN.md`. ## THE TWO ROOT CAUSES OF "STAGING NEVER UPDATES" Both were found by chasing the same symptom — no REBOOT prompt on the phone — and both are worth remembering, because the obvious suspect (the cache bump) was innocent **both times**. **Cause 1 (2026-06-29): nothing new was ever on the server.** The Pages project had been created as a **one-time manual upload, not Git-connected**, so it was frozen at its initial upload and never picked up any dev work. The cache-bump machinery had been working correctly the whole time. *(An auto-deploy workflow was subsequently added; check the workflow files for current state.)* **Cause 2 (2026-06-30, definitive): the Cloudflare Access login gate blocks the service-worker update install.** The Access bypass covered only `/sw.js` and `/manifest.json`, but NOT the SW-precached app-shell assets — those 302 to the login page once the session lapses. So: browser fetches `/sw.js` (200) → detects a new SW → install runs `cache.addAll(...)` → the asset paths 302 cross-origin to login → `addAll` **rejects** → install **fails** → the new SW never reaches `waiting` → no REBOOT prompt → the device stays on the old build. Production has no gate, so it always installs fine. **That is the entire prod-works / staging-stalls asymmetry.** **Recommended fix (owner's call): remove the Access gate on staging and make it public** — obscure URL, hobby project, BYO-key, so no secrets or user data are exposed, and updates become as reliable as prod. The alternatives are worse: bypassing all asset globs effectively makes it public anyway, and a long Access session merely delays the stall. Sessions **cannot** change Access policies — that is a security setting the owner does in the dashboard. ## ⛔ HARD CREDENTIAL BOUNDARY (incident 2026-06-29) **Sessions set up everything EXCEPT token creation and secret storage.** Deploy needs a Cloudflare API token stored as a repo secret; **I/sessions must NEVER create or store that token — the owner adds it himself.** The incident: an owner-directed session started a browser flow to create the CF token. It was halted. **The rule holds even when the owner asks** — that is precisely the case it exists for. See [[session-management-rule]], [[robco-uos-project]].
STAMP · generated for RELEASE v2.8.5 commit 06e5180 (06e51801b38a) · archive input-tree hash c07fbfbdd2e1ddeb · 754 files · no wall-clock timestamp (regenerates identically when nothing changed).