Changelog
# MWM (Maintenance Windows Management) — Changelog
Newest first. The version is shown as the badge in the UI (click it → this page).
## 2.76 — 2026-07-08
- **Stakeholder admins can manage their own people**: users holding
`manage_stakeholder` now see the Users page filtered to their stakeholder and can
create, edit, welcome, deactivate and delete those contacts. New users they create are
forced into their stakeholder; they cannot touch other orgs' users, platform admins,
or grant/remove admin roles.
- **Users list redesign**: the Name column now leads the table (bigger, bold), email
second. The password field on the edit form now says explicitly that typing there
OVERWRITES the current password.
- **Impact is tenant-team only**: the Impact nav item and its ABM are admin-only, and
stakeholder contacts no longer see or can change the Impact field when creating or
editing an MDT (existing classification is preserved).
## 2.75 — 2026-07-08
- **Self-service password reset**: "Forgot your password?" on the login page mails a
signed set-password link (7-day expiry, same mechanism as onboarding), branded and in
the user's language. The response never reveals whether an address exists; requests
are logged with IP.
- nginx now serves the ACME challenge webroot (groundwork for the Let's Encrypt
certificate).
## 2.74 — 2026-07-08
- **Reschedule asks for the new window right there**: the Reschedule button on the MDT
detail now carries date + start/end time fields; the change is validated (no past
dates, end after start) and the history records "rescheduled: old → new".
- **Today's calendar cell now has a black frame** (was brand-green, easy to miss).
## 2.73 — 2026-07-08
- **Drag & drop on the calendar**: drag an MDT box to another day to move its window.
Approved (Scheduled) MDTs warn first and go back for re-approval — same rule as
editing — and the move is recorded in the history ("window moved A → B, calendar
drag"). Past dates and closed/cancelled MDTs are rejected; the target day highlights
while dragging. Available to users who can request/approve/edit MDTs.
## 2.72 — 2026-07-08
- **Automatic translation of MDT descriptions in emails** (optional, per tenant): paste a
Google Cloud Translation API key in Settings and the free-text description goes out in
each recipient's language (EN/ES/AR), with a "Test translation" button. Results are
cached; any API failure falls back to the original text — notifications never block.
No key = descriptions as written (default).
## 2.71 — 2026-07-08
- **MDT emails now include the Description** you entered, in the HTML details card and
the plain-text body. The label comes out in each recipient's language (the free text
itself is sent as written — automatic translation of it is in the backlog: it needs an
external translation service).
## 2.70 — 2026-07-08
- **Wizard fixed**: Back / Next / Save showed all at once on every step (a CSS display
rule was overriding the `hidden` attribute), so Next was still visible on the Review
step and clicking it did nothing. Now each step shows exactly its own buttons — Review
shows only Save.
- **"Working…" feedback on every form**: submit buttons show a spinner and stop taking
clicks while the request is in flight, so a slow save no longer looks dead.
## 2.69 — 2026-07-08
- **Executor shown by full name**: the executor selector (wizard and classic form) now
reads "Name — email" instead of the bare address, and the MDT detail shows the
executor's and requester's name with the email as secondary text.
## 2.68 — 2026-07-08
- **Stakeholders page is tenant-admin only**: the nav item is hidden and all its routes
(list, create, edit, delete) now require the admin permission.
- **Stats are scoped by visibility**: a stakeholder contact now sees KPIs computed ONLY
over the MDTs they can fully see (an INDRA user gets INDRA numbers). Admins, approvers
and internal users keep tenant-wide stats; scheduled KPI reports stay tenant-wide.
## 2.67 — 2026-07-08
- **Edit notify-stakeholders from the MDT detail** (admins/approvers): a new "Edit
stakeholders to notify" box changes ONLY who gets notified / who sees the MDT — no
status reset, no re-approval (the full Edit still voids the approval as before). The
add/remove diff is recorded in the history. The detail page now also shows the
notify list for everyone.
## 2.66 — 2026-07-08
- **Stakeholder visibility is now enforced everywhere and configurable per tenant**
(Settings → Notifications → Stakeholder visibility). A stakeholder contact used to see
every MDT on the dashboard calendar. Three modes:
- **Only their own MDTs** (new default) — the calendar, MDT list, dashboard queues and
Outlook feed only show MDTs touching their organization (their services, explicit
notification, or being executor/requester).
- **Number only** — they see every box on the calendar but foreign MDTs carry just the
MDT number and window: no title, services or people; opening the detail is blocked.
- **Everything** — previous behavior.
Internal users, admins, approvers and stakeholders flagged "sees all" always see
everything in full.
## 2.65 — 2026-07-08
- **Activity log for tenant admins** (nav → Log): everything that happened in the
workspace across all MDTs, newest first — lifecycle transitions, every notification
that went out (with recipients and channel: "mail to / telegram to / cc"), and executor
answers. Filterable by type and by MDT, paginated, each row links to its MDT.
## 2.64 — 2026-07-08
- **Designed status + actions on list views**: user status is now a badge with a colored
dot (green Active / gray Inactive) and the row actions (edit, welcome, deactivate,
delete) are chip buttons with hover states — applied consistently to every list view
(Users, Services, Stakeholders, Impact, platform Tenants).
## 2.63 — 2026-07-08
- **Design pass on list views**: the page container widened (960 → 1240 px) so the Users
table no longer clips the action buttons on normal screens.
- **Phone field on users**: new contact phone in the user form, shown under the name in
the list.
- **Wider emails**: the branded email shell grew from 560 to 720 px — long lines no longer
wrap unnecessarily.
- The welcome email and user form now say **"Telegram bot"** instead of "tenant bot" (all
three languages).
- **Close (admin)**: tenant admins can force-close an MDT from any open status (next to
DELETE, with outcome select); recorded in the audit trail as "closed by admin (forced)".
## 2.62 — 2026-07-08
- **Security package**:
- Login brute-force lockout: 5 failed attempts lock the account for 15 minutes
(works across workers; failures and lockouts logged with IP).
- HTTP hardening headers on every response: CSP, X-Frame-Options DENY, nosniff,
Referrer-Policy, Permissions-Policy, HSTS.
- Cross-site POST rejection (Origin/Referer check) on top of SameSite cookies.
- Minimum password length raised to 8 (set-password links, user create/edit).
- **Per-tenant access control (geo-filtering)**: Settings → Access control — allowed
countries (ISO codes) and/or always-allowed IP ranges (CIDR). Empty = open. Country
lookup via local DB-IP database (downloaded at deploy; fails open if absent). The
settings page shows your current IP/country so you don't lock yourself out; escape
hatch: `flask access-reset <slug>`. The platform subdomain is never filtered.
## 2.61 — 2026-07-08
- **Fixed clipped action buttons** on list views (Users, Services, Stakeholders, Impact,
Tenants): the actions column no longer overflows the table — rows keep their height and
the table scrolls horizontally when narrow. Also removed a glyph that rendered as a
broken box on some Windows browsers.
## 2.60 — 2026-07-07
- **Tenant logo**: new Branding box in Settings with a logo URL. The logo shows in the
top bar, in every branded email and in scheduled reports (falls back to the MWM mark).
- **Scheduled reports**: new box in Settings — pick report type (Upcoming windows /
Activity summary / KPI summary), frequency (daily / weekly / monthly + day), time
(tenant timezone) and recipient emails. Reports go out automatically via the scheduler,
localized per recipient; each row has Send now / Pause / delete.
## 2.59 — 2026-07-07
- **DELETE button for tenant admins**: admins can now permanently delete any MDT from its
detail page, whatever its status (confirm dialog warns that history goes with it).
Non-admins still can't delete anything.
## 2.58 — 2026-07-07
- **Quick user deactivation**: an activate/deactivate action on every user row. Inactive
users can't log in, receive no notifications, and executor prompts are skipped for them
(recorded in the MDT timeline).
- **Always CC redesigned**: its own box — pick a user, hit Add; each CC person shows with a
✕ to remove. No more multi-select gymnastics. Saving the settings form no longer touches
the CC list.
- The cancel transition button now reads **"Cancel MDT"** (it looked like a page-exit) —
action buttons are also translated now.
## 2.57 — 2026-07-07
- **The 44 historical maintenance windows from v1 are now in MWM** (imported with their
original refs, statuses mapped — Pending Review→Submitted, Approved→Scheduled,
Completed→Closed✓, Failed→Closed✗ — services, stakeholders, executors and creation dates
preserved; four refs that collided with today's tests carry a “-v1” suffix). The scheduler
is suppressed for past-window imports, and Stats/calendar now show the real history.
## 2.56 — 2026-07-07
- **Editing an approved MDT voids the approval**: it goes back to Submitted (audited, with a
clear flash) and the executor circuit is cleanly re-armed — on re-approval the questions
fire again. Previous-attempt checkpoints stay in History marked 🗄.
- **Rescheduled is now a real state**: an approver can send a Scheduled window to
Rescheduled; it stays editable and open (visible in dashboard queues), then re-Submit →
re-approve restarts the full circuit. Date history preserved.
## 2.55 — 2026-07-07
- Stats: **monthly series** (last 12 months) — windows, emergencies, hours, closed OK/failed
per month.
## 2.54 — 2026-07-07
- **Overlap warning**: creating or editing an MDT that shares services with another open MDT
on an intersecting window shows a heads-up with the conflicting refs (non-blocking).
- **Advance notice finally fires**: subscribers of approved-events get a localized
"maintenance window coming up" reminder `advance_notice_hours` before the window
(once, tracked in the timeline).
- **Silent-executor reminder**: if the executor doesn't answer the pre-check within 30
minutes, the question is re-sent once (tracked as "Reminder sent to executor").
## 2.53 — 2026-07-07
- **Automatic daily Postgres backups**: new `backup` container dumps the database (gzip) to
`/home/somtik/mwm/backups/` every 24h — first dump right at startup — keeping 14 days of
rotation. Restore: `gunzip -c backups/mwm-DATE.sql.gz | docker compose exec -T db psql -U
postgres mwm`.
## 2.52 — 2026-07-07
- **Classic | Wizard toggle** on New MDT — two buttons like the language switcher; the choice
is remembered.
- **Per-tenant weekend days on the calendar**: Settings → Notifications lets each tenant pick
its weekend (RCRC default: Friday+Saturday); those days get a gray-blue tint so at a glance
you avoid scheduling when few people are around. Nothing hardcoded.
- **Welcome email, resendable per user, in THEIR language** ✉: new "welcome" button on each
user row sends a branded mail (workspace URL, their login, Telegram activation code) with a
**Set your password** button for no-password contacts — a signed 7-day link to a public
set-password page (v1-style onboarding, trilingual). Users who already have a password get
an "Open MWM" button instead.
## 2.51 — 2026-07-07
- **New MDT wizard** ✨: when creating an MDT you can switch between the classic single form
and a **step-by-step wizard** with a progress bar (Basics → Window → Services → Impact &
notify → Executor → Review). The review step summarizes everything before saving; your
choice of format is remembered. Same fields, same validations, same endpoint.
- Fix: recovered 296 ES + 305 AR translations that a broken catalog extraction had silently
emptied (v2.50 shipped with gutted catalogs for a few minutes). The catalog pipeline now
syntax-checks the tree before extracting and validates completeness after.
## 2.50 — 2026-07-07
- **Subscribe to MWM from Outlook/Google/Apple Calendar** 📅: new per-user iCalendar feed —
the "Subscribe" button on the dashboard shows your personal secret URL; paste it once in
your calendar app and every MDT appears as an event (window times in the tenant timezone,
ref + status + ⚡ in the title, services/executor/link in the description, cancelled events
marked). It refreshes on its own — live as the calendar fills up. Honors stakeholder
visibility: each person's Outlook shows what they'd see in the web.
## 2.49 — 2026-07-07
- **Every notification is now an INDIVIDUAL message in the recipient's language.** Event
mails go one-per-recipient rendered in each user's locale (EN/ES/AR) — subject, body and
the branded details card. The executor questions (ready / start / result) and their buttons
arrive in the executor's language, by mail and Telegram. Bot replies (/activate) localized
too. The Always CC people get exactly ONE archive copy per event, in the tenant language.
## 2.48 — 2026-07-07
- **New Stats page** (📊 button in the nav bar): live KPI cards — total windows, executing
right now, total/avg hours in maintenance, emergencies, started on time vs late (10-min
grace), never-confirmed windows, closed OK/failed — plus breakdowns by status, by executor
(windows + hours), by service and by stakeholder.
- **Execution queue on the dashboard**: each executor with their open-window count and
clickable refs — spot the overloaded one before assigning more (count turns gray at 5+).
## 2.47 — 2026-07-07
- Dashboard queues panels now always visible (with a friendly "nothing waiting" note when
empty) instead of hiding when there is no pending work.
## 2.46 — 2026-07-07
- **Dashboard queues**: two new panels under the calendar — **Approval queue** (each approver
with their count and the clickable MDT refs waiting on them; ⚡ marks emergencies, which
only appear in the queues of Emergency Approvers/admins) and **By stakeholder** (each
stakeholder with its open MDTs). Sorted by load, live as MDTs move.
## 2.45 — 2026-07-07
- Backlog: **iCalendar (ICS) subscription feed** — per-user secret URL so Outlook/Google/Apple
calendars subscribe once and get every MDT live (window times in tenant timezone, ref +
status, link to detail), honoring stakeholder visibility; optional holidays/PTO overlay and
a "Subscribe 📅" button on the dashboard.
## 2.44 — 2026-07-07
- Backlog: **user PTOs** (self-service vacations visible as a calendar overlay with toggle,
executor-availability warnings) and **custom per-tenant holidays ABM** (incl. change
freezes, like v1's change_freeze).
## 2.43 — 2026-07-07
- **Saudi holidays on the calendar** with an instant on/off toggle button right on the
calendar header (🕌 Holidays: ON/OFF, remembered per session). Islamic dates (Eid al-Fitr
1–3 Shawwal, Arafat Day, Eid al-Adha 10–12 Dhu al-Hijjah) computed from the official
Umm al-Qura calendar (hijridate); civil dates (Founding Day Feb 22, Saudi National Day
Sep 23) included. Holiday cells get an amber tint and the holiday name, localized in the
3 languages.
## 2.42 — 2026-07-07
- Settings → Mail: new **Always CC** multi-select of users — every outgoing mail (event
notifications, executor questions, emergency alerts) CCs the selected people. De-duplicated
against the To list; supports multiple selections.
## 2.41 — 2026-07-07
- Emergency timing refined per spec: **READY question immediately on approval; START question
waits for the window time; RESULT question fires the moment start is confirmed.** (2.40 had
the start question firing right after ready — corrected.)
## 2.40 — 2026-07-07
- **Emergency circuit — everything immediate.** An MDT whose window falls inside the tenant's
emergency threshold is auto-classified as EMERGENCY (⚡) on create/edit. At creation, the
Emergency Approvers (or admins as fallback) get an instant "awaiting approval NOW" alert
with a Review & approve button. Only an Emergency Approver (or admin) can approve it — and
on approval the executor gets the "all good?" question IMMEDIATELY (no pre-check wait);
when they confirm, the "do we start?" arrives right away (no window-start wait). No queues,
no timers: create → alert → approve → confirm → run.
## 2.39 — 2026-07-07
- Backlog: the whole Telegram bot (menus, buttons, executor prompts, /activate replies)
localized in each user's chosen language (EN/ES/AR), same as the per-recipient emails.
## 2.38 — 2026-07-07
- Backlog: native in-chat Telegram confirmations for the executor (callback buttons instead
of opening the browser). Note: mail + Telegram start-confirmation already works since 2.23.
## 2.37 — 2026-07-07
- Backlog: added **interactive Telegram bot menus per user, driven by their access levels**
(Requesters draft, Approvers approve from the chat, Executors confirm their windows,
Notifications-only users browse the calendar — same RBAC as the web).
## 2.36 — 2026-07-07
- New **BACKLOG.md** at the project root: the living improvements list. First user-requested
item: **per-recipient individual emails in each user's preferred language** (instead of one
bulk email with everyone in To) — also for Telegram. Plus the accumulated pending items:
emergency circuit, KPIs, editable catalogs (ABMs), zero-downtime deploys, v1 cutover,
Postgres backups.
## 2.35 — 2026-07-07
- **Click a calendar day to create an MDT on that date**: the day number is now a link that
opens the New MDT form with the window date pre-filled; hovering a day shows a green “+”
and highlights the cell.
## 2.34 — 2026-07-07
- **Visual lifecycle timeline on the MDT detail** (top of the page): a red progress line with
a dot per stage — Drafted → Submitted → Scheduled → Executed → Closed. Reached stages are
filled, the NEXT step pulses with a "next step" tag, and under each dot you see what
happened there (approvals, mails/Telegram sent, executor answers) with times. Cancelled
shows as a dark terminal dot with the remaining stages dimmed. RTL-safe.
## 2.33 — 2026-07-07
- **History now lists exactly who was notified and how**: each "Notification sent" entry
details `mail to: …` and `telegram to: …` with the actual addresses. Executor prompts also
record their channel and address.
- **New MDT form: "Stakeholders to notify"** — pick which stakeholders get alerted for this
MDT (their contacts with notification roles), in addition to the owners of the affected
services (v1's mdt_stakeholder brought back).
- **Branded HTML emails** — the v1 email look, in MWM green: brand header, details card
(Ref/Title/Window/Services/Status), and big action buttons for the executor questions.
Plain-text fallback included.
- MDT numbering now starts at **MDT-00100** so it never collides with the v1 numbers.
## 2.32 — 2026-07-07
- Fix: 27 Spanish/Arabic translations were silently wrong — `pybabel update` fuzzy-matched new
strings to old similar entries (e.g. the Arabic dashboard title showed "Maintenance windows."
instead of "Maintenance Calendar"). All recent strings force-corrected; the catalog process
now overwrites instead of only filling blanks.
## 2.31 — 2026-07-07
- **The dashboard is now the Maintenance Calendar** (like v1): month grid with each MDT as a
colored pill (status colors, start time, ⚡ for emergencies), Prev/Today/Next navigation,
today highlighted, click-through to the MDT. Below it, the **Open MDTs** table (newest
first). The module cards are gone — the nav bar already covers them.
- Nav bar and language switcher got hover feedback; calendar localized in the 3 languages
(month/day names via locale).
## 2.30 — 2026-07-07
- **Unified History on the MDT detail**: one chronological timeline with EVERYTHING —
lifecycle transitions, every question sent to the executor, every executor answer, and
every notification batch (with `event: reached/recipients` detail). The separate
"Executor flow" table is gone.
- MDT list: removed the redundant "open" link (the Ref is the link).
## 2.29 — 2026-07-07
- Fix: 500 on POSTs that commit and then load data (e.g. Cancel/transition an MDT, sometimes
creating one). The RLS context hook read `g.tenant.id` from an ORM object expired by the
commit, re-entering connection acquisition. The hook now uses a plain-value snapshot taken
at request start. (Postgres-only bug, invisible to the sqlite test suite — page sweeps now
include real POST cycles.)
## 2.28 — 2026-07-07
- **Per-user notification channels**: each user chooses **Email + Telegram / Email only /
Telegram only / Muted** — honored by every lifecycle notification. Executor questions
respect the preference too, but fall back to any available channel if the preferred one
can't deliver (the "do we start?" must arrive).
- **New "My profile" page** (click your email in the top bar): language, notification
channels, name, password change, and your Telegram activation status/code with
copy-paste `/activate` instructions. Admins can also set the channel preference from
the user form.
## 2.27 — 2026-07-07
- **The 5 notification roles now really work as a subscription matrix**: `notify_created`
fires when an MDT is created, `notify_approved` when it is approved/scheduled,
`notify_initiated` when the task starts, `notify_completed_ok` / `notify_completed_fail`
when it closes — by email and Telegram (to linked chats). Works from both the web buttons
and the executor confirmation links.
- **Stakeholder-scoped notifications**: contacts of a stakeholder only get MDTs that touch
THEIR services; internal users (no stakeholder) and "sees all MDTs" stakeholders get
everything.
- **Window validation**: creation rejects past dates, and end time must be after start time.
## 2.26 — 2026-07-07
- **Multi-language UI: English (default), Español and العربية with full RTL.** Language
switcher (EN | ES | AR) on the top bar of every page; the choice is remembered per user;
admins can preset a user's language and set the tenant default in Settings. Arabic mirrors
the whole layout automatically (logical-property CSS).
- **Complete visual redesign** — modern design system: brand tokens, 6 distinguishable MDT
status colors (with color-blind-safe dots), responsive layout (mobile subnav, scrolling
tables), keyboard focus outlines, categorized flash messages (green success / red error),
hero-style login, elevated dashboard cards. Pills no longer wrap mid-word.
- **Telegram activation like v1**: every user gets an activation code (shown on their user
page); sending `/activate CODE` to the tenant bot links their chat automatically — no more
manual chat IDs. The 19 v1 codes are imported, so existing codes keep working. A Telegram
icon in the Users list marks who is already linked.
- Mail: **From display name** setting and **"Send test to"** field for the test email.
- Data migrated from v1 (10.0.254.151): 18 stakeholders, 18 services, 19 contacts with roles,
Telegram chat IDs and compatible passwords.
## 2.25 — 2026-07-07
- Settings: new **Readiness** box — a live checklist (per tenant) that validates the whole
notification/executor setup: master switch, email channel complete, Telegram channel
complete, at least one user with a chat ID, timezone valid. Executor prompts only flow when
it says ready.
- Telegram: new **"Send test message to me"** button — sends a REAL message to your own chat ID
with the token in the form (the old button only validated the token). Clear guidance if your
user has no chat ID yet.
## 2.24 — 2026-07-07
- Deploy: nginx is restarted after every `compose up` so it re-resolves the `web` upstream.
(Adding the scheduler service shifted web's internal IP and nginx kept proxying to the old
one → 502 until restarted. Now automatic.)
## 2.23 — 2026-07-07
- **Executor flow (el corazón operativo).** New **Executor** role (`execute_mdt` permission,
now required to Execute/Close). For every Scheduled MDT with an executor: N minutes before
the window (per-tenant setting, default 60) the executor gets email + Telegram
**"Is everything OK for this window?"**; at window start, **"Do we start?"**; on YES the MDT
moves to Executed, an email goes to everyone with notification roles (**"task started"**),
and the executor immediately gets **"Finished OK or FAILED?"** — the answer closes the MDT
with the outcome. All via signed links that work without login (executors can be no-login
contacts).
- **Every step is timestamped** in the new `mdt_checkpoint` table (RLS) and shown as an
"Executor flow" timeline on the MDT detail, plus an **Execution time** figure
(start confirmed → finish reported) — the base for hours-in-MDT reporting.
- New **scheduler** container runs the check every minute (`flask notify-tick`).
- Settings → Notifications: new **Executor pre-check (minutes)** and **Timezone**
(default Asia/Riyadh) fields. Users now have a **Telegram chat ID** field (get yours from
@userinfobot); Telegram messages carry inline buttons.
## 2.22 — 2026-07-07
- **Users can now be associated to a Stakeholder** (new optional field + dropdown in the user
form, new column in the Users list). This is the first brick of the contacts model: a user
linked to a stakeholder is that organization's contact.
- **Password is now optional when creating a user**: leave it blank to create a
**contact without login** (shown with a "no login" pill) — they can hold roles like
Notifications without ever signing in. Setting a password later enables login.
## 2.21 — 2026-07-07
- **Roles now GOVERN the workflow (Fase 1 del backlog funcional).** Every MDT transition checks
real permissions: Submit needs `request_mdt`, Schedule/approve needs `approve_mdt`,
Execute/Close need `edit_mdt` (or approver), Cancel needs requester/approver. The `admin`
permission (and platform admins) always pass. Action buttons only show what YOU can do.
- **Audit trail**: new `mdt_event` table (RLS-protected) records who moved each MDT, when,
from/to which status, with an optional note — shown as a History section on the MDT detail.
Creation is recorded too.
- **Closing an MDT now REQUIRES an outcome** (success / unsuccessful) — no more silent NULLs.
- **Physical delete restricted**: only Drafted MDTs, only by admins — anything further along
must be Cancelled (the record is evidence).
- **Users, Roles and Settings pages are admin-only** (permission `admin`); their links and
dashboard cards hide for everyone else.
- Boot-time `seed-tenant-admins`: any active tenant with no admin gets the Admin role granted
to its oldest active user, so nobody gets locked out by the new enforcement.
## 2.20 — 2026-07-07
- **Deploys are now serialized with a server-side lock** (`flock /tmp/mwm_deploy.lock` around
both the file extraction and the whole bootstrap). Two deploys launched minutes apart used to
build in parallel and *the one finishing last won* — even if it was the older one — which
put a stale container back into service (the "v2.18 page after deploying 2.19" episode).
Queued deploys now wait their turn instead of racing.
## 2.19 — 2026-07-07
- **Login: the platform-admin password is no longer silently reset on every deploy.**
`seed-platform-admin` runs on each container boot and used to overwrite the existing
password with the `.env` value — so any password you set survived only until the next
deploy. It now only creates the user if missing; use `flask set-password` to change it.
- Login field accepts **email or username** again (`type="text"`): platform admins created
with `create-admin <login>` have username-style logins, which the `type="email"` field
introduced in 2.16 rejected in the browser.
## 2.18 — 2026-07-07
- Deploy no longer hangs at the end. The server writes a `.deploy_status` file (build result,
host vs container version, PASS/FAIL) and `deploy.bat` reads that instead of exec-ing into the
still-booting web container. Server-side version capture is time-boxed, so it can never block.
- Deploy output no longer prints the platform admin password.
## 2.17 — 2026-07-07
- **Real root cause of the intermittent login / "stale page" saga found and fixed.** The RLS
GUCs (`app.current_tenant`, `app.platform`) were set at **session level** on pooled DB
connections, so a connection kept the previous request's tenant — or platform mode — alive
after any mid-request commit (e.g. the lazy role seeding on the Users pages). Under RLS that
silently returns wrong/empty rows: pages that "look cached", users that appear logged out,
and a latent cross-tenant data-leak risk. The GUCs are now **transaction-local**
(`set_config(..., true)`) and re-applied at the start of every DB transaction via a
SQLAlchemy `after_begin` hook, so no request can ever run under another request's context.
- Session cookie renamed to **`mwm_session`** — any stale `session` cookie still sitting in a
browser (from v1 or older builds) can no longer shadow a fresh login. Everyone logs in once
again after this deploy; that is expected.
- Cookies are now `Secure` by default (override with `SESSION_COOKIE_SECURE=0` for plain-http
dev) and logins last 12 rolling hours instead of "until the browser closes".
- Platform login no longer falls back to matching users across all tenants when the `admin`
tenant is missing — it fails with a clear message instead.
## 2.16 — 2026-07-07
- Server-side login fixes: new `flask list-users` (shows every user with tenant, active flag, and
whether a password is set) and `flask set-password <tenant_slug> <email> <password>` (resets a
password and re-activates the user). Shipped as `list_users.bat` and `set_password.bat`.
- Login label corrected to **Email** — there is no separate username; sign-in is by email only.
## 2.15 — 2026-07-07
- Deploy is now **self-verifying**: a failed server build can no longer hide behind a stale
container. `bootstrap` always signals completion (even on failure) and reports `BUILD_RESULT`;
`deploy.bat` prints the build log, container status, web logs, and a host-vs-container version
`DEPLOY_CHECK=PASS/FAIL` so we always know exactly what is running.
- Login page reloads itself if the browser restores it from back/forward cache (bfcache), so a
stale snapshot can never be shown to the user.
## 2.14 — 2026-07-07
- Settings: **Test** buttons in Mail and Telegram boxes with on-screen feedback (send a real
test email; validate the Telegram bot token via getMe). Uses the values currently in the form.
## 2.13 — 2026-07-07
- QW8: per-tenant **Settings** (subnav button) with three boxes — **Mail (SMTP)**, **Telegram**
(bot token), and **Notifications** (emergency threshold days, advance-notice hours). Stored
per-tenant in `tenant_setting` with RLS.
## 2.12 — 2026-07-07
- Diagnostic logging (per-request + login) now OFF by default, gated behind MWM_DEBUG_LOG=1.
Root cause of the recent 'stuck version / intermittent login' was stale BROWSER CACHE from the
pre-no-cache era, not a server bug (logs proved login worked every time).
## 2.11 — 2026-07-07
- Temp: login diagnostics to pinpoint the direct-tenant-login issue (RLS GUC + user visibility).
## 2.10 — 2026-07-07
- QW7: **Impact catalog (ABM)** — per-tenant catalog of impact types (Logical L1/L2, Physical
P1/P2/P3, Infra I1/I2/I3, seeded by default and fully editable). MDTs can reference impact
types on the form. New `impact_type` + `mdt_impact` tables with RLS.
- Prettier platform / workspace / unknown landing pages (centered hero with the brand mark and
a real Log-in button).
## 2.09 — 2026-07-07
- Hard no-cache on EVERY page: HTTP `no-store` headers (all responses) PLUS `<meta http-equiv>`
no-cache tags in base.html, and the stylesheet is cache-busted with `?v=<version>`. Every
template extends base, so all current and future pages inherit it. (Fixes stale console /
workspace pages that looked like missing data.)
## 2.08 — 2026-07-06
- QW6: **MDT module** (the core) — create / edit / list / view maintenance windows with the
lifecycle **Drafted -> Submitted -> Scheduled -> Executed -> Closed** (+ Cancel), types
(scheduled / emergency_scheduled / ad_hoc), affected services, executor, and outcome
(success / unsuccessful) captured at close. New `mdt` table with its own RLS policy.
## 2.07 — 2026-07-06
- QW5: **Users & roles** (tenant-side) — the tenant admin manages their own users
(add / edit / activate / delete, password reset) and assigns roles. Default role bundles
(Admin, Approver, Emergency Approver, Requester, Notifications, Stakeholder Admin) are seeded
per tenant; a read-only Roles page lists each role's permissions.
## 2.06 — 2026-07-06
- QW4: **Services** module — create / edit / delete services inside a tenant, each optionally
linked to a stakeholder (dropdown). RLS-scoped; on the dashboard and tenant sub-nav.
## 2.05 — 2026-07-06
- QW3: **Stakeholders** module — create / edit / delete client organizations inside a tenant
(RLS-scoped), with the "sees all MDTs" flag. Reachable from the dashboard card and a new
tenant sub-nav.
## 2.04 — 2026-07-06
- CLI `create-admin <login> <password>` for global (platform) admins; the login form now
accepts a **username or an email**. Helper: `create_admin.bat`.
## 2.03 — 2026-07-06
- QW2: tenant users log into their subdomain and land on a **dashboard** (module cards).
- Platform console: **tenant detail** page — add / remove a tenant's login users (bootstraps
tenant access; e.g. give `rcrc` its first user).
- **Modern UI refresh**: new design system (topbar with brand mark, cards, tables, pills,
buttons) — applied across the platform console and tenant pages.
- **No caching anywhere**: `Cache-Control: no-store` on every response — fixes stale pages
like the old "Unknown workspace".
## 2.02 — 2026-07-06
- Fix 500 on platform/admin login: the RLS GUC `app.current_tenant` was set to an empty
string when no tenant is in context (admin subdomain), and `''::int` errors in Postgres.
Now defaults to `'0'` (a safe, non-matching id); platform access still flows via `app.platform`.
## 2.01 — 2026-07-06
- Auth: real login / logout with sessions; login scoped to the platform (admin subdomain)
or to the tenant of the subdomain.
- Platform console at `admin.mwm.somtik.com`: list, create, enable/disable and delete tenants
from the web, and set each new tenant's first admin login. Slugs forced lowercase + validated.
- CLI: `create-tenant` now lowercases slugs; added `delete-tenant` and `list-tenants`.
- Dockerfile sets `FLASK_APP=wsgi.py` so `flask ...` works in `docker compose exec`.
## 2.00 — 2026-07-06
- Project kickoff: v2 rebuild of the RCRC maintenance-windows tool as **MWM**, a multitenant
product served at `mwm.somtik.com` (RCRC Riyadh Bus = first tenant).
- Foundation: Docker + Postgres + Flask (app factory + blueprints), SQLAlchemy 2 models,
Flask-Migrate / Alembic.
- Multitenancy: shared DB + `tenant_id` on every tenant table, Postgres **Row-Level Security**,
subdomain-based tenant resolution middleware, platform-admin escape hatch.
- Core models: tenant, stakeholder, service, user, role, permission, user_role_grant.
- Deploy kit: key-based SSH (passwordless), non-superuser DB role for RLS, nginx wildcard-TLS.