Every release that reaches production is recorded here: what was added, what was
changed, and what was fixed. Entries are written for someone who was not
present when the work happened — state the user-visible effect, not the commit subject.
The version shown in the app footer corresponds to the entries below. Format follows
Keep a Changelog; versions follow SemVer.
[Unreleased]
Fixed
- Editing a goal and saving it could fail with no explanation. The API returned
nullfor a goal's "why this matters" and description, then refused to acceptnullback when you saved. Reading a goal, changing its title and saving it was rejected. It now accepts what it hands out.
- Goals were never counted against a life area, and it looked like the link did not exist. Every Fulfillment area page reported "Active Goals: 0" no matter how many goals you had. The link was there all along — goals have carried a life area since the table was built — but the goals API silently dropped the field on its way out, so nothing downstream could see it.
Goals now carry their life area, can be filtered by it, and the area pages
show a real count. An area with no goals still shows 0, because that emptiness
is worth seeing; a count that could not be loaded now says so instead of
showing a zero it has not earned.
- The Goals page never finished loading, and it was a duplicate of Commitments anyway.
/goalssat on "Loading…" indefinitely while the API behind it returned all 134 of your records. It turned out not to be a separate feature at all:/api/goalsreads the very samecommitmentstable that the Commitments page reads, and simply renames the fields on the way out. One set of records, two screens, one of them broken.
Goal creation and every link that pointed at /goals — the "Create Goal"
card on the Fulfillment dashboard, the menu entry, the header links, the
insight and recommendation buttons — now go to Commitments, which works and
which carries the completion, renegotiation and integrity tracking. The
/goals address still works and forwards there, so bookmarks are not broken.
Nothing was deleted and no records moved.
- Commitments made on the Fulfillment screen were stored only in the browser you made them in. They were written to browser storage and never to your account, so they were absent on every other device and clearing site data would have removed them. They are now saved to your account like everything else.
If you have commitments still sitting in this browser, the Fulfillment screen
now offers to move them across and tells you how many it found. It asks
first, it never deletes the browser copy, and running it twice cannot
duplicate anything.
Adding a commitment there also used to say "Commitment added successfully!"
whether or not it had saved. It now only confirms when the save actually
happened, and shows the reason when it does not.
[1.15.0] — 2026-09-04
Fixed
- The Toxicity Chart was connected to nothing, and you were right to suspect it. Its thirteen levels and trends were written into the page. So was its "reset boundaries" button, which changed a number in memory and told you it had worked. There is no toxicity table, column or endpoint anywhere in the application — nothing was ever recorded, and nothing could grow.
The chart, its three views and all its controls remain. Areas with no reading
now show nothing rather than a value, because drawing them at zero would
claim you have no toxicity there. The reset button is disabled with a note
saying it is not connected yet.
- An invented life was loaded into the whole application. A set of hardcoded life areas — with scores, statuses, and an area called "Music Production" that is not one of yours — was used on first load, whenever your account had none, and whenever the request failed. It also offered areas the server would refuse to save.
- A failed load no longer looks like an empty account. Four screens caught a failure, threw it away, and showed "you have nothing here". Their error messages existed but could never appear. They now tell you something went wrong and offer to retry.
- A failed reading no longer shows as a zero. A failed score query rendered every life area at 0; a failed notification count rendered as "all caught up"; a failed lookup rendered an area named "Unknown Area". Each now says it could not load.
- A failed read no longer discards your saved emotions. The emotion settings screen quietly replaced your saved selection with the defaults when a read failed, and saving from that state would have overwritten your real choices. It now keeps your data, blocks the save, and says so.
- Saving your life areas now explains what went wrong. It named the area that failed and withheld the reason, so pressing the button again produced an identical message and looked like nothing happened.
[1.14.0] — 2026-09-04
Changed
- The banned vendor is gone from the application code. 224 files moved onto a neutral data layer — every screen, every hook, and 189 of the server routes. What remains is a handful of deliberate exceptions, each recorded with the reason it was left.
The application never actually used that vendor at runtime; it has been
talking to its own database for some time. What changed is that its name is
no longer written through the code.
Fixed
- Connecting an external account was impossible. Every attempt crashed on a function that does not exist, before any account was linked and before the session was saved, and reported only a generic error. That is the same connection path the settings page was wrongly claiming had already succeeded.
Known — reported, not changed
- One server route reads a differently-named key and therefore runs with ordinary permissions. Moving it onto the shared client would silently give it administrator rights, so it was left alone for a person to decide.
- Four routes identify which workspace a request belongs to using information the new layer does not carry. Migrating them would have silently lost that, so they stay as they are.
- The health check decides whether to test the database by looking at a setting the application ignores, so it can report the database as unconfigured while the database is working perfectly.
[1.13.0] — 2026-09-04
Fixed
- Your life-area scores were never being saved. Not one, ever — including the twelve the screen reported as saved. Every value you set went nowhere, and the only area that told you so was Finance.
Saving now works, and more importantly a save that did not persist can no
longer claim it did: after writing, the app reads the row back and compares
it against what you asked for. If they differ it says so, naming the field,
instead of moving you on.
The onboarding screen no longer trusts a successful response either. It
checks that each reply carries back the exact figure sent, then re-reads the
whole set before it lets you continue.
- Even a working save would have left your dashboard showing zero. The dashboard reads a different column from the one your answers are written to, and treats an empty value as 0 — so every area would have scored 0 and all thirteen would have appeared to need attention. The three score columns now stay in step, and legacy writes are promoted into the authoritative one, so screens that read the old columns are fixed too.
No figure is rescaled and no column is dropped.
- Reordering your life areas reported success even when it moved nothing.
[1.12.1] — 2026-09-04
Fixed
- Your integrations page said Notion, Google Calendar and Gmail were connected. None of them were. It also showed sync times — "2 hours ago", "5 minutes ago" — for syncs that never happened. The page makes no network request of any kind and never checked anything. It now says plainly that no integration is available yet and that it is not reading your connection status from anywhere.
Pressing Connect also did nothing silently. It still cannot connect anything,
because there is nothing to connect to yet, but it now says so instead.
- The check that catches invented data was not looking at one of the places it lives. It skipped the folder holding app-wide shared state, so anything fabricated there was reported clean forever. That folder is now scanned.
[1.12.0] — 2026-09-04
Removed
- The hostel check-in and booking code has been retired, with your permission. It belonged to a different product — rooms, beds, check-in dates, payment status — and no screen in this application could reach it. Nothing that was part of WisdomOS was touched. It remains recoverable from version history.
Changed
- Settled a contradiction about student grants. One record said a Student Practice Grant required proof of student status; another had deliberately switched that off. The second is now treated as the decision, so the two agree and the check that had been failing on every change now passes.
In plain terms: a Student Practice Grant is issued without proof of student
status — six months on the full plan, extendable to twelve. The verification
step still exists and is switched off rather than deleted, so turning it back
on is a one-line change whenever you want it.
[1.11.3] — 2026-09-04
Fixed
- Two automated checks had been failing without ever running. Both errored on a missing permission before reaching the thing they were meant to check, so they reported the same red as a genuine failure. One of them, now that it runs, correctly reports a real result.
Known — needs a decision
- Student Practice Grants are being issued without proof of student status. Two records disagree: the specification says verification is required, and a later change deliberately switched it off, with "disable verification" in its own title. Whichever is right, having both on file is the actual problem. Recorded rather than silently resolved, because either answer changes who can get a six-month free plan.
- Automated code scanning is switched off for this repository, so those checks cannot pass. That is a repository setting, not a code change.
[1.11.0] — 2026-09-04
Fixed
- Fifteen places in the app were asking the database for journal data in a way it was configured to refuse. Journal state and media, the wisdom coach, AI artifacts and journal AI actions all failed on every request, and had done for some time. Because a failed lookup here returns "nothing" rather than an error, each one drew an empty screen instead of saying anything was wrong.
- Your recommendations were being written by a model told your energy was 70/100 — a number you never gave it. When the real figures were missing, three invented ones were passed into the prompt, so the advice you read was reasoned from a measurement that did not exist. The prompt now leaves out what has not been measured, and says so plainly when nothing has.
The same figures were being saved to your record, including a trend of
"stable" for weeks with no readings at all — indistinguishable, afterwards,
from a week that really was steady. Those are now stored as "not measured".
- The autobiography link feature addressed a part of the database that does not exist, so every link failed while the screen reported success and showed an empty list.
- Every fulfillment score has been calculated without its boundary penalty, because the table that penalty reads from was never created. Scores were therefore higher than they should have been. New scores apply it; figures already stored are unchanged.
- A shared-cluster lookup and a link listing both reported "no results" when the underlying read had failed.
Security
- There has been no security audit trail. Sign-in failures, rate limits, blocked requests and suspicious activity were all written to a table that was never created, and the error was discarded without even a log line. The table now exists and is append-only: you can read your own record, and nobody — including you — can edit or delete it, because an audit log a subject can rewrite proves nothing.
- Safety detections were never recorded. The screening itself always worked and returns its result immediately, so no guidance was ever withheld from anyone; what was lost is the record that a detection happened. Nothing survives from before this change.
- Two lookups let any signed-in person read another person's founder declaration by passing their identifier. Both now refuse.
[1.10.0] — 2026-09-04
Security
- Any signed-in person could rewrite any clinic's data-retention settings. The rule meant to restrict this to clinic administrators had no condition in it at all — the comment above it said "clinic admins can manage", the rule itself said "anyone". Someone could have set a clinic's retention to one day and switched on automatic purging, arming a mass deletion that would fire the moment a purge job was ever turned on.
Proven by carrying out the attack, not by reading the code: an unrelated
account changed another clinic's retention from ten years to one day, and
deleted a row outright. Both are refused now. It was the only rule of its
kind in the entire database — a mistake, not a pattern.
Reading those settings is still open to any signed-in person. That is
recorded rather than hidden: narrowing it needs a clinic-membership model
that does not exist yet.
Fixed
- If your interpretations could not be read, you were told you had none. The screen answered "success, zero results" whenever the underlying read failed, so an outage was indistinguishable from an empty notebook. The list now says something went wrong.
- Linking an interpretation to an entry half-worked, silently. It called a database routine that does not exist, and the failure was discarded, so the two halves of the link drifted apart from the very first one.
- Deleting an interpretation removed its links before checking you owned it, and reported success even when nothing was deleted.
Known
- Automatic data retention has never run. The tables it needs were never created and nothing schedules it, so it has been reporting "0 records processed" as a clean success. Recorded plainly rather than provisioned to look finished — making the schema exist without a scheduler would make the system look compliant while changing nothing.
[1.9.1] — 2026-09-04
Security
- A new check now refuses to build if a database table is added without its access rules. Because of how this database is configured, those rules are the only thing standing between a table and an anonymous stranger — the permissions written into individual migrations are overwritten on every restart. Until now that depended on whoever wrote the migration remembering. It is now enforced, and the check tests itself first so it cannot quietly stop working.
Seven existing tables are recorded as known exceptions. All seven hold
reference data — lists of regions, institutions and category names — and no
personal content. The list is frozen: nothing may be added to it.
[1.9.0] — 2026-09-04
Added
- Organizations and member roles now exist in the database. Multiple people can belong to one organization with owner, admin and member roles, and the rules keeping one organization's data away from another are enforced by the database itself rather than by application code remembering to check.
Those rules were tested by attacking them: reading another organization's
rows, adding yourself to one you do not belong to, promoting yourself,
moving a record across organizations, an administrator minting an owner, and
an anonymous request after a restart. All refused.
Fixed
- Creating an organization would have failed with a confusing error, and an organization could not be deleted at all — which also made the account that created it undeletable, blocking the legal right to erasure. Both were found by attacking the new rules before release rather than after.
[1.8.0] — 2026-09-04
Fixed
- You could never record how a life area was going. The one screen in the product that asks for that sent a field the table does not have, so every submission you ever made was rejected before it was stored. Nothing recorded it, and nothing told you.
That is also why every fulfillment figure read 50. With no scores to read, the
calculation substituted a placeholder for every area and multiplied it out —
the same number for every person, every time the page loaded. The placeholder
is gone: an area you have not scored now reads as not scored, and the overall
figure is withheld rather than invented.
- Marking a notification as read never did anything. The screen reported success and changed nothing; the notification came back on the next load. Creating one returned an id for a record that was never saved.
- Email and text notifications have never been delivered. Both looked up the recipient in a place that does not exist and reported "user not found". They now report the real reason instead of a misleading one.
- Two progress markers could never be earned, because the code counting them was reading somewhere nothing is written. It reads the right place now.
- A practice history you never had. The daily practice screen showed a 45-day record, a 14-day longest run and a 7-day current run to everyone, including a brand-new account. Those figures were written into the page. The cards remain; they now say "not recorded" until there is something to record.
Security
- A disabled migration carried a policy that would have let anyone write into any person's notifications — including an unauthenticated stranger, with a link of their choosing, displayed inside your own notification bell. It was named as though it were restricted to the server. It was not restricted at all. Rewritten before use, and the exploit reproduced first to be sure.
- Filed for follow-up: every table in the database grants full access to anonymous callers on each restart, so the access rules written into individual migrations are not what is protecting your data — row-level security is. It does hold, and that was tested directly, but it means one table shipped without it would be open. A build check is proposed.
[1.7.0] — 2026-09-04
Fixed
- The contribution dimension tiles did nothing. Pressing Doing set a value that nothing read — not even the tile's own border, which was a plain hover effect. The list now filters, a second press clears it, a "Showing N of M" bar makes the filter visible, and a dimension with nothing in it offers to add one rather than showing a blank panel. The tiles are now real buttons, so they can be reached by keyboard and read by a screen reader.
- The contribution panel appeared twice on the same screen, each copy with its own state, so once filtering worked the two would have disagreed.
- Choosing a tag or a life area saved the relationship early. Those controls sat inside the form without a type, and an untyped button inside a form submits it. Picking a tag committed the record before you had finished filling it in.
- The overflow menu on a relationship card was invisible. White glyph on a white card: the card had no dark-mode colour, the page sets white text in dark mode, and that button style is the only one that sets no colour of its own. It was clickable the whole time, which is why it read as missing rather than broken.
- The curated teachings page had never shown anything real. Its content was a hardcoded sample — quotes with invented view counts and approval dates — displayed whenever the request failed. The table it reads had never been created, so the request always failed, and that sample was the only thing the page had ever displayed. The table now exists, seeded with your own 21 entries, and a failure now shows an error instead of invented quotes.
- Contacts' Email and Call buttons now work where we hold that detail, and are hidden where we do not. Controls with nothing behind them are disabled with an explanation rather than left silently inert.
Added
- A written assessment of the framework upgrade that clears 21 outstanding security advisories, with the actual scope measured rather than estimated.
Known
- Several features still query tables that were never created on this database. Work is under way; the teachings library was the first.
[1.6.4] — 2026-09-04
Fixed
- The container image would not build. Moving a dependency setting to the file the package manager now reads was correct, but that file was not being copied into the image, so the build failed on a mismatch while local installs kept succeeding. Confirmed fixed by building the image layer, not by reading the change.
[1.6.2] — 2026-09-04
Security
- Patched the three critical dependency advisories, including one in the sign-in library where an email address was validated before being normalised — a route to signing in as someone else using a lookalike address. Also patched a PDF-export injection issue and a test-tool advisory.
- Restored a dependency pin that had silently stopped working. The pin was written in a place the package manager stopped reading two major versions ago, and says so only as a warning line buried in install output — so the package it was added to hold back had been free to drift for some time with nothing reporting it. Moved to where it is actually read, and confirmed applied.
Known — needs your decision
- Sixteen further advisories are in the web framework itself and are only cleared by a major-version upgrade, which changes how every server route reads its inputs. Not attempted here; it needs its own pass with its own testing.
[1.6.1] — 2026-09-04
Fixed
- The version in the footer could not tell you which build you were on. It read "v1.5.0 · dev" on every production deploy. The build configuration was looking for a commit variable that only exists on Vercel, and this app runs on Fly — so it fell back to a placeholder every time. The commit was being passed correctly the whole way and simply had nothing reading it.
The footer now shows the real short commit and links to it. Verified by
building with a known commit and reading it back out of the generated page.
[1.6.0] — 2026-09-04
Integration of nine parallel workstreams.
Fixed
- Deleting a communication log entry answered 405 and silently did nothing. The relationship page had always issued that request; the route never implemented it, so the entry stayed on screen. Deleting now also recalculates the "last contacted" date on the relationship — the database trigger that maintains it only fires on insert, so removing your most recent entry used to leave a relationship looking more recently contacted than it was.
- The interactions log showed invented entries and could not record real ones. There was no endpoint behind it. There is one now, and the page reads and writes through it.
- Emotion customization was unreachable. Two links pointed at a settings page that did not exist, so both 404'd, while the customization panel behind them was already built and working. The page now exists and is in the settings menu.
- Three pages existed that nothing linked to. A contract test now fails the build when a page is added without being registered, or is registered as visible without appearing in any menu — the condition that makes a finished feature look missing.
- Insight confidence percentages were random numbers. A figure rendered as "N% confidence" beside an AI insight was generated, not measured. No badge is shown now unless the model states a confidence.
- A hardcoded paragraph described every user's patterns as their own — naming a "Morning Clarity" pattern and "peak hours (6-9 AM)" regardless of what their entries said. It is now derived from the patterns actually loaded, and the card is hidden when there are none.
- "View Full Analysis" did nothing. There is no deeper analysis view; the summary is the whole result. The control now says so rather than sitting silently inert.
Added
- A guard against silently-skipped database migrations. The migration runner keys applied migrations by their number alone, so two files sharing one number meant the second was skipped on every boot — permanently, with no error and a deploy that still reported success. Two workstreams had in fact chosen the same number. The build now fails on a duplicate instead of losing a migration.
- Wisdom Coach sessions, with per-trigger effectiveness measured from your own sessions: for each thing that started a session, the share of the recommendations it produced that you acted on.
- Onboarding and registration flows, and a journal entry edit screen.
Changed
- Completion rates read "no rate yet" instead of "NaN%" when nothing has been recorded.
[1.5.0] — 2026-09-04
Fixed
- Contributions could not be saved in the Transforming, Creating or Stewarding categories — the failure you recorded in your own journal. The table still enforced the original three-value taxonomy (
being,doing,having) from the very first migration, while every screen writing to it had moved to the five-axis model. Saving anything outside those three was rejected by the database and surfaced as an unexplained error. The constraint now matches the taxonomy in use, and existing rows stay valid.
It stayed hidden because the schema file records only type String: a database
CHECK constraint is invisible to schema introspection, so no tooling in the
project could see it.
- The Contributions screen reported success on every failed save. It sent fields the handler did not accept, and checked only whether the request succeeded with no branch for failure — so a rejected save looked identical to a saved one, and every card it drew came back blank.
Changed
- The four separate Contribution screens are now one screen with tabs. The old addresses still work and redirect. The redirect is deliberately temporary rather than permanent, so the change stays reversible until you have seen it.
[1.4.0] — 2026-09-04
Fixed
- A clinician opening any patient URL was shown invented clinical readings. The patient page ignored the id in the address entirely and generated mood, sleep and medication-adherence figures at random, presenting them as that patient's record. It now resolves the id against the real consent roster: no consent shows a refusal, consent with nothing shared says exactly that, and no reading is ever estimated.
- Two screens replaced your real data with invented data when a request failed. Behavioural patterns and founder teachings both returned a hardcoded sample on any error — including an expired session — so a silent 401 looked like a normal page of results. Both now surface the failure instead.
- An invented energy score of 70 was being written into the database whenever the real average was missing, and a real score of 0 was discarded along with it. Nothing is persisted now when there is no measurement.
- Congratulations, partners and completion figures were fabricated per account. A 7-day meditation streak nobody earned, named accountability partners with invented support scores, and a 100% completion rate for users with no follow-ups at all.
Changed
- Sample content is now confined to demo mode and never appears on a live account. It lives in one place, behind one flag that is absent from the production configuration, and every demo screen carries a persistent banner so sample data can never be mistaken for your own.
Added
- A build guard against invented user data. It fails the build on unexplained random values in product code, so the class of bug fixed above cannot come back unnoticed. It is self-tested: a guard that has stopped catching anything fails loudly rather than passing silently.
Changed (internal)
- The schema guard now also checks the fields written by inserts and updates, not only the ones read back, and carries a deliberately-failing case so a guard that has stopped working cannot report success.
[1.3.0] — 2026-09-04
Fixed
- Nine features were locked for everyone, with no way to unlock them. Priority Matrix, AI Insights, Advanced Analytics, Export, Wisdom Coach, Difficult Conversations, RCI Scores, Voice Journaling and Custom Life Areas all sent you to a "forbidden" page. Every account is created on the free plan, those features are off on the free plan, and there is no way to buy a higher one — so the gate could never be lifted by anyone, including the owner. The founder tier now unlocks them.
Paid-plan gating is otherwise unchanged; this affects the founder tier only.
[1.2.5] — 2026-09-04
Fixed
- The health check was reporting the wrong version. It read a value that is only set when the app is started through a package script, which is not how it runs in production — so it reported
1.0.0while the app was actually serving 1.2.4. The one machine-readable signal about which build was live was wrong.
Added
- Deploys now prove they landed. Health reports the exact commit running, and the deploy pipeline checks that production is serving what was just built before calling the deploy a success. Previously a failed build left the old version running with nothing to say so — new work silently stopped arriving for six releases, and it was only caught by hand.
[1.2.4] — 2026-09-04
Fixed
- Deploys can complete again. The build had been failing for several releases — dying partway through with no error message at all, while the same code built cleanly on a developer machine. Production kept serving the previous version throughout, so nothing broke for anyone using the app; new work simply stopped reaching it. This is the third attempt at a fix, and the first two are left in the log below rather than tidied away.
[1.2.3] — 2026-09-04
Fixed
- Deploys now build somewhere with enough memory. The previous release tried to fix the failing build by giving it more headroom. That made it worse — the build died sooner rather than later, because the limit being hit was the build machine's, not the setting that was raised. Builds now run on the CI runner, which has more memory to give, and the setting is sized to stay under the limit rather than above it.
[1.2.2] — 2026-09-04
Fixed
- Deploys were failing silently. The production build died partway through with no error message — just a non-zero exit — while the identical code built cleanly on a developer machine. Nothing about the application changed; it simply could not be packaged. First attempt at a fix did not work (see 1.2.3).
[1.2.1] — 2026-09-04
Fixed
- Real people's names are out of the product. Two first names appeared in 18 files — including a server API route that returned one in its response to every user, and the database seed, which planted them into any database it was run against. Anyone using the app could see a named individual alongside a relationship "status" and, in one case, a personal message written to them. All product occurrences are replaced with obviously-synthetic placeholders, and a stale
.bakcopy of the API route is deleted.
A CI check now fails if a name is reintroduced, and it is tested against a deliberate
bad case so it cannot quietly stop working. Three infrastructure files that reference
a GitHub *organisation* of the same name are excluded and documented — rewriting those
would break repository tooling.
Nothing was removed: every affected screen renders exactly as before, with placeholder
names in place of real ones.
[1.2.0] — 2026-09-04
Added
- A schema guard that catches the bug class TypeScript cannot see. About 140 of the 227 API routes address the database by string literal —
.from('table') .select('column')— so the compiler has nothing to check them against. That is why several features shipped broken and stayed broken: email verification could never succeed, every admin route returned "unauthorized" without ever running its permission check, and the knowledge-graph screens failed on every call. All of them compiled cleanly.
The new check reads both the Prisma schema and the SQL migrations, then verifies
every table and column name used in the code actually exists. It runs in CI on
every change, and it proves itself first: before reporting, it injects a
deliberately wrong table and column and confirms it catches both. A check that
quietly stops checking is worse than no check at all.
It reports rather than blocks for now — the current backlog is 261 findings, and a
gate that fails on day one gets switched off instead of fixed.
[1.1.4] — 2026-09-04
Fixed
- The security settings page showed invented devices and three controls that did nothing. "Active Sessions" listed a hardcoded MacBook Pro, iPhone and iPad with invented locations and times — the same three for every user, with no request made anywhere. Worse, "Sign Out All Devices", the per-device "Sign Out", and "View Backup Codes" had no handlers at all. Someone who believed their account was compromised could press "Sign Out All Devices" and be left thinking they had secured it.
The invented devices are gone, replaced by an honest explanation and the one action
that does work today — changing your password ends every other session. The controls
are visibly unavailable rather than silently inert. Nothing was removed: the section,
its copy and its controls all remain, and re-enabling is deleting disabled once
session listing exists. That needs a real change — NextAuth runs the JWT strategy
here, so there are no session records to enumerate.
[1.1.3] — 2026-09-04
Fixed
- The changelog page was empty in production.
/changelogcompiled, deployed and returned 200 — but rendered "the changelog could not be read for this build" instead of the release history, because.dockerignoreexcluded*.md, soCHANGELOG.mdnever entered the Docker build context. The page reads the file at build time, so the miss was silent: nothing failed, the content simply was not there. Caught by opening the page in production rather than trusting the build. The file is now included, the build logs loudly if it ever goes missing again, and the fallback links to GitHub rather than dead-ending.
[1.1.2] — 2026-09-04
Fixed
- "Deactivate account" silently did nothing. The confirmation button in the deactivation modal had no handler, and no deactivation endpoint exists anywhere in the app. Pressing Confirm did nothing at all, leaving people believing their account had been deactivated when it had not. The button is now visibly unavailable with a direct route to a human, rather than failing in silence. Nothing was removed — the entry point, copy and modal all remain, and re-enabling is one line once the backend exists.
[1.1.1] — 2026-09-04
Security
- Self-serve tenant creation is closed.
POST /api/tenants/createhad no authentication and no rate limit, and it created a pre-verified owner account on any plan the caller named — including ENTERPRISE, which carries a 999,999 user cap and 1TB of storage. Anyone could provision one with a single request, and there is no payment processor, so nothing charged for it. The original handler is kept intact so it can be re-enabled once it has authentication, a rate limit, plan restriction and no email pre-confirmation.
Fixed
- The insights API no longer invents your data. When the pattern query failed it returned a hardcoded week of energy, focus and fulfillment scores — labelled "mock data for development" but running in production, and trending upward toward the weekend so it read as a real finding. There was no way to tell it was fabricated. It now returns an honest error the app can show as "couldn't load this".
- Removed Vercel Analytics. The app runs on Fly, so the script it injected pointed at a path that does not exist; it 404'd on every page load for every user and logged a console error each time.
[1.1.0] — 2026-09-04
The first release after a full audit of the live product. Several paths that
looked fine in code had never worked for a real user.
Fixed
- Google sign-in completed and then threw you out. NextAuth was handing off to
/auth/callback, which is the old Supabase handler; it found nocodeto exchange, treated that as failure, and redirected to a login error. Sign-in now completes to the page you asked for. - Nobody could create an account. Every "Get Started" button on the landing page called the Supabase OAuth client, which returns a silent 501 in this deployment — so the buttons did nothing at all, with no error. The product could only be entered by people who already had an account.
- No navigation below 1024px. The menu was desktop-only and the mobile drawer, bottom bar and sidebar existed in the codebase but were never mounted. On a phone — or a laptop window narrower than 1024px — there was no route to any screen.
- `/founder` and `/admin` were unreachable by everyone. The signed-in session carried no access tier or role, so every user resolved to the lowest tier. Nothing in the product could grant the founder tier at all.
- The clinician workspace could not be entered. Two screens linked to an onboarding page that did not exist, gated behind a flag nothing ever wrote.
- Invented data was shown as your own. Unscored life areas were filled with random numbers, so the Global Fulfillment Score changed on every page load. The insights screen fabricated a week of figures whenever a request failed. A demo seeder wrote a fictional autobiography and journal into every visitor's browser on first load.
- Signed-in requests were rejected. API routes required a bearer token the app never sends, so anything reading the session cookie returned 401 — including the Goals page, which redirected you away on every visit.
- Commitment details could not be saved. The client sent
PATCHwhere the route only acceptedPUT, and every method required a header the page never sent. - Email verification could never succeed — it read three database columns that do not exist.
- An open redirect: the post-login destination was taken from the URL unchecked.
Security
- Two privilege-escalation holes closed. Anyone could register with an admin role embedded in their sign-up and then read platform-wide data. Separately, any signed-up user could suspend every clinician on the platform through a database function with no authorization check — verified by running the attack, then confirming it blocked.
- Unauthenticated callers could read *and delete* a backup table containing every user's journal entries. Row-level security is now enforced on every table.
/api/bookingsreturned guest names, emails, phone numbers and payment status to anyone, with no authentication.- Removed a signing secret that was hard-coded in this public repository and used to verify tokens as well as issue them.
Added
- Version number in the footer, linked to the exact deployed commit, plus this changelog at
/changelog. - A project-scoped agent team under
.claude/agents/with a shared learnings file, so work handed between agents does not restart from zero. - Security headers and error reporting configuration merged into the config file that Next.js actually reads — the previous one had never been loaded.
Changed
type-checknow generates the Prisma client first. Without it, queries against columns that do not exist compiled cleanly, which is how several of the bugs above survived.- Copy across the app no longer uses encouragement or gamification language, per the product's AI Silence Canon: praise attached to mood and commitment data has been replaced with factual statements, and a "Limited Time Offer" badge that had no expiry behind it was removed rather than reworded.
[1.0.0] — 2026-01-03
Initial baseline.
- Sovereign canon enforcement across repos
- RampUP delivery engine
- UX trust layer (navigation, footer, settings, feedback)
- Evidence vault
- Agent governance and register enforcement