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]
[1.21.33] — 2026-10-16
Changed
- fix(journal): Extract Domains attaches your own life areas (#1596) (#1597)
[1.21.32] — 2026-10-01
Added
- Matter completed-work points with an auditable record, reversible ordinary and milestone classifications, and duplicate-submit protection.
- User-entered Toronto follow-ups with private in-app upcoming/overdue reminders and recorded completion/rescheduling.
[1.21.31] — 2026-10-01
Added
- Searchable matter-name register grouped by Life Area, with compact Matter/Subject rows and distinct-matter totals across overlapping groups. Names remain as supplied; partial reads and account changes are handled explicitly.
[1.21.30] — 2026-10-01
Changed
- fix(journal): resolve legacy Life Area facet labels (#1586)
[1.21.29] — 2026-10-01
Added
- Private local matter-name mapping preview and owner-scoped register linked to multiple Life Areas. Names-only import sends no document contents or local paths.
- Inactive metadata monitoring and reviewed-change primitives with durable offline reconciliation; access, credential issuance and transmission remain separately gated. Fresh imports have no commitment links or available commitment-date proposals.
Fixed
- A sender batch reports partial completion while metadata events remain queued, preserving a truthful nonzero result.
[1.21.28] — 2026-10-01
Fixed
- Life Domain cards, chevrons and commitment counts now open their matching workspace and lists. Domain details show linked goals and subdomains; unmeasured scores and unassessed health have a clear explanation and assessment link.
[1.21.26] — 2026-10-01
Changed
- fix(today): readable badges and actions; isolate lifecycle dialog renders (#1565)
[1.21.23] — 2026-09-30
Changed
- fix(schedule): stop stacking sessions at 16:00; show session titles on /today (#1560)
[1.21.20] — 2026-09-28
Changed
- fix(journal): load Life Domains only when the picker opens (regression from #1543) (#1552)
[1.21.19] — 2026-09-28
Changed
- docs: recover screen-feedback plan and design spec (2026-09-23) (#1537)
[1.21.18] — 2026-09-28
Changed
- fix(journal): life_area_ids must be the caller's own life areas (POST + PATCH) (#1549)
[1.21.17] — 2026-09-28
Changed
- feat(journal): Life Domain picker in the composer toolbar (one primary, any secondaries) (#1543)
[1.21.16] — 2026-09-25
Changed
- feat(goals): move objectives to another goal (wave 2D) (#1509)
[1.21.15] — 2026-09-25
Changed
- feat(goals): add and delete milestones on an existing objective (wave 2F) (#1507)
[1.21.14] — 2026-09-24
Changed
- feat(goals): objective detail panel — progress log, milestones, per-objective resources (wave 2A) (#1495)
[1.21.13] — 2026-09-24
Changed
- feat(goals): status state machine, header status control, purge gated on archived (agile spec slice 1) (#1486)
[1.21.12] — 2026-09-24
Changed
- fix(export): binder PDF — section title cards on their own page; no duplicate sections (#1478)
[1.21.11] — 2026-09-24
Changed
- feat(goals): Goals section completion — integration of #1470 #1466 #1464 #1463 #1469 #1467 #1471 #1454 #1452 #1458 #1460 (#1475)
[1.21.10] — 2026-09-24
Changed
- feat(goals): complete Goal CRM lifecycle (#1447)
[1.21.9] — 2026-09-24
Changed
- fix(autobiography): restore Quick Navigation completion colors (#1443)
[1.21.8] — 2026-09-24
Changed
- test: guard screen feedback page-flow placement (#1438)
[1.21.7] — 2026-09-24
Changed
- fix(db): recover lifecycle migrations skipped by historical version aliases (#1436)
[1.21.6] — 2026-09-24
Changed
- fix: complete Phoenix Upset and Journal repair (#1425)
[1.21.5] — 2026-09-23
Changed
- fix: prevent workspace boundary crashes (#1420)
[1.21.4] — 2026-09-23
Changed
- Publish Life Book Heat Map and resilient error UI (#1417)
[1.21.3] — 2026-09-23
Fixed
- Repair Journal Patterns actions for records displayed from either pattern store, with clear inline errors when changes cannot be saved.
- Make pattern text and actions readable in dark mode, including expanded details, ratings and keyboard focus, with text no smaller than 12px.
Added
- Find temporarily hidden patterns in a Hidden view and restore them with Unhide without losing their content, rating or lifecycle status. Never show remains a separate suppression choice.
[1.21.2] — 2026-09-23
Changed
- Fix relationship heatmap RPC type mismatch (#1410)
[1.21.1] — 2026-09-23
Changed
- Explain how to use Contribution Targets (#1408)
[1.21.0] — 2026-09-22
Fixed
- Restore the in-app release history by including it in the production package; fail packaging if the application cannot read the history file.
- Prevent a slow saved-day response from replacing a new archetype selection or the plan for a newly selected date.
- Keep Day Design intention controls within mobile screen widths and make example activity labels and times readable in light and dark mode.
- Restore signed-in navigation and pages by providing workspace state above the shared header and page content. Includes regression coverage for the authenticated root layout and public sign-in page.
- Refresh workspace state on account changes, reject stale workspace selections, and show failed loads separately from empty results.
- Wait for account permissions to finish loading before redirecting a signed-in person to an access-denied page; preserve real access restrictions and retry behavior.
- Restore existing goal markers and lifecycle filters without changing commitment classifications; preserve importance and metadata, restrict related goal records to their owner, register shipped routes, and include omitted personal content in account export and erasure.
- Make interrupted goals/workspace migrations safe to resume and keep the manual family-side rollback outside automatic startup migrations.
Added
- Expand Day Design with ten additional archetypes under More archetypes, while keeping the original six visible. Saved selections and intentions survive reloads, including older saved values.
- Provide Day Design descriptions and example schedules in all seven supported languages. Examples remain suggestions and do not create calendar bookings.
- feat(habits): habit design editor modal (
HabitDesignEditorModal) supporting in-place editing of habit identity statement, cue type, cue detail, routine, minimum viable version (MVV), reward, motivation/why, and tracking target. - feat(habits): target-focused Never-Miss-Twice flows linking "shrink to MVV" and "move the cue" recovery prompts directly to highlighted fields in
HabitDesignEditorModal. - feat(habits): habit conversion modal (
HabitConvertModal) allowing promotion of habits into structured commitments or goals with automatic routine bullet-point parsing for child habit splitting. - feat(habits): 7-point Self-Report Behavioural Automaticity Index (SRBAI) rating modal (
HabitAutomaticityModal) with live index calculation and historical score tracking. - feat(habits): full detail page integration on
/habits/[id]with header actions, interactive automaticity tile, design loop editor, linked commitment indicator banner, and complete English/French translations.
Security
- fix(integrations): remove
/api/auth/hubspot/callbackand its alias/api/hubspot/callback— the former validated the OAuthstatewithout binding it to the signed-in caller and fell back toPRIMARY_USER_ID/ the firstusersrow / a hard-coded UUID when no state matched, letting anyone bind a HubSpot authorization code to an account that wasn't theirs./api/integrations/[provider]/callback(state scoped touser_id) is now the only HubSpot callback (DEC-0148, RISK-0115).
[1.20.44] — 2026-09-17
Added
- feat(reflective-feedback): expand all 6 categories to 2 paired dimensions each with dark design system, i18n translations, and persistence (#1337)
Fixed
- fix(upsets): high-contrast text and buttons in Quick Capture completion modal, single Saved step indicator, and removable StatusPill life-area chips (#1337)
[1.20.43] — 2026-09-17
Changed
- fix(journal): render body formatting and fetch live weather instead of placeholders (#1306) (#1308)
[1.20.42] — 2026-09-16
Changed
- fix(autobiography): person panel Save/Last contact/Notes stuck disabled (#1312)
[1.20.41] — 2026-09-16
Fixed
- Autobiography person panel: Save, Last contact and Notes no longer stay disabled (effect re-run loop).
[1.20.40] — 2026-09-14
Changed
- Maintenance release; no user-visible change recorded.
[1.20.39] — 2026-09-14
Added
- feat(account): catalog all 5 WisdomOS account tiers (Free / Explorer, Foundation, Architect, Sovereign, Organizations) under
/settings/accountwith detailed screen access, transparent pricing, and philosophical framing. - feat(account): interactive Screen Access Matrix comparing 22+ governed screens across 7 Canon navigation modes (
REFLECT,SEE,DECIDE,RELATE,EVOLVE,CLINICAL,ADMIN). - feat(account): 3-step Upgrade Sales Funnel modal with mode-filtered screen breakdown, feature specifications, 1-click Foundation checkout, and bespoke consultation intake.
- feat(profile): profile photo and avatar upload component with Supabase storage integration, automatic preview, and audit-logged deletion controls (
AvatarUpload,/api/profile/avatar).
Changed
- feat(footer): link AXAI Innovations brand credits and copyright notice to
https://axaiglobal.com. - chore(release): bump package version to 1.20.39 and verify DEC-0103 version bump arithmetic.
Fixed
- fix(integrations): support
HUBSPOT_REDIRECT_URIenvironment override and add callback alias route/api/hubspot/callbackredirecting to/api/auth/hubspot/callbackfor exact OAuth redirect URI matching. (Correction, 2026-09-17: this entry originally said the alias redirected to the secure/api/integrations/hubspot/callbackroute — it did not. Both routes were removed for a state-binding vulnerability; see the Unreleased Security entry, DEC-0148, RISK-0115.)
[1.20.38] — 2026-09-14
Changed
- feat(integrated): redesign command center for visual inclusivity, legibility, and high-contrast theme harmony (#1294)
[1.20.36] — 2026-09-14
Added
- feat(patterns): automated pattern discovery engine scanning reflections and journal entries for recurring protective loops against canonical archetypes (Canon §6.B, §7.8).
- feat(patterns): background AI inference hook in journal creation and on-demand trigger endpoint (
/api/patterns/infer) with in-app notification alerts for newly discovered candidates. - feat(patterns): Pattern Confrontation & Completion modal (
PatternCompletionModal) guiding users through protective payoff acknowledgment, sovereign identity insight ("Who I took myself to be" vs. "Who and what I actually am"), completion declaration, and optional reflection journaling. - feat(patterns): inline loop field editor for direct nuance modifications on pattern cards in Pattern Studio.
- feat(calendar): Calendar Ally panel with intelligent natural language commitment and schedule orchestration (
CalendarAllyPanel,lib/commitments/agent-orchestrator.ts). - feat(journal): Journey Journal interactive editor for structured breakthrough reflection capture.
- feat(insights): authenticated direct DB pattern analytics fallback preventing 503 errors during recommendations generation.
- feat(commitments): commitment scheduling now works on every kind of commitment, not just practices, and gains two new recurrence options — every N days, and monthly on a chosen day of the month (#1268, DEC-0120). Editing a commitment's schedule no longer rewrites its past history: WisdomOS remembers what the schedule actually was on any past day. Every occurrence check-in is now kept in a real history instead of the last one silently overwriting the previous report, you can mark an occurrence as Missed directly, and an optional reflection you add when checking in now reaches your main Journal, tagged to that commitment (#1268, DEC-0121).
- feat(integrations): a scheduled commitment now two-way syncs with a Google Calendar you choose — Phoenix pushes new/changed/paused schedules as an event, and moving, extending, or deleting that event on Google's side updates the commitment back in Phoenix (#1269)
Fixed
- fix(patterns): resolve premature JSX closing tag and nesting syntax error in Pattern Studio.
- fix(recommendations): avoid 503 patterns_unavailable failure during recommendations generation by adding authenticated fallback calculation.
- Unplanned days load as an explicit empty plan instead of a missing-endpoint error; failed reads remain errors.
- Pricing availability badges have stronger light- and dark-theme text contrast.
- Session journal ingest rejects another user's life-area ID before writing. CI fixtures now support authenticated reset accessibility coverage, and rate-limit overrides reject unsafe or excessive ceilings.
- fix(ci): golden-path's shared e2e identity was crossing the /api/* rate limiter mid-run, and the access gate then rendered "We could not load your access" for every page after — surfacing as a
/onboardingURL timeout that named nothing about the real cause. The limiter's maxima are now overridable viaAPI_RATE_LIMIT_GET_MAX/API_RATE_LIMIT_MUTATE_MAX(CI workflows only; production defaults unchanged), and the suite now fails with a message naming the access gate instead of a bare timeout. (#1256)
- fix(a11y): the primary CTA and every default button clear AA in light mode (#1247) —
--primarymoves from indigo-500 to indigo-600, taking white-on-primary from 4.47:1 to 6.29:1; dark mode unchanged./onboardingnow redirects from the server as a real 307 instead of after hydration./auth/signoutreads the app's provided auth context, avoiding missing-provider errors in development and production. The browser a11y audit stops treating/resetas public (it never was) and the step-card contrast spec signs in first. - Make commitment outcome/history writes atomic, preserve checked-in occurrences during schedule edits, enforce ownership on history links, and submit optional reflections once (#1268).
- Keep recurrence anchors/counts and next-due cache consistent; retain completed commitments' readable history and include check-ins in privacy export/erasure.
- Keep shared browser-test users within a configurable API request budget; production limits remain unchanged (#1256, #1258).
- Repair inherited accessibility CI: contrast, entrance-animation settling, authenticated reset fixtures, onboarding redirect, and signout rendering (#1247, #917).
[1.20.34] — 2026-09-14
Fixed
- fix(hooks): eliminate React Rules of Hooks violations in
SessionExecutionModal(hoist hooks above early returns and guard mutating handlers) andRelationshipCard(unconditional router hook usage). - fix(lint): ensure
pnpm run lintpasses with 0 errors across all UI packages.
[1.20.33] — 2026-09-14
Added
- feat(commitments): provide prominent "Push to Calendar" and "Push to Schedule" execution triggers across all commitment surfaces:
- Inside
FulfillmentDisplayaccordion view for each active commitment. - Inside
FulfillmentDisplaylist view. - Inside
CommitmentEntriesnote/reflection composer and header bar next to "Add entry". - Inside
LifeAreaCommitmentsexpanded view. - Slide-over
ScheduleDraweropens with pre-populated form and immediate interactive recurrence preview.
Fixed
- fix(ci): resolve CodeQL advanced security alerts:
- Eliminated loopback HTTP self-fetch SSRF in
/api/agenda/today. - Added strict object type and numeric ID validation in HubSpot webhook handler.
- Resolved clear-text logging taint flags in
scripts/secrets_audit.py,pack/root/scripts/secrets_audit.py,scripts/db/seed-e2e-users.mjs, andscripts/dev/db-up.mjs. - fix(ci): resolve Playwright strict mode locator violation on
commitment-scheduling.spec.ts(getByText(FIXTURE_TITLE).first()), unblocking Golden Path E2E and downstream Mobile Smoke Test builds. - fix(privacy): register
contribution_theme_imagestable inlib/privacy/user-data-inventory.tsanddocs/privacy/user-data-inventory.md, add required pseudonymise columns forcompliance_audit_log, and fix seed generator timestamps. - fix(cron): update
WISDOMOS_URLrepository secret to canonicalhttps://lifeintelligentlydesigned.comto eliminate Cloudflare 301 redirects on bearer-token cron jobs.
[1.20.32] — 2026-09-14
Fixed
- fix(today): eliminate flaky HTTP loopback self-fetch for habit items in
/api/agenda/todayby querying habits in-process viagetTodayHabitGroupshelper, resolving the "Habit items couldn’t load right now" failure banner caused by port collisions, dropped authorization headers, and dev server deadlocks. - fix(integrated): correctly unpack habit chains from
group.chainson the Today calendar overview so habits reliably render alongside scheduled commitments.
Added
- feat(difficult-conversations): 168 templates across 7 life areas and Phoenix phases, real-time step response capture, direct push to journal with structured debriefs, and RCI Weekend 5 Canon Decision Agent integration.
- feat(rci): Relationship Capability Index scoring engine, Weekend 5 Canon Decision Agent recommendations, and full CRUD API.
[1.20.31] — 2026-09-12
Changed
- Habits: schedule step, schedule history, Today fixes, cue-triggered habits (WP-2) (#1221)
[1.20.30] — 2026-09-12
Changed
- fix(relationships): merge moves habit links, hides merged people from pickers (#1208, D-5) (#1218)
[1.20.29] — 2026-09-12
Changed
- fix(ci): clear the three checks that have been red on main itself (#1230) (#1231)
[1.20.28] — 2026-09-11
Added
- feat(habits): habit scheduling — a "When?" step in the wizard, schedule history, Today fixes, cue-triggered habits (#1209, WP-2, DEC-0107). You can now set a real schedule when designing a habit — every day, specific days, N times a week, N times a month, every N days, or "when the cue happens" — plus an optional exact time or general time-of-day window, a duration, a start date and an optional review date. Changing a habit's schedule later no longer rewrites its past streak and consistency history: days before the change are still judged against the schedule that was actually in force then. The Today view now buckets habits by their real scheduled time (fixing "7:00pm" landing in Morning and "7am" landing in Anytime), skips days a habit isn't scheduled, and gives cue-triggered habits their own "If it comes up" group with an honest "how often" / "last done" readout instead of a fabricated streak or percentage. The final wizard step now has a "Start today" checkbox (on by default) so a new habit actually shows up on Today instead of sitting undiscoverable until you find the Start button on its own page. The routine field is now a proper text area. Reminder times can be saved on a schedule but are not sent yet — there is no delivery mechanism for habit reminders.
- feat(habits): a habit can link multiple life areas, and cues are typed (#1223, WP-1/WP-3, DEC-0109). A habit's primary life area still scores and rolls up exactly as before; you can now also mark up to 5 other areas it "also impacts," shown as toggle chips in the wizard. The cue step supports several typed cues per habit, each with its own input — an exact time, a place, another habit to follow, an emotional state, an event, or being with someone (a multi-select over your people, with an inline "add person" if they're not there yet). Fixed: a person you'd merged away as a duplicate, or whose relationship you'd marked complete or ended, no longer shows up in the people pickers.
- feat(habits): habits now show up on the life-area and person pages that involve them (#1225, WP-4, DEC-0111). A life area's page has a new Habits tab: habits scored against this area, and — separately, muted — habits that also impact it. A person's page has a new Habits tab too: habits where they're your accountability partner, and habits cued by being with them. A relationship you've marked complete still shows its habits, labelled as such, rather than hiding them.
- feat(commitments): commitment record page, Increments 0-2 (#1175, DEC-0102) — the commitment detail page now shows a lifecycle graph (Declared → Active → At Risk → Completed, with Renegotiated/Paused/Broken as branch moves available now) and the existing "Why this matters" reasons chart composed alongside it.
commitment_entriesgains a six-value action status (Undertaking/In Progress/Blocked/Delegated/Deferred/Done) with schema-enforced companion fields (Blocked/Deferred require a review date, Delegated requires an owner). A commitment now automatically moves to At Risk when two milestones are overdue within 7 days of each other, reversible at any time via the existing "Back to Active" move. Record-feed union, milestone-builder wiring, and the People/Shared-pattern sections are deferred to a later wave.
Fixed
- fix(life-areas): life-area picker no longer shows merged/retired areas (#1210, DEC-0115, WP-0) — every dropdown that lists life areas (the habit wizard, onboarding, direction review, insights, wisdom-center, setup) used to show a user's full 13-key legacy list *and* every V4 domain they had bootstrapped side by side, so the same area could appear twice under two different names.
GET /api/identity/life-areasnow returns active areas only by default; the/life-areasmanagement screen still sees retired rows (?include_inactive=1), so nothing is hidden, only de-duplicated for everyday use. Five legacy keys with an unambiguous V4 equivalent (Finance, Intimacy & Love, Spiritual Alignment, Creativity & Expression, Sexuality) are merged into their V4 counterpart for anyone who has both — existing habits, commitments, journal entries and scores follow the merge, nothing is deleted. "Justice & Ethics" gets its own icon instead of sharing the app's generic placeholder glyph. - fix(relationships): merging a duplicate person (#1137) now also moves their habit accountability link, and a merged-away person no longer appears in the habit wizard's accountability picker,
/relationships,/interactionsor/upsets(#1208, DEC-0108). Opening a merged person's own page now shows a notice pointing to the record they were merged into instead of the stale record.
[1.20.27] — 2026-09-11
Added
- feat(relationships): relationship lifecycle v6 — the termination reason registry grows from 10 categories / 103 codes to also include the owner's full 22-category / 272-code registry (Integrity, Communication Breakdown, Relationship State, Freedom to Be/Act, Fulfillment Failure, Reciprocity, Accountability, Repair Failure, Play/Possibility, Respect & Dignity, Trust, Manipulation & Control, Exploitation, Boundaries, Safety, Legal/Ethical Exposure, Repeated Pattern, Values/Direction, Attention/Cognitive Load, Natural Completion, Personal Choice), added to — not replacing — the existing catalog. The relationship-status vocabulary widens to include strained/repair-needed/renegotiation/distanced/completion-proposed/completed (completion is distinct from ended — a relationship whose purpose was fulfilled, not a rupture). A "Consider Completing Relationship" structured review walks through what happened, what was communicated, what support was sought, and what the relationship currently produces, before a user-set 0–9 disposition and a next step; finalizing writes one permanent history record that is never edited or deleted, and the relationship itself is never deleted — its status changes. A completed relationship can later be re-created in a different form (e.g. romantic partner → friend). Two consent-gated, per-item AI actions are available on request only: propose a disposition with a visible reason, and separate what was observed from the meaning added to it — both drafts the person reviews and confirms, never an automatic change. Selecting a safety concern (violence, threats, coercion, stalking, severe exploitation) skips the repair steps entirely and points to the existing crisis resources. Free-text notes that describe another named person are left out of a data export by default (#TBD, DEC-0099, RISK-0077)
Fixed
- fix(journal): a saved Commitment Chart note ("Add to Journal") could fail with a database constraint error — a 2026-09-29 migration had silently narrowed which journal entries are accepted and dropped
commitmentfrom the list. Restored (DEC-0099, RISK-0077)
[1.20.26] — 2026-09-11
Fixed
- fix(nav): the mobile-bottom-bar PR (#1080) added an un-gated
LanguageSelectorto the signed-in desktop header that pushed the row past the documented 1024-1279px budget (~40px over a ~16px margin). Hidden belowxlfor a signed-in user, the same technique and breakpoint the "Find anything" label already uses; relocated into the account menu below that width so the control is never actually unreachable. - fix(nav): the mobile bottom bar's "Decide" slot linked to
/priority-matrix(behind thepriority_matrixfeature flag — unreachable for a free-plan user) instead of/commitments(ungated). Retargeted to/commitments. - fix(a11y):
/auth/login,/auth/registerand/auth/reset-password's entrance animation (animate-auth-card-in, a 0.4s opacity/scale fade) could race the Cypress contrast gate's scan, reading a real-but-transient mid-fade contrast and reporting a violation that was gone a frame later — the same shape as the loading-splash race the gate's ownsettle()already guards against, now guarded for this animation too. - i18n: 26
commitments.*keys that shipped English in the French catalog, and 26 in Spanish, translated (found via the i18n-gap ratchet gate while merging main forward for #1080's remediation).
[1.20.25] — 2026-09-11
Added
- feat(commitments): "Schedule this" — practice-kind commitments can be given a real schedule (one-time/daily/weekdays-I-pick/weekly/flexible, exact start+duration or flexible frequency, timezone, start/end conditions, pause) from the commitment detail page; a live occurrence preview updates as the form changes, before saving (#DEC-0097, behind
NEXT_PUBLIC_FEATURE_COMMITMENT_SCHEDULING, default off) - feat(commitments): a Today rail on
/integrated's existing content lists today's scheduled commitment occurrences exactly once, with Completed/Partial/Planned skip/Reschedule actions and an optional post-outcome reflection —/integrateditself remains the canonical Fulfillment home (behind the same flag) - feat(commitments): occurrences can now be recorded as
partial(in addition to completed/skipped/missed) and rescheduled in place, with a stated 409 conflict if the target date collides with another occurrence for the same commitment
Fixed
- fix(habits):
GET /api/habits/today's "today" is now the caller's local calendar date (via a newlib/dates/local-day.ts, timezone fromprofiles.timezoneor a request parameter) instead ofnew Date().toISOString()'s UTC date — the bug that could read a populated Habits Today view as empty for part of every day depending on the user's timezone offset
Changed
- chore(commitments):
commitment_occurrences.statuswidened to acceptpartialandunreported(the latter is a read-time-only computed display status, never written by any route — an elapsed unreported slot is never shown as a miss);commitment_events.event_typegainedoccurrence_partial
[1.20.24] — 2026-09-11
Fixed
- Removed four dead
onClick={stopPropagation}handlers left over on modal dialog panels (CompletionConfirmModal, bothCreateAutobiographyModalcopies,NotNowModal) — each one's ancestor wrapper had no click handler for the propagation stop to matter, and the pattern was tripping the mouse-only-control accessibility guard as an unreachable-by-keyboard control. No behavior change. - Fixed the privacy export/erasure live-database test's generic row seeder, which was violating check constraints on
person_assessments,person_reason_evidenceandcoach_session_audioand failing the suite (#1149). lib/privacy/erasure.ts's delete-pass loop only deferred a table to the next pass on a foreign-key refusal (23503); a check-constraint violation (23514) raised by another table's ownON DELETE SET NULL— as happens whenjournal_entries/interaction_eventsis deleted while aperson_reason_evidencerow still points at it — went straight to a thrown error instead of deferring the same way. Found by running the live export/erasure suite against a real migrated Postgres, not by static review (#1149, #1155).
[1.20.23] — 2026-09-11
Fixed
- fix(db): the collage imagery migration now has a unique version (20260930000700) so production applies it; it had collided with the coach close-out migration and never ran (#1186)
Added
[1.20.22] — 2026-09-11
Added
- Relationship segments (/relationships/segments, also at /settings/segments) can now be edited in place: clicking a card (not just its Delete button) opens a panel for role type (free text with suggestions — an existing value like "teacher" is never coerced into a fixed list), start/end year, context, when you met and how precisely, life areas, notes, tags, and last contact. The Add Segment form has the same fields. Delete now asks for confirmation and offers Undo. (#1137)
- A "Possible duplicates" section on /relationships and /relationships/segments finds person records that share a name once case and accents are ignored, previews exactly what a merge would move, and — only after you confirm — merges them: segments, interactions, journal links, contact points and flags move to the record you keep; the other is archived, never deleted. (#1137)
- The person workspace has a new read-only History tab unioning that person's relationship segments, any autobiography entries explicitly linked to them, and their journal entries into one chronology. (#1137)
[1.20.21] — 2026-09-11
Changed
- fix(habits): compute today in the user's timezone, not UTC (#1151)
[1.20.20] — 2026-09-11
Changed
- chore(release): the release version bump now rolls a patch of 100 or more into the minor version (
1.MINOR.PATCH→1.(MINOR + floor(PATCH/100)).(PATCH % 100)), computed once inscripts/release/next-version.mjsand used byrelease.ymland theversion:*scripts.1.19.117is read as1.20.17; the next release is1.20.18. (#1171, DEC-0103)
[1.19.119] — 2026-09-11
Changed
- ops(fly): pin app machines to performance-1x with 2GB (#1174)
[1.19.118] — 2026-09-11
Changed
- docs(phoenix): Wave 1 vertical-completion truth-and-design register (#1144)
[1.19.117] — 2026-09-11
Fixed
- fix(mobile): global header/footer chrome — the wordmark logo link (mobile and desktop headers) and the signed-out "Sign In" link raised to 44px; the footer's brand blurb and copyright line raised from 14-15px to 16px, every footer link (social icons, Quick Links, Phoenix Tools, Resources) raised to a 44px tap target with the footer already stacking to one column below
md;ContactsManager(mounted at/contactsand/settings/contacts) and the Life Domains page (/framework/domainsand/settings/domains) had their non-wrapping header rows changed to wrap/stack on narrow viewports, fixing the overflow reported there (#1081)
[1.19.116] — 2026-09-11
Fixed
- fix(settings): below
lg,/settingsand its 15 section routes no longer sit inside a fixed 320px sidebar next to aflex-1content pane (an un-prefixedflex gap-6inapp/settings/layout.tsxwith zero responsive class on either child) — the actual mobile overflow, one file over from where the spec first placed it (CTO review §1a). Belowlgthe 15 sections now render as a full-width list (icon, name, description, chevron; ≥56px rows, theme-tokenbg-card/border-border); tapping one drills into its route with a "‹ Settings" back header. Atlg+ the existing sidebar-and-content layout is unchanged (#1060) - fix(settings):
/settings/integrations' three stat tiles (grid-cols-3, un-prefixed) forced the layout viewport to 612px wide on a 390px device — found live while verifying the fix above on this same route. Nowgrid-cols-1 sm:grid-cols-3(#1060)
[1.19.115] — 2026-09-11
Fixed
- fix(mobile): body copy, button labels and card descriptions on
Button,PhoenixButton,CardDescriptionandPhoenixCardDescriptionrendered at 14-15px (text-sm) by default — below comfortable mobile reading size on 197 combined call sites app-wide. Default size now renderstext-base(16px);size="sm"is unchanged as the explicit opt-down (DEC-0075, #1059) - fix(mobile): three sub-44px tap targets raised to 44px — the
NavigationV3mobile hamburger, the Home/FulfillmentDisplayOverview/Map/List toggle, and the journal-privacy "Got it" dismiss (fixed at its shared source,FirstTimeTooltip) (#1059)
[1.19.114] — 2026-09-11
Fixed
- mobile(pricing):
/welcome#pricing's Architect/Sovereign/Organizations tier cards no longer wrap "Not available yet" in a dead<button>/<a>(pattern #878); they render as plain, non-interactive<article>cards. Foundation's real "Begin Foundation" CTA is unchanged. The footer crisis-resource links (988, 1-866-APPELLE) are now real ≥44×44px tap targets, ≥8px apart, instead of 28×19/113×19 inline text links. - mobile(checkin):
/checkin's "Check In & Start My Day" button was genuinely disabled until a mood is picked, but nothing on the page said so, and the disabled style measured 2.08:1 contrast. Both fixed: a visible reason ("Select a mood above to check in.") appears while disabled, and the disabled style now uses theme tokens that clear 4.5:1 in both themes. Two sub-44px controls ("Add another intention", remove-intention) raised to the tap-target floor.
[1.19.113] — 2026-09-11
Added
- ci(mobile): a non-required Playwright check (
mobile-smokejob ingolden-path.yml) now measures every authenticated page at iPhone-width viewports (390×844, 430×932) for horizontal overflow, undersized tap targets, and sub-16px body text — no user-visible change; this is CI instrumentation that currently finds real defects (Settings, Commitments, Relationships, Home overflow, plus a shared header and footer under 44px/ 16px on every page), tracked for the per-surface fixes to come (#1058)
[1.19.112] — 2026-09-11
Added
- feat(i18n): the app now renders in the visitor's actual chosen locale on the first server response —
<html lang>and every message were hardcoded English until the client swapped them in after hydration, even for a user who had picked French or Spanish.i18n/request.tsresolves locale from thelocalecookie, thenAccept-Language, then English, server-side. The language switcher (built, but previously mounted nowhere) is now visible in the top bar and a new "Display Language" row in Settings, offering English, Français and Español (#1044) - feat(i18n):
scripts/i18n_gap.ts+ a newi18n-gapCI check block a translation-catalog regression across en/fr/es before merge — a missing key, or a growing count of strings that were never actually translated (#1044)
[1.19.111] — 2026-09-11
Added
- i18n: translated the 1,295
messages/fr.json/messages/es.jsonvalues that were byte-identical to English (the incomplete pass documented in the trilingual plan) — 642 fr + 653 es lines changed across pricing, admin, fulfillment, contribution, resources, community, and (merged forward from #983/#1021) commitments lifecycle/detail/timeline/previous-terms namespaces. Key set and order unchanged (4,025 keys, parity held across en/fr/es); 17 keys were deliberately left matching English because the correct translation is a true cognate or an unlocalized brand/loanword term already treated that way elsewhere in the catalog (Microsoft Azure AD, Coaching, Transcription, Tags, Cycle {current}/{max}, the action/actions ICU plural) (#1045)
[1.19.110] — 2026-09-11
Fixed
- fix(ci):
golden-path.yml's twonode-versionentries move from'18'to'20'— since the collage photo upload work merged (#1048, #1063),sharp@0.35.4(used byapp/api/collage/[kind]/photo/route.ts) requiresnode >=20.9.0, sopnpm buildfailed on Node 18 in this required check on every PR.package.json'sengines.nodefloor also moves from>=18.0.0to>=20.9.0to match, now that every workflow, the Dockerfile, and.nvmrcagree on Node 20. (#1158, DEC-0100)
[1.19.109] — 2026-09-11
Added
- feat(contribution): time-aware prominence and Contribution Return — contribution themes now fade in prominence (
foreground/enduring/history, computed, never a number shown to you) rather than disappearing or staying permanently equal; a theme you mark "Still central to who I am" stays prominent regardless of age, and an explicit "Move into my history" is the only thing that files one away — age alone only ever fades it, never deletes or hides it. A periodic Contribution Return, on the same display tab, invites you (only when your chosen interval has passed, 3+ themes were added since your last reflection, or you ask) to walk your foreground themes and, per theme, Reaffirm ("still represents me," keeps the original date), Evolve (express it differently now — a new theme, the old one kept and moved to history), Integrate (fold several into one broader theme, the originals kept visible underneath), or Move into history — then asks what this display doesn't yet show. Declining ("Later" / "Not now") is never recorded anywhere. Completing a review reuses the existing display's own "offer an updated display" flow rather than a new generation path. New tablecontribution_reviews(not the pre-existing, unrelatedcontribution_returnsReturn Protocol table) (#1138, D-16)
[1.19.108] — 2026-09-11
Added
- feat(contribution): Living Contribution Display on
/contribution's display tab — "Create my Contribution Display (AI)" reads the user's complete Architecture of Contribution (themes with verbs/layer/somatic/shadow, Return Protocol entries), interprets it via the text-model layer into themes/composition/reflects/alt_text (each citing which entries support it, never a stock symbol per dimension), and generates one coherent image via the existing gpt-image-1 adapter. Gated behind a new off-by-default Settings → Privacy toggle ("Contribution Display (AI)", separate from AI Collage Imagery — this feature's prompt carries somatic/shadow self-disclosure a collage-image consent was never asked to cover). Evidence quotes are counted ("named by N people") but their text is never selected into the read, let alone sent to a provider. An interpretation citing fewer than 2 distinct entries is rejected before any image is generated ("could this have been generated for almost anyone?"). Keep / Refine (free-text correction → new version, prior kept version untouched) / Discard; "What this reflects" panel outside the artwork, labelled source-supported vs artistic; dated Previous versions; "Offer an updated display" only when the material has changed, never automatic. New tablecontribution_displays, versioned and never overwritten (#1115, D-15)
[1.19.107] — 2026-09-11
Added
- feat(collages): "Generate AI collage image" (Contribution collage only) and "Upload a photo of your collage" (both collages) on
/collages, per owner override of the draft spec's AI-imagery exclusion. AI generation is explicit, per-item, gated behind a new off-by-default Settings → Privacy toggle ("AI Collage Imagery", separate from Journal Insights), builds its prompt from a field allow-list (month, counts, life-area names, contribution theme title/verbs — never a person name, journal text, or quote), shows the prompt before sending, and requires Keep/Discard review before a generated image is treated as final. Uploaded photos are EXIF-stripped server-side and HEIC-converted before storage; both actions need object storage the owner is provisioning separately, and answer "not configured" until it exists. No automatic or scheduled generation; no "you haven't generated one" nudges (#1051, D-14)
Changed
[1.19.106] — 2026-09-11
Added
- feat(collages): Life Collages at
/collages(See category) — a Relationships collage built frominteraction_events,journal_entry_peopleandrelationships, and a Contribution collage built fromcontribution_themes,contribution_evidence,contribution_returnsandcontribution_activity_events. Every count expands to the exact record ids it counts, with links through to/relationships/[id]and/journal/[entryId]; a month scrubber re-runs the same read query with a different?month=YYYY-MMwindow (no stored snapshot, no blending of adjacent months); a month with no records renders visibly blank. No AI call anywhere in the render path, no scores, no comparisons. The Contribution Display tab (/contribution?tab=display) now links to its collage view and back (#1039)
[1.19.105] — 2026-09-11
Added
- feat(identity): each life area now shows a GitHub-style activity graph — a count of recorded activity per day for the trailing 12 months, broken down on expand by kind (journal entries, commitment status changes, interactions, coach sessions) and, for journal entries, by whether they were written in the app or by an agent session. A day with nothing recorded stays a neutral, unmarked cell — never red, never "missed" — and nothing here is a score: every number shown is a plain count. Does not appear on /journal or /journal/timeline (D-13). Practice completions are not yet tracked and are named as omitted rather than shown as zero (#1038)
[1.19.104] — 2026-09-11
Changed
- fix(security): platform-admin gate no longer conflates workspace ownership with platform authority (P0) (#1145)
[1.19.103] — 2026-09-11
Added
- feat(reset): the
/resetBoundary Reset ritual now persists relationally —public.reset_rituals(one row per attempt: the boundary chosen, all five steps' answers, the closing "Moving forward, I will..." statement) behind six routes under/api/reset/rituals, closingjournal_entries.reset_ritual_id's dangling reference (it pointed at nothing since 2026-01-08). Writes are non-blocking with bounded retry — the ritual completes even offline; only the relational copy is lost, never the ritual itself. On completion, offers to track the recalibration statement as a commitment through the existing commitments endpoint (#1027)
Fixed
[1.19.102] — 2026-09-11
Fixed
- Security (P0):
assert_platform_admin()(Postgres) andrequireAdminRole(lib/wcpg-admin.ts) authorized any signed-up user, not platform admins, because both checked for aworkspace_membersrow with roleowner/admin— a row every user has for their own personal workspace. Any authenticated account could suspend theCLIENT,SOCIAL_WORKERorCOUNSELORplatform role for everyone viarpc_suspend_role/rpc_reactivate_role,POST /api/audit/roles, the WCPG admin routes, orPOST /api/db/query. Both checks, plusapp/api/admin/grants/metrics/route.ts(same defect, found in the same review), now key on a newplatform_adminstable — seeded empty, granted only by the owner directly against the database, independent of any workspace's membership. (DEC-0095, RISK-0075) - fix(reset): the
/resetstep card and the two/coachheadings from the previous fix (#1027) used a hard-coded surface/foreground pair (bg-white/text-phoenix-ash/text-black) — a second spot-fix, and one never actually checked against the app's dark theme. Converted to the same semantic theme tokens the commitment card uses (bg-card text-foreground border-border, body copytext-muted-foreground, the response textarea and progress-bar/dot track too), verified >= 4.5:1 body text and >= 3:1 large text in BOTH light and dark themes by readingapp/globals.css's actual--card/--foreground/--background/--muted-foregroundvalues directly, not assumed from a single white-background check (#1027)
[1.19.101] — 2026-09-11
Changed
- fix(upsets): lens footnote passes Reflection Purity Guard (#1141)
[1.19.100] — 2026-09-11
Added
- feat(coach): redesigned
/coach— Speak/Write toggle with a visible running transcript, mic button with an accessible name and idle/recording/transcribing state, mood and energy moved to close-out, audio transcribed then discarded by default (opt-in "keep the recording" per session), crisis resources shown on any transcript chunk or typed message that trips the existing detector (#986) - feat(coach): coach responses now follow the Difficult Conversations question ladder (OPENING → PROBING → DIRECT), one question at a time, with an explicit coaching-not-therapy boundary in the system prompt (#986)
- feat(coach): close-out extraction — a session proposes 3-8 items (commitment/action/insight/challenge/blocker/person/template answer); nothing is written to commitments, commitment entries or relationships until the user accepts each one individually (#986)
- feat(coach): History and Patterns tabs restored, built only on real session data — Patterns states its denominator on every number and says so plainly with fewer than 3 sessions (#986)
Fixed
- fix(coach): H1 title was a gradient fill on a cream background with no resolvable contrast; now a solid ink color (#986)
[1.19.99] — 2026-09-11
Fixed
- fix(db): five 2026-09-10 migrations reused an applied version timestamp and never ran in production; renamed to unique versions so they apply on the next boot, plus a guard test that rejects duplicate migration versions (DEC-0092, RISK-0072)
[1.19.98] — 2026-09-11
Added
- A new "Insights" tab on the Upsets page shows deterministic counts computed from your own upsets — unresolved/resolved totals, average time to resolution, recurrence by person and by life area, and a monthly trend. No AI, no suggestions; a grouping is only ever called "recurring" at 3 or more occurrences. (#1104)
- feat(habits): build-habit loop — six-step wizard, stage lifecycle, consistency/automaticity tracking, never-miss-twice (#937)
Fixed
[1.19.97] — 2026-09-11
Added
- Every upset you save (Upsets & Incompletions) now also logs into the master Journal — one entry per upset, kept in sync on every edit and resolution, tagged
upset,upset:<status>, and linked to the person or life area when set. Deleting an upset archives its journal entry rather than losing it. (#1097)
Fixed
- The Upsets Quick Capture modal no longer leaves you guessing what happened: submitting now shows what was actually saved (the real trigger text, who/what it was linked to, whether it reached your Journal) with two explicit next actions, instead of silently closing. The dialog also no longer overflows past the screen when both optional sections are expanded, fits phone-width viewports without clipping intensity buttons 9-10 off-screen, and every control in it now meets the 44px touch-target floor. (#1104)
/upset-inquiryno longer shows a "Health: 70%" / "Pattern Recognition System" indicator on page load before any tab is clicked — it now appears only once you open the Patterns or Resolution tab, matching the app's silence-by-default rule for reflection surfaces. The intensity number on each entry no longer shows a hardcoded (and often wrong)/5next to values that were actually captured on a 1-10 scale. (#1104)
[1.19.96] — 2026-09-11
Added
- Every upset you save (Upsets & Incompletions) now also logs into the master Journal — one entry per upset, kept in sync on every edit and resolution, tagged
upset,upset:<status>, and linked to the person or life area when set. Deleting an upset archives its journal entry rather than losing it. (#1097)
[1.19.95] — 2026-09-11
Added
- feat(commitments,journal): Commitment Chart notes can be explicitly added to the Journal. Each saved chart note now carries an "Add to Journal" control; clicking it opens a confirm dialog showing exactly what will be created, and confirming creates a private Journal entry (
source: 'commitment') through the Journal's own write path, taggedcommitment+commitment:{section}plus the commitment's life area, linking back viametadata.commitment_id/chart_note_id/section. One-way and idempotent — editing the note later never touches the entry, and a second click reads the existing entry back rather than creating a duplicate. The Journal timeline now shows a "Commitment" chip on these entries, linking back to/commitments?open={id}#{section}. No automatic mirror exists or is planned; seeJOURNAL_CONSTITUTION.md§8.6 (issue #1092, DEC-0085). - feat(journal): the Claude Code
PreCompacthook now writes a session-journal draft too, alongsideSessionEnd— captures the transcript context right before it would be lost to compaction: the same deterministic git/gh summary asSessionEnd, plus a redacted excerpt of the last ~1% of the transcript (never an AI-generated summary — no new model call), tagged with a compaction number so a long session produces "Session <id> · compaction 1", "· compaction 2", etc.phoenix hook installnow wires both events by default (--events SessionEnd,PreCompactto choose).phoenix config set life-area <slug>maps a repo to a life-area label the hook sends on every entry (#1037, PR C2)
[1.19.94] — 2026-09-11
Changed
- feat(journal): SessionEnd hook — deterministic session summary, mandatory redaction (#1064)
[1.19.93] — 2026-09-11
Changed
- feat(journal): phoenix CLI (login/whoami/journal add/status/flush) (#1057)
[1.19.92] — 2026-09-11
Changed
- feat(journal): personal API tokens + session-journal ingest route (#1052)
[1.19.91] — 2026-09-11
Added
- feat(direction): personal meaning and learning — a
meaning_recordstable (observation/interpretation/expectation/incompletion/declaration/practice), append-only dated revisions, and a "Meaning" section in the Direction Review flow that chains records withrecord_links. No AI suggestions — explicit-invocation only, deferred to DEC-0060 (#1032) - feat(direction): contribution and continuity — a
commitment_handoffstable (proposed/accepted/declined, kind ∈ handoff/shared_responsibility/succession); a commitment leaves the owner's live list only once a handoff is accepted, never on proposal alone. Routes under/api/direction/handoffsand/api/commitments/[id]/handoffs. A "who this commitment serves" query is exposed at/api/direction/contribution(degrades gracefully until #969'sbeneficiary_relationship_idmerges) — no list UI in this PR, that surface belongs to the Relationships work (#1032)
[1.19.90] — 2026-09-11
Added
- feat(relationships): person card, seven owner-requested sections (DEC-0086/RISK-0068) — an Upsets tab (resolved/unresolved, linking to
/upset-inquiry); a difficult-conversations count and log, with a "Log this conversation" link from the toolkit page when reached from a person's card; commitments to this person and shared with this person sub-groups above the existing follow-ups list (shared commitments stay hidden, not empty, untilcommitment_handoffs(#1032) merges); a card-header last contact line (when + channel, frominteraction_events) and next follow-up line, both plain facts with no staleness color-coding; a Remembered card — "the last thing they told me that I remembered," dated and user-typed; inline Edit on follow-ups (adds adescriptionfield the API already accepted) and on contact points (PATCH /api/relationships/[id]/contact-points— previously GET/POST/DELETE only, so changing a number meant delete-then-re-add, silently droppinglabel/preferred) (#1100)
Fixed
[1.19.89] — 2026-09-11
Changed
- feat(relationships): per-person program flags — partnership book toggle (#1098)
[1.19.88] — 2026-09-11
Fixed
- fix(autobiography): the "Important People" card saved a person to a year (toast confirmed,
relationship_segmentsrow created) but never rendered anyone — its only child was the "Add person" picker, which renders nothing while closed. People now render as chips grouped by category (family/mentors/partners/friends), each linking to the person's card, with an optimistic add and an inline ✕ remove that handles every year-boundary case (single-year and just-added-open-segment delete, start/end bump, and a Split-or-Cancel confirm for a year strictly inside a span) with a 5-second Undo toast. Also fixed: a first-time "Add person" always logged a spurious 406 on the network tab (.single()asserted exactly one row on a lookup where zero was the expected, common case — now.maybeSingle()) (#1091) - fix(journal): a journal entry's linked people now render as chips under the title on the entry's own (read-only) view, each linking to the person's card; POST/PATCH now reject a
linked_peopleentry whoserelationship_iddoes not belong to the caller (400 unknown_relationship_id) instead of silently linking the entry to someone else's person row (#1091) - fix(autobiography): clicking a person on
/autobiography(a chip, or a picker result) now opens an inline Person Record Panel to edit their start year, end year/"Present", role, last contact, and notes — previously the only way to touch a person's record from this screen was to leave it; picking someone in the picker opens the panel prefilled so a start year that defaulted to the year being edited can be corrected in the same motion, instead of a separate hunt to fix it later.PATCH /api/relationships/[id]also now 404s (not 500) when the id doesn't exist or isn't the caller's, matching whatGETon the same route already did (#1121)
Added
[1.19.87] — 2026-09-11
Changed
- feat(journal): 'People in this story' picker + person-card Stories section (#1053)
[1.19.86] — 2026-09-11
Changed
- feat(relationships): person workspace increment 4 — boundaries and recommendations (#1013) (#1024)
[1.19.85] — 2026-09-10
Changed
- feat(relationships): person workspace increment 3 — assessments and patterns (#976) (#984)
[1.19.84] — 2026-09-10
Added
- feat(relationships): person workspace increment 2 — contact actions (#975). Call gets a no-contact override step; Email sends through the connected Gmail account (composer shows recipient + sending address, records the interaction only on a successful API response, never on a
mailto:); "Log a conversation" replaces the quick interaction form with channel/direction/who-initiated/occurred-at-precision/pasted-notes/source-link and ansms:link besidetel:; Timeline gains a Statistics section (last contact/two-way exchange/spoken conversation, 7/30/90/365 counts, channel breakdown, initiation balance, longest gap — all labelled "based on recorded activity", no response-time metric) and a filterable contact-history report with CSV export (private notes excluded by default)
[1.19.83] — 2026-09-10
Added
- feat(relationships): per-person program flags — a labelled "Included in my community book for partnership" switch on the person workspace header, a non-interactive indicator plus an "In partnership book" filter chip on
/relationships. Genericperson_program_flagstable + code registry (one key,partnership_book, today) preparing for a later partnership-development phase that is explicitly not built here; no scoring, ranking, or nudges anywhere (#1093) - feat(journal): "People in this story" picker under Tags in the journal composer (New Entry and Edit, progressive disclosure, no AI) that searches your own relationships and links them to the entry; a "Stories" tab on the person workspace lists every entry that links them — title, occurred date and feeling only, newest-first, never entry content — with a link into the entry. The read/write plumbing (
journal_entry_people,linked_peopleon POST/PATCH /api/journal) already existed and was already tested; nothing had ever called it (#1047) - feat(relationships): person workspace increment 4 — Boundaries (status with dated history, "reasons I've noticed" against the existing 103-code catalog organised by the Termination Matrix's six dimensions as a plain count with denominator, never a score, an exit plan with a safety screen, practical loose ends, and a private closure-message record) and deterministic Recommended next steps (contact-gap/overdue-follow-up/review-date/unacknowledged-support triggers, no AI, suppressed entirely for no-contact/ended/archived); the no-contact override gate now covers follow-up creation and accepting a recommendation; item 14 of the Matrix (physical/sexual boundary crossing) is never a reason code and always routes to the crisis resources (#1013)
- feat(relationships): person workspace increment 3 — Trust/Likability/Enrichment assessments (each independently editable, with facets, scope by overall/life-area/commitment, "not assessed" as a real value, and full supersession history) and a deterministic Patterns tab (mentions vs. interactions kept separate in every figure, first/last appearance, 7/30/90/365-day interaction counts, initiation balance, user-authored tags, life-story links via role segments); no combined score, no AI (#976)
- feat(relationships): person workspace increment 1 — Timeline, Commitments (via existing follow-ups), and Details tabs on a person's profile; Call/Log interaction/Add follow-up work now, Email renders visibly unavailable pending increment 2's Gmail connection (#960)
- feat(relationships): duplicate-person detection on "Add Relationship" (accent-/case-insensitive, user decides via confirm) (#960)
- feat(relationships): exact journal-presence and interaction counts on the directory and profile, with every count opening its contributing entries (#960)
[1.19.82] — 2026-09-10
Changed
- feat(direction): the Direction Review flow (#962) (#1116)
[1.19.81] — 2026-09-10
Added
- feat(direction): life direction and adaptation — the extensible foundation. Five new owner-scoped tables (directions, decisions, experiments, transition reviews, record links); no UI yet, no existing table touched (#962)
- feat(direction): the Direction Review flow at
/direction/review— revisit a priority, see linked commitments, compare options, choose an experiment or propose commitment changes, and schedule a review. Proposed commitment changes never take effect until explicitly accepted in the same flow (#962)
Fixed
[1.19.80] — 2026-09-10
Changed
- governance: Phoenix Clinical reinstated as a governed workspace under the one-platform decision, reversing the portal-removal half of D-6 on the owner's revenue-rationale ruling; PR #884's clinical-module rebuild is directed to be rebased and re-verified against seven invariants (credentials, scoped sharing, non-destructive record edits, jurisdiction rules, consent+audit, clinician review of AI drafts, safety routing), not merged as-is (#1007)
[1.19.79] — 2026-09-10
Added
- feat(commitments): AI milestone builder in the commitment modal — a "Suggest milestones with AI" control beside Milestones "Add", visible only when the AI-suggestions consent setting is on (otherwise a plain link to it). Proposes 4–8 ordered milestones for the commitment's title/why/life area/size/target date; each is reviewed, edited or rejected individually, and nothing is written until accepted. A crisis-indicator match on the "why" text shows crisis resources instead of calling the model. First DECIDE-mode explicit AI action under
canon/AI_SILENCE_CANON.md's Rule 1 Amendment (DEC-0060, #1012) (#931)
Changed
[1.19.78] — 2026-09-10
Changed
- governance(canon):
canon/AI_SILENCE_CANON.mdRule 1 gains a named exception — an explicit, per-item, consent-gated AI action whose output is a proposal (nothing written until accepted) may now exist in any mode, including DECIDE. No AI surface is added by this change itself; it is the canon amendment the commitment milestone builder (#931) depends on.lib/ai/silence-gate.tsgains the matching code path, additive and inert for every existing caller (#931)
[1.19.77] — 2026-09-10
Governance
- Recorded the owner's Phoenix unified-platform decision (one platform, five governed workspaces) and mapped its twelve invariants against current repo state (#961, DEC-0054). Governance record only; no app behaviour changed.
[1.19.76] — 2026-09-10
Added
- chore(ci): a new CI check, ledger-never-shrinks, fails a PR if any canon ledger loses an entry between its merge base and HEAD — closes the gap that let PR #841 ship
canon/ledger/decisions.logtruncated from 44 entries to 1 (#955)
[1.19.75] — 2026-09-10
Added
- feat(habits): build-habit loop — six-step wizard, stage lifecycle, consistency/automaticity tracking, never-miss-twice (#937)
[1.19.74] — 2026-09-10
Added
- security(api): every
/api/*request now has a rate-limit budget, enforced once inmiddleware.ts— previously only 6 of ~269 route.ts files called the limiter at all (#947)
[1.19.73] — 2026-09-10
Fixed
- fix(observability):
/api/health's database probe could hang indefinitely on a stuck connection instead of reporting unhealthy within a bounded time — it now races the samewithDeadlinemechanismcheckUserLookupalready used, under a new named 2s constant - fix(hubspot): the webhook status endpoint read
HUBSPOT_ACCESS_TOKEN, a name the sync path never used, so it always reportednot_configuredeven with the real token (HUBSPOT_PRIVATE_APP_TOKEN) deployed (#927);webhookUrlis now derived fromNEXT_PUBLIC_APP_URLinstead of a second env var nothing ever set
Changed
- docs(auth): the headers on
lib/auth/config.tsand the NextAuth route handler said NextAuth was "parallel, not-yet-active" long after production turned it on (fly.toml'sENABLE_NEXTAUTH=true) — corrected to state the live status; no logic change
[1.19.72] — 2026-09-10
Fixed
- fix(scripts):
scripts/canon_changed_files.shno longer passes--depthtogit fetchoutside CI — an unconditional--depth=200fetch, run by every agent's localcanon_guard.shpass, wrote.git/shallowinto this repo's shared.git(all agent worktrees share one) 11 times in one night (2026-09-10), breaking merge-base computation repo-wide twice. Local runs now skip the fetch or fetch plainly, with no--depth; CI's behavior is unchanged. (#1088)
[1.19.71] — 2026-09-10
Added
[1.19.70] — 2026-09-10
Added
- feat(commitments): Commitment Chart v1 UI (PR 3 of 4), behind the
commitment_chart_v1flag (NEXT_PUBLIC_FEATURE_COMMITMENT_CHART_V1, off by default) — a four-row accordion chart (cost of not doing / what it makes possible / who it's for / evidence) replaces the #938 type-radio Reasons panel on the commitment card when the flag is on; notes accumulate newest-first, are editable in place for 24h then only amendable; drafts autosave per (commitment, section) tolocalStorageand never become notes; a read-only Importance badge with a hover breakdown; the existing entries panel is labelled "Activity". With the flag off,CommitmentReasons.tsx(#938) renders unchanged — supersede, not deletion. No "✓ in Journal" affordance is rendered anywhere in this UI — PR 2's Journal-mirror trigger was not built pending an owner ruling onJOURNAL_CONSTITUTION.md§8.1, and this UI does not claim a state that cannot exist yet (#1031)
[1.19.69] — 2026-09-10
Changed
- feat(commitments): Commitment Chart v1 data model + API (§2, §3, §5) (#1042)
[1.19.68] — 2026-09-10
Added
- fix(commitments): the commitment card and its dialogs/panels/detail page rendered near-invisible text in dark mode —
CommitmentCard's default-status card wrapper hard-codedbg-white border-gray-200with nodark:pair, so it stayed literally white through dark mode while text elsewhere on it used tokens tuned for a dark surface (--muted-foregroundat #94A3B8, ~2.6:1 against a stray white card, well under the 4.5:1 floor). Every hard-coded Tailwind gray/white/status-color-without-a-dark-pair incomponents/commitments/**andapp/commitments/**now uses the theme tokens (bg-card/bg-muted/border-border/text-foreground/text-muted-foreground) or gets its missingdark:variant, preserving the #968 green=completed/blue=renegotiated/gray=abandoned status coding. New regression coverage:__tests__/commitments/card-contrast.test.tscomputes WCAG contrast from the actualapp/globals.csscustom properties (not a DOM render — see the file's header) and scans the swept files for the one pairing that measures under 4.5:1 even with correct tokens (bg-muted+text-muted-foreground, ~4.3:1 light / ~4.0:1 dark — fixed by pairingbg-mutedwithtext-foregroundinstead, as the spec names for the "N entries" bar). The same pattern is present elsewhere incomponents/**outside this scope (241 files, ~2,400 occurrences) and is out of scope for this PR (#1031)
[1.19.67] — 2026-09-10
Added
- feat(commitments): Commitment Chart v1 data model + API (PR 2 of 4, owner feedback on the #938 Reasons panel) —
commitment_reasonsreused as the chart-notes table (amends_id, journal_entry_id, ai_signal, updated_at, widened text limit);importance_score/importance_factors/importance_computed_atadded tocommitments; newGET/POST /api/commitments/:id/chart,PATCH .../chart/:noteId(24h edit window, then amend),POST .../chart/:noteId/amend,GET .../importanceroutes;entry-countsextended with per-section counts and importance score so the list stays at its existing call count. The spec's Journal-mirror trigger is not built —JOURNAL_CONSTITUTION.md§8.1 forbids a non-journal surface writing journal entries; seegovernance/decisions/2026-09-10__a-commitment-keeps-a-chart-not-a-weight.md(#1031)
- feat(commitments): the detail page now renders the whole commitment — life area (linked to the filtered list), tags, "Why this matters" (
metadata.importance), stage·condition ("Acting · At risk"), kind, completion criteria, target date, outcome, created/updated, and a reverse-chronological history timeline (events + entries merged, filter chips, month grouping past 20 items, an inline add-entry composer) readingGET /api/identity/commitments/[id]/events(#969/#983). Previously it rendered only title, description, due date, priority and (always-shown) reminders (#977) - feat(commitments): a "Previous terms" panel walks the
original_commitment_idrenegotiation chain (3+ hops), reading each hop's diff fromcommitment_renegotiations(#977) - feat(commitments): prev/next navigation on the detail page, respecting the list's active tab and life-area filter (carried in the URL query string,
lib/commitments/list-filter.ts) (#977) - feat(commitments): the commitment card is now a real link to its detail page — ⌘/middle-click open a new tab, Tab reaches it, Enter opens it, and it has a visible focus ring. Fixes the invalid
<Link><button>nesting in the card's own action menu at the same time (#977) - feat(commitments): one shared priority mapper (
lib/commitments/priority.ts, scale documented indocs/commitments/priority-scale.md) — the card and the detail page independently bucketed the samepriorityinteger into different labels (priority = 1showed "Low" on the card and "Very Low" on the detail page) (#977) - feat(commitments): a batched
/api/commitments/entry-countsendpoint and a "N entries" badge on the card, replacing the per-card/entriesfetch that fired once per row on every page load (#977) - feat(commitments): the "All" tab progressively reveals rows (40 at a time, "Show more" plus auto-load on scroll) instead of mounting all 749 at once, which the owner measured going blank past 24 seconds with 749 concurrent
/entriesrequests firing at once (#977) - fix(commitments): the detail page's Reminders card is now hidden entirely for a non-active commitment, instead of always rendering with an "only available for active commitments" notice — and its Complete/Renegotiate/Abandon actions (previously entirely absent from the detail page) are gated on
isLiveCommitmentStatus, matching the card's own gates (#977) - fix(commitments):
CommitmentCard's due/completed/created dates and its urgency (overdue) calculation usednew Date(dateOnlyString), which reads a PostgresDATEvalue as UTC midnight — west of Greenwich this renders as the PREVIOUS day. The card now uses the sharedlib/date/calendar-date.tsformatter the detail page already used, so the two screens cannot independently disagree about which day a date-only field names again (#977)
[1.19.66] — 2026-09-10
Changed
- fix(commitments): the card renders theme colours, not hard-coded light ones (#1029)
[1.19.65] — 2026-09-10
Changed
- Commitment lifecycle: 12-stage model, hierarchy, transitions, and clone-and-link renegotiation (#983)
[1.19.64] — 2026-09-10
Added
- feat(journal): personal API tokens (Settings > API Tokens) and
POST /api/ingest/session-journal, so a script or CLI can write a draft journal entry to your own account — the entry always lands as a draft awaiting your accept/dismiss in the Journal timeline ("Session summaries awaiting your review") unless you turn on auto-publish; a server-side crisis check runs on the text and shows the existing crisis resources on a flagged entry, notifying no one (#1035) - feat(journal): a
phoenixCLI (pnpm phoenix, orphoenixafterphoenix install-shim) —login/logout/whoami, andjournal add/status/flushfor the session-journal ingest route above. The token is stored in the macOS Keychain when available, a mode-0600 file otherwise; a network failure queues the entry locally instead of losing it, forjournal flushto retry later (#1036) - feat(journal):
phoenix hook installwires a Claude Code SessionEnd hook that writes a session-journal draft automatically when a session closes — commits since the session started, PRs/issues touched, and the working-tree diff stat, gathered from git (no model call unless--narrateis passed). Every value is redacted for API keys/tokens/private-key blocks before it touches disk or the network. Offline writes queue for the CLI'sjournal flushto pick up later (#1037, SessionEnd only in this PR — PreCompact follows) - feat(commitments): the 12-stage commitment lifecycle (Emerging→Integrating), three independent fields (stage/condition/outcome), the hierarchy (life_commitment/ specific_promise/practice), practice occurrences, and 14 transition/branch actions (advance/continue/clarify/replan/wait/pause/renegotiate propose-accept-decline/repair/ release/close/integrate/renew) recorded through
commitment_events, the existing engine-v2 event log.statusstays derived so every existing route keeps working with no code change (#969) - feat(commitments): renegotiation now clones and links instead of overwriting the original in place — the original commitment is preserved with status
renegotiated, a new linked row (original_commitment_id) carries the negotiated terms forward, and arenegotiatedcommitment_event is written on both. Existing pre-#969 renegotiated commitments get a backfilled event, recovering the diff fromcommitment_renegotiationswhere one exists and honestly flagging it unrecoverable where none does (#969) - feat(commitments): every status/lifecycle write (create/complete/abandon/renegotiate/ archive/unarchive/PUT/transitions) now logs one
commitment_eventsrow through a single shared function, closing the gap where a commitment created after the engine-v2 migration never got acreatedevent;GET /api/identity/commitments/[id]/eventsserves a merged, paginated events+entries stream for a timeline UI (#969) - feat(commitments):
archivedas a fourth terminal status — "out of sight, not a failure, not a win." New Archive/Unarchive routes and card actions move a commitment out of every default list (Active/Resolved/All) into its own Archived tab without deleting it or judging how it ended; excluded from integrity math entirely (#959) - feat(commitments): a left-border colour, muted+strikethrough title, and completion date (replacing due date) per status on
CommitmentCard, a status legend above the list, and a default sort of Active → Renegotiated → Completed → Abandoned → Archived (#959)
Fixed
- fix(commitments): the See surface (
/fulfillment-v5) and/commitmentscomputed "active" from two different rules — one counted the literal string'active', so it reported 0 Active while/commitmentscorrectly showed hundreds. Both now readisLiveCommitmentStatus(#959) - fix(commitments): integrity now renders "—" (with the resolved count and the minimum needed) below
INTEGRITY_MIN_DENOMINATORresolved commitments, instead of a hard 0%/100% off a sample of one or two (#959)
[1.19.63] — 2026-09-10
Fixed
- fix(commitments):
declaredcommitments (~748/749 in production, backfilled by the engine-v2 migration) can now be completed, abandoned, and renegotiated — the three status-transition routes andCommitmentCard's action gates only recognized the literal'active'(#903)
[1.19.62] — 2026-09-10
Changed
- fix(billing): three VP Finance findings from #952 review (#1014)
[1.19.61] — 2026-09-10
Changed
- fix(billing): the pricing page shows all four tiers again (Architect/Sovereign/Organizations render with no price and no button — Foundation is still the only one you can buy), and a Stripe promotion code now actually grants access instead of completing a $0 checkout with nothing to show for it (#945)
- fix(billing): three issues VP Finance flagged in review of #952 — a Foundation charge now carries a statement descriptor so it does not show as the hostel's name on the buyer's card statement; the pricing card no longer shows a billing-cadence line ("Ongoing membership", "Annual, by application", "Annual partnership") on the three tiers that cannot be bought; and Foundation's copy no longer promises a $250/week instalment plan the checkout cannot honour (#1005)
[1.19.60] — 2026-09-10
Changed
- nav(settings): Segments, Day Archetypes and Life Domains moved off the top bar into Settings, at
/settings/segments,/settings/daysand/settings/domains— same components, so/relationships/segments,/framework/daysand/framework/domainskeep working. Per the 2026-09-10 UX audit's three-of-five recommendation (seedocs/navigation/settings-candidates-2026-09-10.md);/creditsand/redeemwere NOT moved — Decision D-9 already filed them under Community (#1010)
Fixed
- fix(settings): the "Screen reader optimizations" accessibility toggle now does something — it sets
data-screen-reader-optimizedon the document root, which makes visually-hidden helper text visible and keeps skip-links always visible, instead of being stored and never read (#1010) - fix(protocol):
fetchUserTier()on the Commitment Protocol page no longer hardcodes every visitor to{tier: 'paid', canCreate: true, canAudit: false}— it now reads the signed-in user's real tier the same way the rest of the app does (#1010)
[1.19.59] — 2026-09-10
Changed
- ops(deploy): fly-deploy.yml deploys with
--strategy rolling --ha=falseinstead of--strategy immediate --ha=false, now thatwisdomos-app-webruns two machines (owner-approvedflyctl scale count 2);fly.toml'smin_machines_runningrises 1 → 2 to keep both warm; the two post-deploy checks now verify every machine individually instead of trusting whichever one the shared hostname answers from (#1009)
[1.19.58] — 2026-09-10
Fixed
- fix(api): an anonymous GET on fulfillment-engine/snapshots/[id] and identity/commitments/[id]/reminders(/[reminderId]) answered HTTP 500 instead of 401; a sweep test now covers 143 routes sharing the pattern (#951)
- fix(api):
/api/debug-autobiographyanswered HTTP 200 for an unauthenticated request and for a thrown server error; both now carry the correct status (#951) - fix(teachings):
mark-seen,/progress, and/reflect500'd when the teaching-library tables are absent instead of the honest 503 their sibling routes already answer (#951) - test(e2e):
wisdom-coach-flow.cy.tsnow clears cookies and session storage between tests and asserts on login-page content, not only the URL, to kill a cookie-leak flake that once landed on /welcome instead of /auth/login (#951)
[1.19.57] — 2026-09-10
Changed
- fix(truth): the header counts its own rows, and Add stops overwriting (#885) (#886)
[1.19.56] — 2026-09-10
Changed
- nav(settings): move Contact management off the top bar into Settings (#982)
[1.19.55] — 2026-09-10
Changed
- change(commitments): the Description field in "Add New Commitment" now asks "Why is this important to you?" instead of "Describe your commitment" — the label is unchanged (#932)
[1.19.54] — 2026-09-10
Added
- feat(commitments): a pinned "why" stack on each commitment — reasons accumulate, newest first, with the count on the card header (#938)
Changed
- nav(settings): Contact management (CSV import, plus Add/Export/activity history) moved off the top bar into Settings, at
/settings/contacts— same component, so/contactskeeps working. Owner feedback: it's import-only work and doesn't need a top-bar slot. Reachable from the avatar menu (Settings → Contacts (import)) and from the Settings index sidebar; a candidates list for a few similar screens (docs/navigation/settings-candidates-2026-09-10.md) is left for owner approval (#979)
Fixed
- fix(commitments): the header stat tiles and the Active tab now count a commitment engine-v2 backfilled to
status = 'declared'as active, matching what the card already showed (#938)
[1.19.53] — 2026-09-10
Changed
- fix(commitments): restore the 747 commitments Phase 1 made invisible, and add the hold states (#684) (#901)
[1.19.52] — 2026-09-10
Changed
- fix(coach): sentiment is an object, so the column must be jsonb (#900) (#902)
[1.19.51] — 2026-09-10
Added
- feat(difficult-conversations): expansion pack v1.1.0 — 22 owner-written conversation templates across 6 new categories (Trust & Betrayal, Toxic Patterns, Frustration & Resentment, Boundaries & Entitlement, Money & Entitlement, Repair & Reconciliation) on
/difficult-conversations; templates can now carry a per-conversation safety note and their own "Remember" list (#939)
[1.19.50] — 2026-09-10
Fixed
- fix(safety):
abuse_disclosurecrisis patterns now match coercion and pressure disclosures ("he pressured me into...", "he forced himself on me", "wouldn't stop after I said no", French "m'a forcé(e) à...", "j'ai pas pu dire non") in addition to assault/hit/fear-of language. A user describing coercion was previously routed to a documentation flow instead of the crisis resources (D-10, issue #897, #964)
[1.19.49] — 2026-09-10
Fixed
- fix(settings): the "not set up on this deployment" note on integration cards named the missing environment variables with raw
<code>tags printed into the sentence; the names now render properly, as "A and B" (#929)
[1.19.48] — 2026-09-10
Fixed
- fix(dashboard): the Journal, Interactions and Life Areas tiles now show real counts — they were hardcoded to 0, 0 and 12 and never fetched anything — and the Autobiography tile counts the years you have actually documented instead of reading an empty projection. A tile that cannot load now says "not loaded" rather than showing a zero that means "unknown" (#922)
[1.19.47] — 2026-09-10
Changed
- feat(guards): a ratchet against screens that report a failed load as an absence (#831) (#843)
[1.19.46] — 2026-09-10
Changed
- fix(ci): compute a PR's changed files from the merge base, and guard it (#813) (#820)
[1.19.45] — 2026-09-10
Changed
- fix(ui): five silent failures — four failed loads shown as empty, one failed save shown as nothing (#827) (#841)
[1.19.44] — 2026-09-10
Changed
- fix(observability): Sentry was configured, running, and blind to server errors (#838) (#845)
[1.19.43] — 2026-09-10
Changed
- fix(phoenix): an unmeasured dimension is no longer reported as "beginner" (#829) (#842)
[1.19.42] — 2026-09-10
Changed
- fix(coach): a failed request no longer reads as an empty coaching history (#825) (#840)
[1.19.41] — 2026-09-10
Changed
- fix(fulfillment): a score can no longer be written onto another user's life area (#823) (#824)
[1.19.40] — 2026-09-09
Changed
- feat(assessments): the relationship capability instrument, scored on a canonical relationship (#818) (#819)
[1.19.39] — 2026-09-09
Changed
- fix(honesty): four surfaces stop showing numbers nobody gave them (#781) (#815)
[1.19.38] — 2026-09-09
Changed
- feat(git): a merge driver for the append-only ledgers (#817) (#814)
[1.19.37] — 2026-09-09
Changed
- fix(e2e): the acceptance specs no longer die on an aborted request or a detached button (#919) (#920)
[1.19.36] — 2026-09-09
Fixed
- fix(priority-matrix): the Life Area field is a dropdown of your established life areas, in the order you arranged them, instead of a free-text box beside 34 of them. An item saved earlier with a name that matches none of them keeps that name, flagged, rather than having it blanked or rewritten; if the list cannot load, the field says so instead of standing empty (#910)
[1.19.35] — 2026-09-09
Changed
- fix(autobiography): the people picker searches the roster, and choosing someone writes (#908) (#909)
[1.19.34] — 2026-09-09
Fixed
- fix(autobiography): "Add person to <year>" now searches your whole roster as you type — accents and case ignored, so
belangerfinds Bélanger andfilfinds Filiatrault — with role filters, and marks anyone already in that year so the same person cannot be added twice under a second spelling. Choosing a person now actually writes them into the year: before, it changed nothing. Keyboard and screen-reader complete; nothing animates (#908) - fix(release): every release since #891 had failed — the workflow pushed the version bump straight to
main, which now requires a pull request. Four deploys shipped reporting a stale version number. The release commit now goes through a PR that merges itself once the checks pass, and the deploy waits for it so the footer can no longer show the wrong version (#906) - fix(autobiography): Important People now render for every year. The section had been empty for everyone because the read queried
autobiography_eventswhile adding a person writesrelationship_segments— two different tables. Nothing was ever lost; people stored years ago are visible again, carried forward from the year they were added until an end year is set, with the role you actually chose rather than "friend" for everyone. Removing or re-roling a person also works: it had been failing on a column that does not exist, and the failure was reported as "Person not found" (#904)
[1.19.33] — 2026-09-08
Changed
- build(workflow): preflight, hooks and one workflow doc to stop stale-baseline work (#891)
[1.19.32] — 2026-09-08
Changed
- fix(nav-tests): finish D-7 in cypress, and correct a written reason that went stale (#892)
[1.19.31] — 2026-09-08
Changed
- fix(nav): give the 38 menu paths a mode, and test for the gap that hid them (#876) (#877)
[1.19.30] — 2026-09-08
Changed
- feat(db): commitments engine v2 phase 1 — instrument, no pixels (#880) (#881)
[1.19.29] — 2026-09-08
Changed
- Navigation: the owner's ten categories replace the ten invented themes (#860)
[1.19.28] — 2026-09-08
Changed
- feat(truth): delete four controls that described their own absence (#878) (#879)
[1.19.27] — 2026-09-08
Changed
- M1: /integrated is the Fulfillment Display — relocate the one real panel, delete the four that have no data (#867)
[1.19.26] — 2026-09-08
Changed
- fix(truth): an empty denominator renders "—", never 100% (#874) (#875)
[1.19.25] — 2026-09-08
Changed
- feat(nav): full-column Fulfillment Display, and D-7 — every product surface on the menu (#852) (#855)
[1.19.24] — 2026-09-08
Changed
- fix(writes): a mutation reports success only when the server accepted it (#869)
[1.19.23] — 2026-09-08
Changed
- fix(deploy): actually build on the runner — production is four commits behind (#872) (#873)
[1.19.22] — 2026-09-08
Changed
- feat(journal): the card stops cutting mid-word and says when, how private, and whether flagged (#846) (#847)
[1.19.21] — 2026-09-08
Changed
- feat(nav): Everything — every surface, grouped by theme, one click from the nav (#848) (#849)
[1.19.20] — 2026-09-08
Changed
- feat(journal): voice notes keep their audio, somewhere it survives (#835) (#836)
[1.19.19] — 2026-09-08
Changed
- fix(entitlements): the resolver honours the account tier (#821) (#822)
[1.19.18] — 2026-09-08
Changed
- feat(founder): administration plane with an append-only audit of every read (#806) (#807)
[1.19.17] — 2026-09-08
Changed
- feat(ci): a service-role client must be added deliberately, and the list must shrink (#694) (#809)
[1.19.16] — 2026-09-08
Changed
- fix(tenancy): the tenant user count was the whole platform's, and a seat limit would be decided from it (#811) (#812)
[1.19.15] — 2026-09-08
Changed
- feat(rci): derived Index /10 beneath the 1-5 score; remove three unimported components (#700) (#805)
[1.19.14] — 2026-09-08
Changed
- fix(ci): the required Golden Path check has not started since #712 — an empty env: makes GitHub refuse the workflow (#804)
[1.19.13] — 2026-09-08
Changed
- fix(founder): the founder can reach the founder product (#800) (#802)
[1.19.12] — 2026-09-07
Changed
- feat(auth): the sign-out confirmation is a Phoenix screen (#792) (#797)
[1.19.11] — 2026-09-07
Changed
- feat: connect contact notes, journal mentions and HubSpot history (#796)
[1.19.10] — 2026-09-07
Changed
- fix(auth): Google sign-up no longer lands people on localhost (#793) (#794)
[1.19.9] — 2026-09-07
Changed
- fix: no link or image points at the reader's own machine (#790) (#791)
[1.19.8] — 2026-09-07
Changed
- fix(auth): await params so the login rate limit keeps applying (#788) (#789)
[1.19.7] — 2026-09-07
Changed
- tracker: Circle View points at the commitment-first workspace (#744) (#752)
[1.19.6] — 2026-09-07
Changed
- feat(commitments): entries — insight, challenge, blocker, action (#737) (#740)
[1.19.5] — 2026-09-07
Changed
- fix(integrated): open a life area's detail directly under its row (#735) (#736)
[1.19.4] — 2026-09-07
Changed
- feat(tracker): load life areas, open any month, save every paint (#729) (#731)
[1.19.3] — 2026-09-07
Removed
- An unreachable journal-save endpoint that ran an AI reframe without consent.
POST /api/journal/createhad no caller anywhere in the product (the journal screens and the API client post to/api/journal), yet on every save whose text matched an upset heuristic it sent the entry body to the model provider without reading the "Journal Insights" consent setting. The route is gone, along with the test that existed only for it and its entry in the consent-scan's known-ungated list. Nothing a person could reach has changed.
Proof of zero callers: searched journal/create in every source, test,
Cypress, docs, config and script file; searched every fetch, template and
concatenated form of /api/journal; read lib/api-client/endpoints.ts
(posts to /journal) and lib/db/*. Hits were the route itself, ledger
history, one migration comment, one guard comment, a stale build log and
the route's own test.
- Three navigation components nothing rendered —
components/Navigation.tsx,NavigationM1.tsx,NavigationV2.tsx— and a second, unmounted copy of the footer (components/Footer.tsx). The menu people see isNavigationV3(via the header) and the footer they see isapp/components/Footer.tsx; both are untouched. Proof: searched module paths, symbol imports,require/dynamic imports and JSX usage across the repo; the only Footer importer iscomponents/layout/AppChrome.tsx:59(theapp/componentscopy) and the only navigation importer isapp/components/Header.tsx(NavigationV3). The remaining mentions of the deleted files are prose in three docs and one code comment. - The footer's GitHub links. They pointed at an organisation the app is not built from, and the actual repository is private, so a visitor following them reached a wrong page or a 404. Removed rather than re-pointed; the other social links and the build label are unchanged.
Added
- Delete my account and all my data. Settings → Account now offers two distinct ways out, named for what they do. *Close my account* erases the identity (name, e-mail, profile, password, sign-in links) and retains the records, no longer linked to a person — what the old "Delete Account" button actually did while its copy promised more. *Delete my account and all my data* (
POST /api/account/erase) deletes every row the account owns in every one of the 229 tables the inventory names, then the sign-in identity, in one transaction; if anything would survive, nothing changes. It asks for the account's e-mail address typed back, checked on the server, and is rate-limited like sign-in. One record without content is kept: a hash of the former account id, the time, and row counts per table. Local ritual drafts in the browser are cleared afterwards. Copy in all seven locales says "retained" without a period, because nothing in the code verifies the seven-year basis the older route asserts. - The data export now covers every table that holds your data. Previously 24 tables; now 222 — rituals, wisdom artifacts, attachments, entitlements, payments, evidence links, assessments, notifications and the rest — with a
manifestnaming each table, where it sits in the file and how many rows it holds. The 16 regenerable caches left out are listed by name indocs/privacy/user-data-inventory.md. Existing top-level keys are unchanged. Two tests fail the build when a table gains auser_idcolumn without an export/erasure policy. - The journal pipeline's raw content is in the export and in the erasure proof. The first version of the inventory was public-schema-only; the eight
journal.*/journal_agg.*tables (raw body, media paths and transcriptions, location and weather, model output, promoted fragments) were removed on erasure by the database cascade but missing from the export and unchecked by the erasure's residual verification. They now appear undertables.journal.entries_rawetc., are deleted by name and verified gone, and the live cross-check scans every schema, not onlypublic. - Relationships by commitment. Each commitment now has a "People in this commitment" workspace (
/commitments/<id>/relationships): pick a commitment and an assessment date, and see only the people relevant to it in a sortable table — role, expected contribution, recurring outcome, contact frequency and last meaningful contact, reliance, reciprocity, trust, openness, integrity, workability (all 1–5, unrated stays empty), boundary state, and the next conversation with its by-date. Choosing a past date shows the snapshot as it stood then; a new reading on a new day never rewrites an earlier one. Ended relationships are kept and hidden behind a "Show ended" toggle. Clicking a person opens a side panel with their history across every commitment and date, the reading for the chosen date, and quick actions. People who appear in three or more commitments are listed with the count and one question — "What does this concentration mean to you?" — whose answer is stored in your own words; nothing is inferred from message volume and nothing is interpreted for you. The historical spider/flow/frequency relationship graphs are retired as production UI and kept as design references (docs/relationships/commitment-first-workspace.md). Relationship types gainroleandinner aspectalongside person, institution and group.
Fixed
- Pattern detection now says "consent required" instead of "server error".
POST /api/journal/patterns/detectanswered 500 when the person had not turned on Journal Insights; it now answers 403consent_requiredwith the setting name, the same reply the coach, agent chat and journal AI actions give, so the screen can point to the setting instead of suggesting a retry. A route test holds this (and was shown to fail with the mapping removed).
Changed
- The Boundary Reset ritual now has its quality gate, and three things the gate found are fixed. The owner's criterion — every ritual renders all five steps, validates required input, saves and reloads locally for a signed-out person, creates exactly one durable artifact, keeps the product chrome out, and passes desktop, mobile, keyboard, accessibility, reduced-motion, happy-path, refresh, retry and empty-completion tests — is now a test suite rather than a sentence:
__tests__/rituals/*(jsdom walks of the real runtime), an extension of the axe gate that scans every step, every refused step and the completion screen, a per-required-field refusal matrix in the reducer tests, ande2e/rituals.spec.ts(Playwright, real Chrome: reload on step three, a whole ritual by keyboard, 390×844 with measured 44px targets,prefers-reduced-motionwith a control). Each new test was shown to fail when the behaviour it guards was broken.
What the gate found and what changed for a person on the page: after a
refused step, keyboard focus now moves to the first field that is short
(or to the "Go to that step" control when the short field is on an earlier
step) instead of staying on a button that did nothing; when completion is
refused because an earlier step is short, the page no longer also says
"One answer is still needed on this step" about a step that is complete;
and a failed restore or private sync now reads a fixed, factual sentence
rather than the storage or database error text, which goes to the console.
A local write that a repository rejects is now reported with a "Try saving
again" control while the words stay on screen (the shipped local
repository absorbs storage failures into memory by design, so this appears
only with a repository that reports faults).
Not proven, and said so: syncing under authenticated row-level security
cannot be tested client-side until the same-origin handlers in
docs/rituals/boundary-reset-consolidation-spec.md §4.2 exist; the suite
carries that as a named todo and asserts the current truth instead —
nothing leaves the page during a ritual.
- Every screen now has a declared access layer, and the declaration is checked against the app on every test run. The route-to-layer map that the request pipeline consults (
types/access-control.ts) named 23 paths, only 16 of them actual screens; the other 140 screens fell through to a default nobody had written down, and one named path (/about) had no page at all. The map now carries all 156 screens in the inventory plus the six layer prefixes, and the dead/aboutentry is gone.
Nothing a signed-in person can reach has changed: every screen was given the
layer it was already being served under, and the routes the inventory calls
public are now called public in the map too (the pipeline decides public
before it ever reads the map, so that alignment is bookkeeping). The screens
whose layer is a product decision rather than a fact — which paid layer the
Fulfillment views, Insights, Commitments and the like belong to — are left
on the free "authenticated" layer and listed in the file under LAYER-TBD
for the owner to place.
Three new test files hold this in place: the page files on disk must match
the route inventory exactly in both directions (with a readable diff when
they do not), every inventory route must have a layer and every layer entry
must be a real page, and every capability id the code refers to must exist
in the capability matrix. Each was proven to catch the failure it guards by
injecting one and watching it fail.
- The product is now called Phoenix. Owner decision DEC-0021: the brand is "Phoenix — Operating System for Life Transformation", tagline "Rise into Fulfillment". The header, mobile drawer and sidebar carry a shared wordmark (the Phoenix mark plus the name); the landing page, sign-in, onboarding, settings, integrations, outreach messages, documentation and outbound email say Phoenix where they said WisdomOS, in all seven languages. A single
brandnamespace inmessages/*.json, mirrored bylib/brand.tsfor email and other non-React code, is the one source of the name; a test holds the two together.
"Formerly WisdomOS" appears, on its own line, in exactly three places — the
landing-page hero, the Settings about row and the email footer — and is
temporary: added 2026-09-06, intended to come out on 2026-12-05.
Not renamed, on purpose: routes, database identifiers, environment
variables, the package name, the two-factor issuer, the Airtable field
"WisdomOS ID", and the legal, consent and clinical-disclaimer copy (terms,
privacy, ethics-doctrine acknowledgment, therapy-assisted onboarding), which
wait for legal and clinical review. The email footer year, hardcoded "2024",
now reads the clock.
- Your journal is no longer sent to an AI service unless you have turned "Journal Insights" on. The privacy setting existed, defaulted to off, and was shown in Settings → Privacy — but nothing checked it before the AI actions on a journal entry, the wisdom coach, or the listening agent (with journal context on) sent your entries to the model. They now refuse with a plain statement naming the setting and where it lives. Nothing about the AI's wording has changed; only whether your text goes at all.
Background pattern detection over your journal skips in the same case and
says so in its result ("not analysed: consent not given") rather than
reporting zero patterns as if it had looked.
Not yet covered, and recorded as open: the older journal save path still
sends an entry to the model when it detects an upset. That route belongs to
a separate change.
- The health check now reports whether sign-in can work. The server and the browser each read their own copy of the setting that selects the sign-in system; when the two copies disagree, sign-in fails for everyone with no error anywhere — and the health check stayed green through exactly that outage.
/api/healthnow carriesauth_flags: "agree" | "mismatch", the server logs an error at start-up on a mismatch, and the build refuses to deploy a configuration where the two copies differ.
Added
- Boundary Reset consolidation specification (
docs/rituals/boundary-reset-consolidation-spec.md): capability map of the three coexisting implementations, the single-runtime target at/reset, storage migration, sync design, quality-gate test plan, sequencing, and the open owner decisions. Documentation only; no product behaviour changes.
Added
- The crisis check now reads French, and accents no longer hide a disclosure. Every place that decides whether to show the helplines — the journal editor, the Signals check-in and the listening coach — ran on English keyword lists. A person who wrote « je veux mourir », « je pense au suicide », « me faire du mal » or « en finir » was shown nothing; so was one who wrote « SUICIDÉ » or « suïcide », because the lists matched exact letters. The lists now live in one shared module (
lib/crisis/patterns.ts) with English and French (Québec French included: « j'veux mourir », « chu pu capable », « à boutte ») side by side, and every detector reads them after stripping accents and case. Everyday idioms that share a word with a crisis phrase — « mourir de rire », « tuer le temps », « en finir avec cette histoire », « ça va me tuer », « me couper les cheveux » — are excluded and tested. When the app's language is French, the resource block itself is in French, with the same numbers and links; the other five languages still get the English block, and their detection is still English-only.
- Badges are now Milestones, and they are records, not awards. Owner decision DEC-0021: the page at
/badges(address unchanged) is titled Milestones in all seven languages. The rarity tiers (Common, Rare, Epic, Legendary), the "Badges Earned" / "Completion" / "Total Progress" scoreboard, the padlocks and the earned/locked framing are gone. Each milestone now states the fact it records — "Reflections recorded on 5 consecutive days", "Fulfillment at or above 80% in every life area at the same time" — with a count against the criterion and the date it was recorded. The menu entry reads "Milestones — Recorded milestones"; the Redeem entry now says what that page does ("Redeem an access code") rather than "Redeem rewards".
- The coach is "the Coach", not "Phoenix". The product is Phoenix; the AI persona sharing its name made "Sessions will appear here as Phoenix identifies patterns" read as the product diagnosing you. The persona's identity line in every coaching prompt now says "You are the Coach"; the empty-history note reads "Sessions appear here once you and the Coach have worked through something"; the waiting state reads "The Coach is reading your entries". The prompts' posture (what the coach is told to do) was not changed — that is a separate owner decision.
- "Phoenix Rising" is now "the Phoenix Cycle" — the reset ritual subtitle, the enterprise onboarding welcome, the best-practices resource title, the error-recovery phase and the monthly audit heading — in all seven languages.
- Copy that appraised, hyped or nudged now states the fact. Completing a commitment shows "Commitment recorded." instead of a paragraph about your integrity muscle; "Celebration Notes" is "Notes"; abandoning shows "Renegotiating changes the terms; abandoning ends the commitment. Both are recorded." instead of "Have you considered renegotiating?"; "AI-powered insights / notifications / Goal Recommendations" read "Pattern surfacing", "Reflection notifications", "Goal suggestions"; "AI Coach" is "Coach"; finishing the practice journey shows "Practice journey completed." Streak counters across analytics, daily practice, habits and the Foundation journey now read as consecutive-day counts ("Consecutive days", "Longest sequence"); the practice grant shows "{n} of {m} recorded" instead of XP.
- The forbidden-phrase guard now reads the localisation files. It scanned only
.ts/.tsx, and the copy lives inmessages/*.json— ten "streak"s, four "AI-powered"s and a "Have you considered" sat inen.jsonwhile the guard was green. It now scans the values of all seven locale files (English phrase lists, so English text only — translated renderings still need a human), no longer truncates its report to five hits, no longer skips the API directory where the AI prompts live, and accepts a"_canonGuardAllow.<key>": "<reason>"sibling as the JSON form of its exemption pragma. A vitest guard (__tests__/canon/phrase-guard-messages.test.ts) applies the same lists, read from the script, on every unit-test run.
Fixed
- A person in distress was handed nothing they could call. Three places responded badly. The listening coach, on hearing something like "I want to end my life", acknowledged the pain and asked a question — and gave no helpline. The Signals check-in offered a "Contact Care Team" button that said "Message Sent" without sending anything to anyone: there is no care team. And the journal editor ran no check at all.
All three now show the same list, in the same order: Canada's 9-8-8
(call or text 988), Québec's 1-866-APPELLE (1-866-277-3553) and
suicide.ca, the US 988 Lifeline and Crisis Text Line (text HOME to
741741), findahelpline.com for everywhere else, and 911 if you are in
immediate danger. Each one says plainly that WisdomOS is not a crisis
service and that no one is notified. The care-team button is gone. On the
Signals page the resources now appear even when the save fails — before,
a failed save hid them. In the journal the check runs in your browser
when you press Save, never blocks the save, and your text goes nowhere it
did not already go.
- The product said it does no crisis detection. That was not true. The professional-onboarding disclaimer (in all seven languages), the journal privacy notice, and the scope disclaimer all said WisdomOS "does NOT detect crises". A local text check has run on Signals notes for some time. They now say what happens: a check in your browser may show you resources; it scores nothing, keeps no record, and alerts no one.
- The onboarding disclaimer read "It is NOT:" and then stopped. It rendered only the first line of its own text. It now shows the whole notice.
- The scope disclaimer was never shown to anyone, and Terms and Privacy were linked from nowhere. A short version now sits under the price on the welcome page and on the first onboarding screen, and both link the Terms of Service and Privacy Policy. The footer links them too.
Added
- Deleted journal entries can be brought back. Deleting an entry has always been a soft delete, but there was no way to see or undo it. The journal timeline now has a Show filter (Active / Archived); the Archived view lists what you deleted, with a Restore action on each card, and an archived entry opened directly shows a banner with the same Restore button. Permanent deletion is not offered in the app.
- Journal entries now record where they came from. Each entry carries a provenance — written here, by an agent, imported, or posted by a script — and the timeline has a Source filter (All / Written here / Agent / Imported). Everything that already existed is classified as written here.
Changed
- The delete confirmation says what actually happens. It read "This action cannot be undone. The entry will be permanently deleted." while the entry was in fact archived. It now says the entry moves to Archived and can be restored from there.
- Saving a journal entry refuses what it cannot save, instead of dropping it. Ten real fields — including the review flag behind "Mark for review", the privacy level, the life area, the "this happened on" date and the template — were accepted with a success response and thrown away. Those fields are now saved. Anything the API will not change is refused up front with the field names listed, and
status: 'archived'is no longer accepted as a way to delete.
Fixed
- "Mark for review" on a journal entry never saved. The request was answered with success and the mark was discarded server-side. It is saved now.
- Deleting a journal entry that did not exist reported success. The API answered
200 { success: true }for an id it never matched — including another user's id, which it correctly refused to touch but wrongly claimed to have deleted. It now answers 404. Soft-deleted entries were also recorded two different ways, and different screens checked different ones; there is one flag now, existing entries have been reconciled, and the timeline and the agent's context both honour it. - Your ritual reflections no longer stay on a shared device after you sign out. Ritual sessions, answers and completed artifacts are kept in the browser so that a reflection survives a lost connection or a signed-out session. Signing out — from any menu, from another tab, or by the session expiring — now removes them, and signing in as a different person on the same browser clears the previous person's. Your own in-progress ritual is never cleared when it is still you.
- The Canadian crisis line shown in the app is now 9-8-8. The resources list still carried Crisis Services Canada's retired 1-833 number. It now lists 9-8-8 (call or text, Canada-wide) and, for Québec, 1-866-APPELLE (1-866-277-3553) with suicide.ca.
Removed
- An unreachable data-retention endpoint and its table.
/api/compliance/data-retentionaccepted anyclinic_idfrom any signed-in user, and its table was readable by every signed-in user. The clinic screens that could have used it were removed earlier; nothing in the product called it. The route, its table and the open read policy are gone. This is closing a hole, not removing a feature.
Changed
- The storefront sells one thing: Foundation. Owner decision, 2026-09-06. The Architect ($250/month), Sovereign ($7,500/year) and Organizations ($25,000+) cards, their "Apply"/"Contact" buttons, the "Continue as Architect" block at the end of the Foundation journey, the "Recommended starting point" banner, the coaching/therapy price comparison, the "programs worth $X — you save Y% off retail" card and the scholarships note are no longer shown. None of them had a checkout, an entitlement or a source behind their figures. Their copy and components are kept; a single list in
config/offers.tsdecides what is on sale.
- Pricing copy says only what the code does. Foundation's "What's included" is now generated from the entitlement definition (
lib/payments/offers.ts) — one line per capability the purchase grants, checked by tests against the permission matrix and all seven locales. Gone from the page: "Or $250/week for 4 weeks" (no instalment plan exists), "30-day container", "AI reflection companion", "Foundation completion certificate", "Curated teachings library", "creates the conditions for real transformation", and "build new neural pathways". What replaced them is the payment fact: one payment, processed by Stripe, no card stored, nothing charged again.
- Begin Foundation now starts the checkout. For a signed-in account the button posts to
/api/checkoutand follows Stripe's page; a deployment without Stripe keys says so ("Payments are not set up on this deployment yet. Nothing was charged.") instead of failing silently. Signed-out visitors still create an account first.
- Settings › Account shows your real plan. It used to say "Premium" to every account and list five permissions nobody had checked. It now reads the purchase ledger: Free (with the sampler explained) or Foundation with the purchase date, "Recurring charges: None", "Card on file: None stored", and the same "What's included" lines as the storefront. The disabled "Manage Subscription" button is gone — there is no subscription. It is also where Stripe returns you after paying, and it says "activating" until the payment is actually confirmed rather than welcoming you on arrival.
Fixed
- Cancelling a checkout led to a 404. Stripe's cancel link pointed at
/upgrade, a page deleted under Decision D-1. It now returns to the price list, which states that nothing was charged. The success link pointed at a page that ignored the purchase entirely.
- 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