• v0.11.0 4066c70329

    v0.11.0
    All checks were successful
    E2E / Playwright against staging (push) Successful in 2m52s
    Check / fmt + clippy + build + tests (push) Successful in 5m34s
    Integration / integration tests (push) Successful in 5m52s
    Build OCI container / Build and push mokosh-server image (push) Successful in 18m9s
    Stable

    David released this 2026-08-07 19:08:18 +02:00 | 42 commits to main since this release

    Changelog

    4066c70 Merge pull request 'Release v0.11.0' (#501) from release/v0.11.0 into main
    5a4b68f Release v0.11.0
    520f333 Merge pull request 'fix(ci): name the disk-exhaustion failure instead of crashing in the linker' (#500) from fix/PMS-740-ci-disk-headroom into main
    d2d836c fix(ci): check disk headroom before restoring the cargo cache
    a64de44 fix(ci): name the disk-exhaustion failure instead of crashing in the linker
    aed8ce0 Merge pull request 'feat(reports): measured duration per client-request type' (#499) from feat/PMS-732-request-type-durations into main
    4bdd790 test(reports): cover period boundaries and the no-data case
    074e5f6 feat(kb): surface the measured duration on the article
    d908b79 feat(reports): aggregate tracked time by client-request type
    45c1a62 Merge pull request 'feat(forms): client request forms (MACD) by magic link' (#498) from feat/PMS-730-client-request-forms into main
    679edeb test(forms): cover the request-link flow end to end
    ee7676d feat(forms): mount the public request-form surface
    8fe96af feat(forms): issue, resolve and redeem a client request link
    2ca1ef2 fix(tenants): copy the request-link template to new tenants
    cda8354 feat(forms): add request-link tokens and the request-link email
    a91713a Merge pull request 'feat(forms): form definitions with per-field validation' (#497) from feat/PMS-731-form-definitions into main
    f733192 fix(forms): reject an update that strands a rule on a removed field
    34d6c29 test(forms): cover the definition round trip and per-field rejection
    c06f370 feat(forms): serve form definition CRUD and validated submissions
    fdba879 feat(forms): add the runtime submission validator
    e961c65 feat(forms): add form definition, field and submission tables
    3fab8b7 Merge pull request 'feat(tickets): attach the procedure KB article to a ticket' (#495) from feat/PMS-730-ticket-procedure-kb-article into main
    e5b4fa0 Merge pull request 'fix(just): run the integration suites as the superuser role' (#496) from fix/test-integration-createdb-role into main
    955283b fix(just): run the integration suites as the superuser role
    f0645e8 test(kb): pin the procedure-article round trip and widget exclusion
    0f597e7 feat(tickets): accept and return the procedure KB article
    a111402 feat(tickets): add procedure_kb_article_id for the MACD request flow
    4a35f7e Merge pull request 'fix(auth): repair the JIT placeholder email once bunyip verifies it' (#494) from fix/PMS-635-repair-jit-placeholder-email into main
    f647e52 fix(auth): repair the JIT placeholder email once bunyip verifies it
    6db29e8 Merge pull request 'feat(ip-enrich): consume dunite-ipenrich for advisory ASN/VPN lookup (BUNYIP-475)' (#493) from feat/BUNYIP-475-consume-ipenrich into main
    90aada9 feat(ip-enrich): consume dunite-ipenrich for advisory ASN/VPN lookup (BUNYIP-475)
    675fa35 Merge pull request 'chore(ci): guard the OCI build type=gha cache pattern' (#483) from chore/PMS-720-oci-gha-cache-guard into main
    b17b515 Merge branch 'main' into chore/PMS-720-oci-gha-cache-guard
    c083244 Merge pull request 'chore(deps): re-pin dunite-stripe-core to the merged rev (DEV-495)' (#492) from chore/DEV-495-repin-dunite-merged-rev into main
    b0f8e4c chore(deps): re-pin dunite-stripe-core to the merged rev (DEV-495)
    cb578ba chore(ci): guard the OCI build type=gha cache pattern

    Downloads
  • v0.10.0 6206452a59

    v0.10.0
    Some checks failed
    E2E / Playwright against staging (push) Successful in 2m14s
    Check / fmt + clippy + build + tests (push) Successful in 2m18s
    Integration / integration tests (push) Successful in 5m41s
    Build OCI container / Build and push mokosh-server image (push) Has been cancelled
    Stable

    David released this 2026-08-04 14:09:16 +02:00 | 78 commits to main since this release

    Changelog

    6206452 Merge pull request 'Release v0.10.0' (#482) from release/v0.10.0 into main
    2dfc2e2 Merge branch 'main' into release/v0.10.0
    5866bd1 Merge pull request 'refactor(billing): consume dunite-stripe-core (DEV-514)' (#491) from feat/DEV-514-consume-stripe-core into main
    32fb7de refactor(billing): consume dunite-stripe-core for Stripe primitives (DEV-514)
    6613aca build: depend on shared dunite-stripe-core crate (DEV-514)
    962cda3 Merge pull request 'docs: name .env.example, not .env.dev, as the source of .env (PMS-708)' (#490) from docs/PMS-708-env-example-not-env-dev into main
    53aa1b6 docs: refresh quickstart staleness beyond the .env sweep (PMS-708)
    0200532 docs: sweep remaining .env.dev references to .env.example (PMS-708)
    3b47317 docs: correct .env provenance in CLAUDE.md + compose guards (PMS-708)
    756a083 Merge pull request 'feat(billing): payment provider integration for invoice Pay Now (PMS-711)' (#489) from feat/PMS-711-stripe-pay-now into main
    b35e23f docs(billing): record the Pay Now surface + scope boundaries (PMS-711)
    c7ee972 test(billing): Stripe Pay Now webhook paid/refund/abandoned/bad-sig (PMS-711)
    cd50467 feat(billing): wire Stripe webhook + portal Pay Now + invoice email (PMS-711)
    305c8b2 feat(billing): payment-provider trait + Stripe reconciliation core (PMS-711)
    59ffee7 feat(billing): add payment_refunds + payments.currency migration (PMS-711)
    7d8164d Merge pull request 'fix(rls): set the tenant GUC on serving reads of RLS-covered tables (PMS-692)' (#488) from fix/PMS-692-tenant-guc-rls-reads into main
    4a93717 test(rls): serving reads resolve through the tenant GUC under NOBYPASSRLS (PMS-692)
    d54256c ci(rls): gate new bare .pool() serving calls without a SAFETY note (PMS-692)
    dd55704 fix(rls): scope email_intake + SLA-worker reads; drop dead pool accessor (PMS-692)
    ceb759c fix(rls): scope portal/auth/tickets/tenants reads by the tenant GUC (PMS-692)
    16a069d fix(rls): route Search + Attachment services through the tenant GUC (PMS-692)
    38f1870 Merge pull request 'fix(auth): skip the per-request bunyip userinfo round-trip (PMS-723)' (#487) from fix/PMS-713-skip-userinfo-per-request into main
    61f3b5a test(auth): cover the userinfo short-circuit gate (PMS-723)
    914b7fb fix(auth): skip the per-request bunyip userinfo round-trip (PMS-723)
    c147687 Merge pull request 'feat(seed): production per-account PSA demo, gated to production (PMS-710)' (#485) from feat/PMS-710-per-account-psa-seed into main
    383332d Merge branch 'main' into feat/PMS-710-per-account-psa-seed
    3a16f32 Merge pull request 'fix(e2e): make the TOTP 2fa step window-safe to stop /login/2fa stalls' (#486) from fix/e2e-totp-window-race into main
    6748170 fix(e2e): make the TOTP 2fa step window-safe to stop /login/2fa stalls
    2d44993 test(seed): cover the PMS-710 production profile and the gate (PMS-710)
    dcdb64e feat(seed): production per-account PSA demo, gated to production (PMS-710)
    b1ff76b Merge pull request 'style(e2e): separate the login retry comment blocks' (#484) from fix/PMS-721-e2e-login-response-diagnostics into main
    4fad0e7 style(e2e): separate the login retry comment blocks
    b4111ed fix(e2e): log main-frame responses and classify a missing login form
    790a925 Release v0.10.0
    968e7a4 Merge pull request 'feat(auth): make profile names Bunyip-owned and read-only' (#481) from feat/PMS-512-bunyip-owned-profile-names into main
    aee1f64 feat(auth): make profile names Bunyip-owned and read-only
    ff38144 Merge pull request 'chore(ci): run the compiling jobs on the dev runner label' (#480) from chore/PMS-719-ci-dev-runner-label into main
    b098f82 chore(ci): run the compiling jobs on the dev runner label
    38c8945 Merge pull request 'fix(auth): give the OAuth popup a nonce CSP so its script runs' (#479) from fix/PMS-691-oauth-popup-csp-nonce into main
    d17051c fix(auth): give the OAuth popup a nonce CSP so its script runs
    955e151 Merge pull request 'fix(auth): count a failed recovery code against MFA lockout' (#478) from fix/PMS-694-recovery-code-mfa-lockout into main
    54ba114 fix(auth): count a failed recovery code against MFA lockout
    324d3b7 Merge pull request 'fix(billing): lock the invoice row before recomputing amount_paid' (#477) from fix/PMS-695-lock-invoice-on-payment into main
    34548ff fix(billing): lock the invoice row before recomputing amount_paid
    2367217 Merge pull request 'fix(auth): gate the bunyip path on user and tenant status' (#476) from fix/PMS-698-bunyip-principal-gate into main
    12db844 fix(auth): gate the bunyip path on user and tenant status
    3bcfa97 Merge pull request 'fix(notifications): deliver the authored body_html' (#475) from fix/PMS-700-dispatch-body-html into main
    f1a45c9 fix(notifications): deliver the authored body_html
    b68cd71 Merge pull request 'fix(auth): increment attempt counters atomically' (#474) from fix/PMS-693-atomic-mfa-attempt-counter into main
    caeba42 fix(auth): increment attempt counters atomically
    378950d Merge pull request 'fix(notifications): skip dispatch when no template matches' (#473) from fix/PMS-701-drop-context-dump-fallback into main
    9e17079 fix(notifications): skip dispatch when no template matches
    9c68160 Merge pull request 'fix(reports): derive report SELECT aliases from the whitelist' (#472) from fix/PMS-690-report-alias-injection into main
    6773722 fix(reports): derive report SELECT aliases from the whitelist
    8666db4 Merge pull request 'fix(dev): stop the dev stack /ready 503 from Infisical' (#471) from fix/PMS-707-dev-ready-infisical-probe into main
    8fc4606 fix(dev): stop the dev stack /ready 503 from Infisical
    6e0ae4f Merge pull request 'fix(notifications): reseed the 023 templates and report unresolved keys' (#470) from fix/PMS-702-reseed-notification-templates into main
    f646985 fix(notifications): reseed the 023 templates and report unresolved keys
    30cde30 Merge pull request 'fix(rls): complete the fail-closed backstop on 11 deferred tables (PMS-683)' (#467) from fix/PMS-683-rls-tail into main
    be81c4c Merge branch 'main' into fix/PMS-683-rls-tail
    6dce6ab Merge pull request 'fix(ready): bound the readiness DB ping at 3s' (#469) from fix/pms-685-readiness-db-ping-timeout into main
    6bbda8e fix(ready): bound the readiness DB ping at 3s
    e4e8a79 fix(rls): complete the fail-closed backstop on 11 deferred tables

    Downloads
  • v0.9.0 43587d2d2a

    v0.9.0
    All checks were successful
    E2E / Playwright against staging (push) Successful in 45s
    Check / fmt + clippy + build + tests (push) Successful in 1m55s
    Build OCI container / Build and push mokosh-server image (push) Successful in 5m24s
    Integration / integration tests (push) Successful in 6m10s
    Stable

    David released this 2026-07-30 20:26:10 +02:00 | 141 commits to main since this release

    Changelog

    43587d2 Merge pull request 'Release v0.9.0' (#468) from release/v0.9.0 into main
    4903595 ci: install gcc + libopenssl-devel on the trimmed openSUSE runner
    fa78132 Release v0.9.0

    Downloads
  • v0.8.0 a317a14e34

    v0.8.0
    All checks were successful
    E2E / Playwright against staging (push) Successful in 40s
    Check / fmt + clippy + build + tests (push) Successful in 1m41s
    Build OCI container / Build and push mokosh-server image (push) Successful in 4m52s
    Integration / integration tests (push) Successful in 8m45s
    Stable

    David released this 2026-07-24 20:19:51 +02:00 | 144 commits to main since this release

    Changelog

    a317a14 Merge pull request 'Release v0.8.0' (#465) from release/v0.8.0 into main
    80fcb02 Release v0.8.0
    34750f7 Merge pull request 'feat(db): fail-closed RLS backstop on quote tables and tenant-RLS coverage guard (PMS-683)' (#464) from fix/PMS-683-rls-backstop into main
    f9f98a0 Merge pull request 'fix(cleanup): pagination offset overflow, constant-time code check, module docs (PMS-684)' (#463) from fix/PMS-684-cleanup into main
    0c22cc0 feat(db): enable RLS on quote tables and add tenant-RLS coverage guard
    9381130 fix(cleanup): offset overflow, constant-time code check, module docs
    f2dd3a9 Merge pull request 'test(auth): lock in bootstrap-admin login without email verification (PMS-676)' (#462) from feat/PMS-676-bootstrap-admin-unverified-login into main
    7f371b4 test(auth): lock in bootstrap-admin login without email verification (PMS-676)
    25cb584 Merge pull request 'chore(config): reconcile OAUTH_SUPER_ADMIN env var name with the code (PMS-682)' (#461) from chore/PMS-682-oauth-super-admin-env-drift into main
    006bd81 chore(config): reconcile OAUTH_SUPER_ADMIN env var name with the code (PMS-682)
    7f62826 Merge pull request 'feat(auth): invalidate access tokens on password reset/change (PMS-681)' (#460) from feat/PMS-681-invalidate-tokens-on-password-change into main
    c28dff4 refactor(auth): address PMS-681 review (fold password_changed_at into the user load, atomic revoke)
    acd0b58 feat(auth): invalidate access tokens on password reset/change (PMS-681)
    80c261f Merge pull request 'feat(auth): rate-limit /forgot-password per (IP, email) (PMS-680)' (#459) from feat/PMS-680-forgot-password-rate-limit into main
    f6b9a77 feat(auth): rate-limit /forgot-password per (IP, email) (PMS-680)
    ad6e4eb Merge pull request 'test(auth): password-reset redeem coverage (PMS-659)' (#458) from test/PMS-659-password-reset-coverage into main
    066e3f7 test(auth): cover the password-reset redeem flow (PMS-659)
    d2cbd64 Merge pull request 'feat(data): add admin Load demo data endpoint (PMS-679)' (#457) from feat/PMS-679-load-demo-data-button into main
    67c16d4 feat(data): add admin "Load demo data" endpoint (PMS-679)
    3ea1df7 Merge pull request 'feat(dev): just verify-demo for the demo seed + import/export path (PMS-677)' (#456) from feat/PMS-677-verify-demo-recipe into main
    5713f70 feat(dev): add just verify-demo for the demo-critical seed + import/export path (PMS-677)
    9420509 Merge pull request 'feat(quotes): convert an accepted quote into a Project (PMS-674)' (#455) from feat/PMS-674-convert-quote-to-project into main
    bb2e456 test(quotes): integration coverage for quote-to-project conversion (PMS-674)
    302bcd0 feat(quotes): mount POST /quotes/{id}/convert (PMS-674)
    a61298d feat(quotes): convert an accepted quote into a Project (PMS-674)
    b5c11a0 Merge pull request 'feat(quotes): send to client + portal accept/decline sign-off (PMS-673)' (#454) from feat/PMS-673-quote-send-and-portal-signoff into main
    d6b241f Merge pull request 'feat(quotes): quotes module CRUD with server-computed totals (PMS-672)' (#453) from feat/PMS-672-quotes-module-crud into main
    1142548 test(quotes): integration coverage for send and client sign-off (PMS-673)
    fd88bde feat(quotes): send route plus the portal sign-off routes (PMS-673)
    7ff7d54 feat(quotes): issue to the client and record their sign-off (PMS-673)
    f657485 feat(portal): rate limiter for the quote decision routes (PMS-673)
    ece3d9a feat(email): quote-ready template for the client sign-off link (PMS-673)
    9196a2b test(quotes): integration coverage for CRUD, totals, freezing, tenancy (PMS-672)
    b630b68 feat(quotes): mount the quotes routes under /api/v1 (PMS-672)
    2bcc117 feat(quotes): quotes service with server-computed totals (PMS-672)
    5955c86 feat(quotes): quote + line DTOs and the lifecycle status model (PMS-672)
    438c84d Merge pull request 'feat(quotes): promote the quote stub to a real entity + quote_lines (PMS-671)' (#452) from feat/PMS-671-quotes-entity-schema into main
    2b97c45 test(approvals): seed a company for the quote fixture (PMS-671)
    e233923 feat(quotes): promote the quote stub to a real entity + quote_lines (PMS-671)
    7b4d1fa Merge pull request 'feat(auth): suspicious-login notify-and-approve gate for password login (PMS-658)' (#451) from feat/PMS-658-suspicious-login-approval into main
    5d9a6e0 feat(auth): suspicious-login notify-and-approve gate for password login

    Downloads
  • v0.7.0 2d3f5893d6

    v0.7.0
    All checks were successful
    E2E / Playwright against staging (push) Successful in 41s
    Check / fmt + clippy + build + tests (push) Successful in 1m41s
    Build OCI container / Build and push mokosh-server image (push) Successful in 5m44s
    Integration / integration tests (push) Successful in 10m11s
    Stable

    David released this 2026-07-15 14:18:47 +02:00 | 185 commits to main since this release

    Changelog

    2d3f589 Merge pull request 'Release v0.7.0' (#450) from release/v0.7.0 into main
    b8a52cb Release v0.7.0
    3aeaac9 Merge pull request 'feat(auth): alert users on a new-country login (PMS-657)' (#449) from feat/PMS-657-login-location-alerts into main
    e778b46 feat(auth): user-settable login-location opt-out, non-blocking alert (PMS-657)
    100ade1 feat(auth): alert users on a new-country login (PMS-657)
    fe5d511 Merge pull request 'feat(seed): load first-visit demo data from a committed bundle, not hardcoded Rust (PMS-651)' (#448) from feat/PMS-651-data-driven-seed into main
    1c0c120 feat(seed): load first-visit demo data from a committed bundle, not hardcoded Rust (PMS-651)
    40a8b83 Merge pull request 'test(seed): assert demo seed is the clean baseline with no zQA/QA prefix (PMS-629)' (#447) from fix/PMS-629-demo-seed-no-zqa into main
    2da0a7e test(seed): assert demo seed is the clean baseline with no zQA/QA prefix (PMS-629)
    dd5e390 Merge pull request 'fix(e2e): correct stale provisioning docs + cli.rs drift, skip suite when staging unseeded' (#446) from fix/PMS-656-e2e-provisioning-cleanups into main
    abbe8c7 fix(e2e): correct stale provisioning docs + cli.rs drift, skip suite when staging unseeded
    fc82ffe Merge pull request 'fix(audit): record real client IP behind Traefik' (#444) from fix/PMS-587-audit-client-ip into main
    af6f8d9 Merge remote-tracking branch 'origin/main' into fix/PMS-587-audit-client-ip
    b321f82 Merge pull request 'fix(e2e): submit login once with rate-limit backoff, surface the hub banner' (#445) from fix/PMS-654-e2e-login-backoff into main
    c0721b4 Merge pull request 'fix(data): defer FK checks so tenant import survives cyclic schema' (#442) from feat/PMS-648-deferrable-fk-import into main
    4f62cc0 fix(e2e): submit login once with rate-limit backoff, surface the hub banner
    6de4363 Merge remote-tracking branch 'origin/main' into feat/PMS-648-deferrable-fk-import
    b3954a1 fix(audit): record real client IP behind Traefik
    bfd56b9 Merge pull request 'feat(data): include tenant_name in the export envelope (PMS-647)' (#443) from feat/PMS-647-export-tenant-name into main
    74b50a6 ci: re-trigger checks (E2E flake on shared staging account)
    d12bead feat(data): include tenant_name in the export envelope (PMS-647)
    86ae88b fix(data): defer FK checks so tenant import survives cyclic schema
    f2de869 Merge pull request 'fix(data): break tenants->company FK before import wipe (un-red main, PMS-648)' (#441) from feat/PMS-648-data-import into main
    8cde8b3 test(data): quarantine import round-trip until deferrable-FK redesign
    7380d40 fix(data): break tenants->company FK before the import wipe
    9880f2e Merge pull request 'test(data): fix red integration round-trip (robust to pre-seeded data, PMS-648)' (#440) from feat/PMS-648-data-import into main
    07fdd05 test(data): make export/import round-trip robust to pre-seeded tenant data
    8f01cb1 Merge pull request 'feat(data): tenant data import - envelope validation + id-remap core (PMS-648)' (#439) from feat/PMS-648-data-import into main
    575bd33 Merge pull request 'feat(data): admin-only tenant data export endpoint (PMS-647)' (#438) from feat/PMS-647-data-export into main
    88df505 feat(data): tenant data import wipe-and-replace load + round-trip test
    d193e46 feat(data): tenant data import - envelope validation + id-remap core
    25a583e feat(data): admin-only tenant data export endpoint
    a9f0f4d Merge pull request 'fix(e2e): make the setup onboarding PUT best-effort, not fatal (PMS-645)' (#437) from fix/PMS-645-onboard-best-effort into main
    3c42e55 fix(e2e): make the setup onboarding PUT best-effort, not fatal
    a89cf26 Merge pull request 'fix(release): sync Cargo.lock on release and correct main's drift (PMS-642)' (#435) from fix/PMS-642-release-syncs-cargo-lock into main
    64717d5 Merge pull request 'fix(dev): mount ./tests so pre-commit compiles integration tests (PMS-640)' (#434) from fix/PMS-640-precommit-compile-integration-tests into main
    54920a7 Merge pull request 'feat(settings): DB-backed SMTP email config with live mailer swap (PMS-638)' (#433) from feat/PMS-638-email-db-settings into main
    7663cb3 Merge branch 'main' into fix/PMS-642-release-syncs-cargo-lock
    15d3141 Merge branch 'main' into fix/PMS-640-precommit-compile-integration-tests
    dba0e5d Merge branch 'main' into feat/PMS-638-email-db-settings
    db11e3d Merge pull request 'fix(e2e): onboard the E2E fixture account so specs are not bounced to onboarding (PMS-641)' (#436) from fix/PMS-641-onboard-e2e-account into main
    b777e41 fix(e2e): onboard the E2E fixture account so specs are not bounced to onboarding
    afec11e chore(release): drop no-op SQLX_OFFLINE from the cargo update step
    e09f70d fix(release): sync Cargo.lock on release and correct main's drift
    9375020 fix(dev): mount ./tests so pre-commit compiles the integration tests
    e9e19b8 fix(test): wrap LogMailer in SharedMailer for create_api_router
    3cc8a96 fix(settings): build-validate email config before persisting it
    9040a0b feat(settings): DB-backed SMTP email config with live mailer swap

    Downloads
  • v0.6.0 c43bd44437

    v0.6.0
    All checks were successful
    E2E / Playwright against staging (push) Successful in 42s
    Check / fmt + clippy + build + tests (push) Successful in 1m55s
    Integration / integration tests (push) Successful in 7m28s
    Build OCI container / Build and push mokosh-server image (push) Successful in 10m21s
    Stable

    David released this 2026-07-07 15:56:59 +02:00 | 233 commits to main since this release

    Changelog

    c43bd44 Merge pull request 'Release v0.6.0' (#432) from release/v0.6.0 into main
    dc4436f Release v0.6.0
    2afc571 Merge pull request 'feat(seed): extend qa-seed with assets, KB, SLA, and appointments (PSA-55)' (#431) from feat/PSA-55-qa-seed-demo-gaps into main
    10bb709 Merge remote-tracking branch 'origin/main' into feat/PSA-55-qa-seed-demo-gaps
    9e27995 fix(e2e): tolerate bunyip 2FA auto-submit in the login helper
    4b8d73d feat(seed): give the QA SLA policy a response/resolution target (PSA-55)
    1568eec feat(seed): extend qa-seed with assets, KB, SLA, and appointments (PSA-55)
    66d74f0 Merge pull request 'feat(seed): add richer showcase demo dataset with create/refresh' (#430) from feat/PMS-620-showcase-seed-dataset into main
    0fbbf13 feat(seed): add richer showcase demo dataset with create/refresh
    fd9c86e Merge pull request 'fix(email): set Message-ID header on every outbound message' (#428) from fix/PMS-624-outbound-message-id into main
    10bfc58 fix(email): set Message-ID header on every outbound message
    dc3c254 Merge pull request 'feat(e2e): exclude external-service flows from production E2E runs' (#427) from feat/PMS-608-exclude-external-from-prod-e2e into main
    83a5963 feat(e2e): exclude external-service flows from production E2E runs
    0424c83 Merge pull request 'fix(auth): enforce role ceiling on user update and fix OAuth callback XSS' (#426) from fix/PMS-625-auth-audit-privesc-xss into main
    8fe83cd fix(auth): enforce role ceiling on user update and fix OAuth callback XSS
    8d4b241 Merge pull request 'feat(auth): return 410 Gone (ACCOUNT_DELETED) for tombstoned users on the API surface' (#421) from feat/MAPPS-348-410-gone-account-deleted into main
    a01c616 Merge pull request 'test(e2e): un-fixme form-validation now that MAPPS-347 SPA fix has merged' (#422) from test/MAPPS-347-unfixme-form-validation into main
    05fdfc7 feat(auth): return 410 Gone (ACCOUNT_DELETED) for tombstoned users on the API surface
    c1b50fd test(e2e): un-fixme form-validation now that MAPPS-347 SPA fix has merged
    58cd083 Merge pull request 'docs(webhook): describe BUNYIP_WEBHOOK_SECRET as service-wide, not per-Application' (#424) from docs/BUNYIP-332-correct-webhook-secret-comment into main
    629c4e7 ci: retrigger against rebased tip (post-BUNYIP-331 e2e fix)
    6a8037a docs(webhook): describe BUNYIP_WEBHOOK_SECRET as service-wide, not per-Application
    59aa239 Merge pull request 'test(e2e): make fillTotpStep + setInputValue aware of the BUNYIP-331 auto-submit' (#425) from test/BUNYIP-331-autosubmit-aware-fillTotpStep into main
    da283a6 test(e2e): make fillTotpStep + setInputValue aware of the BUNYIP-331 auto-submit
    bb55f38 Merge pull request 'feat(auth): receive Bunyip account_deleted webhook and soft-delete the mokosh user' (#416) from feat/PMS-591-bunyip-account-deleted-receiver into main
    0a29847 test(e2e): fixme form-validation spec on MAPPS-347 SPA regression
    a86d8e2 Merge pull request 'feat(sla): validate schedule/holiday JSON on write (reject malformed, 422)' (#420) from feat/PMS-604-sla-json-validation into main
    774cc38 test(sla): cover accept/reject cases for schedule and holiday JSON
    f6c3ad8 feat(sla): reject malformed holiday-calendar JSON on write (422)
    838306c feat(sla): reject malformed business-hours schedule JSON on write (422)
    e66e7b0 refactor(sla): expose clock weekday/time parsers to the module
    1741855 Merge remote-tracking branch 'origin/main' into feat/PMS-591-bunyip-account-deleted-receiver
    6c2c6ca test(auth): match synthetic-name expectations to titlecased output
    d0ec426 feat(auth): receive Bunyip account_deleted webhook and soft-delete the mokosh user

    Downloads
  • v0.5.0 85a0bde835

    v0.5.0
    All checks were successful
    E2E / Playwright against staging (push) Successful in 1m13s
    Check / fmt + clippy + build + tests (push) Successful in 1m29s
    Build OCI container / Build and push mokosh-server image (push) Successful in 10m20s
    Integration / integration tests (push) Successful in 11m13s
    Stable

    David released this 2026-07-01 19:02:59 +02:00 | 267 commits to main since this release

    Changelog

    85a0bde Merge pull request 'Release v0.5.0' (#418) from release/v0.5.0 into main
    46b6a89 fix(e2e): run setup in firefox so bearer capture stops flaking on chromium
    4bca793 Merge branch 'main' into release/v0.5.0
    ef22f82 Merge pull request 'fix(e2e): DOM-set login inputs so headless chromium submits credentials (PMS-592)' (#419) from fix/PMS-592-login-fill into main
    943e28b fix(e2e): survive the post-login chromium crash so the api suite runs
    d4c03ec fix(e2e): --disable-dev-shm-usage so chromium stops crashing in CI
    1ab473e fix(e2e): capture bearer via in-app nav so the WASM token survives
    8ede68e fix(e2e): DOM-set login inputs so headless chromium submits credentials
    a4eb28d Release v0.5.0
    bd2b2fd Merge pull request 'chore(docs): consolidate markdown under docs/ + distill audits into changelog (PSA-48)' (#417) from chore/PSA-48-docs-sanitize into main
    6b2bcc3 revert(migrations): restore immutable applied migrations
    569c014 chore(docs): consolidate markdown under docs/, distill audits + handoff into changelog
    70bccc2 Merge pull request 'ci(migrations): guard against editing already-committed migrations' (#415) from feat/DEV-395-migration-immutability-guard into main
    5f16f44 Merge branch 'main' into feat/DEV-395-migration-immutability-guard
    18cf5be Merge pull request 'docs(sla): JSONB-vs-normalized decision for schedule/holidays (PMS-585)' (#414) from docs/PMS-585-sla-jsonb-decision into main
    9fb95e2 ci(migrations): guard against editing already-committed migrations
    87ea7b6 docs(sla): record JSONB-vs-normalized decision for schedule/holidays (PMS-585)
    ffc296e Merge pull request 'feat(contacts): backfill existing company industries to canonical set (PMS-602)' (#413) from feat/PMS-602-backfill-company-industries into main
    3ada191 style(contacts): wrap long macro calls to satisfy CI rustfmt (PMS-602)
    8202739 feat(bootstrap): normalize-company-industries subcommand (PMS-602)
    3b32f6d test(contacts): industry backfill map integrity (PMS-602)
    a5b9223 feat(contacts): company-industry backfill normalizer (PMS-602)
    9e69653 Merge pull request 'feat(contacts): company-industries settings lookup (PMS-601)' (#412) from feat/PMS-601-company-industries-lookup into main
    0478eca feat(contacts): company-industry lookup endpoints (PMS-601)
    bc5ed67 feat(contacts): company-industry lookup CRUD service (PMS-601)
    7e01291 feat(types): CompanyIndustry lookup DTOs (PMS-601)
    acf9280 feat(tenants): seed company_industries for new tenants (PMS-601)
    26de04d feat(billing): add company_industries lookup table (PMS-601)
    b09fdd2 Merge pull request 'feat(contacts): distinct title/department values endpoint for autocomplete (PMS-583)' (#411) from feat/PMS-583-contact-field-suggestions into main
    0727b16 feat(contacts): distinct title/department values endpoint for autocomplete (PMS-583)
    f778898 Merge pull request 'docs(billing): document invoice lifecycle and void-vs-cancel (PMS-580)' (#410) from docs/PMS-580-invoice-lifecycle into main
    bf0c976 docs(billing): document invoice lifecycle and void-vs-cancel (PMS-580)

    Downloads
  • v0.4.0 aefa9ea03d

    v0.4.0
    All checks were successful
    Build OCI container / Build and push mokosh-server image (push) Successful in 5m12s
    Stable

    David released this 2026-06-30 17:33:32 +02:00 | 299 commits to main since this release

    Changelog

    2e6ba89 Merge pull request 'Release v0.4.0' (#407) from release/v0.4.0 into main
    4119b20 Merge branch 'main' into release/v0.4.0
    9f5e7fc Merge pull request 'fix(e2e): settle hub login form before fill + URL-based 2FA advance signal' (#409) from fix/PMS-595-chromium-credential-resubmit-settle into main
    4cf23bd fix(e2e): settle hub login form before fill + URL-based 2FA advance signal
    026120b fix(e2e): recover chromium SPA login from credential-form re-render race
    60d9b9c Release v0.4.0
    3bafb3c Merge pull request 'fix(auth): explicit Validation::new(Algorithm::HS256) + mint iss/aud/nbf claims' (#406) from fix/MAPPS-334-explicit-validation-secret-floor-v2 into main
    92ed8c1 fix(auth): disable validate_aud until strict iss/aud flip lands
    a6db9f5 Merge pull request 'fix(auth): bind userinfo.sub == claims.sub; truthful email_verified_at' (#404) from fix/MAPPS-335-sub-binding-and-email-verified-truth into main
    d387ebd Merge pull request 'fix(auth): kid-aware JWKS refresh + cap force-refresh cadence + active checks on legacy fallback' (#405) from fix/MAPPS-337-jwks-kid-aware-redirect-cap-active-checks into main
    5722869 fix(auth): kid-aware JWKS refresh + cap force-refresh cadence + active checks on legacy fallback
    9e70ecc fix(auth): bind userinfo.sub == claims.sub; truthful email_verified_at
    ab59925 fix(auth): explicit Validation::new(Algorithm::HS256) + mint iss/aud/nbf claims
    4a857d2 Merge pull request 'fix(config): refuse to boot with a dev-default JWT_SECRET outside dev/test (PMS-497)' (#370) from fix/PMS-497-jwt-secret-production-guard into main
    19dc713 Merge pull request 'feat(api): emit security response headers on every response (PMS-388)' (#367) from feat/PMS-388-app-level-security-headers into main
    102b80b Merge pull request 'fix(dev): bind dev Postgres to loopback and reject public LAN IP' (#399) from fix/dev-db-loopback-bind-on-main into main
    7aa5e68 Merge branch 'main' into fix/PMS-497-jwt-secret-production-guard
    1854d64 Merge pull request 'feat(roles): every mokosh user is an admin of their own instance' (#401) from feat/MAPPS-330-everyone-is-mokosh-admin into main
    39395ed Merge branch 'main' into feat/PMS-388-app-level-security-headers
    7da858b Merge pull request 'fix(auth): cap + anti-replay TOTP verification on agent login' (#400) from fix/PMS-502-mfa-totp-attempt-cap-anti-replay into main
    7fe6aea Merge branch 'main' into feat/PMS-388-app-level-security-headers
    1c9c4fa Merge main: drop superseded postgres-loopback hunk, keep justfile RFC1918 filter
    0fb2d21 chore(migrations): renumber MAPPS-330 backfill 084 -> 085
    0ac0b6f Merge pull request 'fix(e2e): deflake firefox form-validation by waiting for nav links to be visible' (#402) from fix/PMS-543-firefox-nav-visible-wait into main
    f3f1e49 fix(e2e): wait for nav links to be visible to deflake firefox form-validation
    9c49820 fix(migration): drop nonexistent users.deleted_at guard from MAPPS-330 backfill
    cf5860b feat(roles): every mokosh user is an admin of their own instance
    9f61780 fix(auth): cap + anti-replay TOTP verification on agent login
    c8f2250 Merge pull request 'fix(dev): bind app Postgres to loopback and fail loud on missing DB secrets (PMS-496)' (#392) from fix/PMS-496-postgres-loopback-bind-fail-loud-secrets into main
    b9f0cff Merge branch 'main' into fix/PMS-496-postgres-loopback-bind-fail-loud-secrets
    071b3dd fix(dev): bind dev Postgres to loopback and reject public LAN IP
    f8ea1f8 Merge pull request 'test(e2e): expect inline "Company is required." copy on new-ticket form (PMS-535)' (#394) from fix/PMS-535-company-required-copy into main
    508fceb fix(config): repair merge tail + drop superseded validate_jwt_secret (PMS-497)
    7c400b9 test(e2e): expect inline "Company is required." copy on new-ticket form (PMS-535)
    b99cb20 Merge remote-tracking branch 'origin/main' into feat/PMS-388-app-level-security-headers
    f7091ae Merge remote-tracking branch 'origin/main' into fix/PMS-497-jwt-secret-production-guard
    72975a0 ci: re-trigger E2E against staging (PMS-496)
    9f74f7c Merge remote-tracking branch 'origin/main' into fix/PMS-496-postgres-loopback-bind-fail-loud-secrets
    8007fb9 Merge pull request 'Derive OCI publish mode from trigger to stop twin-publish race (GOV-15)' (#382) from fix/GOV-15-twin-trigger-publish-mode into main
    817b4d9 Merge branch 'main' into fix/GOV-15-twin-trigger-publish-mode
    021897a Merge pull request 'test(kb): format assert_eq! in top-driving test, land coverage (PMS-533)' (#393) from fix/PMS-533-kb-test-rustfmt into main
    f859ec0 test(kb): format assert_eq! in top-driving test, land coverage (PMS-533)
    300c0ab fix(dev): bind app Postgres to loopback and fail loud on missing DB secrets (PMS-496)
    a0e3b64 Merge pull request 'ci(e2e): use bun x instead of missing bunx shim (PMS-532)' (#391) from fix/PMS-532-e2e-bunx-shim into main
    a1a1ef9 ci(e2e): use bun x instead of missing bunx shim (PMS-532)
    233c31a Merge pull request 'feat(dev): ensure-env generates all self-owned secrets (PMS-490)' (#390) from feat/PMS-490-ensure-env-generates-secrets into main
    75c11b9 feat(dev): ensure-env generates all self-owned secrets (PMS-490)
    d1f0b51 Merge pull request 'chore(auth): drop stale crates/mokosh-auth-http comment refs (PMS-295)' (#389) from feat/PMS-295-remove-mokosh-auth-mechanism-2 into main
    88b5a47 chore(auth): drop stale crates/mokosh-auth-http comment refs (PMS-295)
    1c619d7 feat(auth): remove mokosh-auth; bunyip-as-OP is the sole OP (PMS-295)
    d29284e Merge pull request 'feat(workflows): add ticket templates for new-ticket pre-fill (PMS-448)' (#387) from feat/PMS-448-ticket-templates into main
    eb20ab6 feat(workflows): add ticket templates for new-ticket pre-fill (PMS-448)
    443e2e7 ci(e2e): run E2E on opensuse-dev runner with pre-baked Playwright (PMS-423)
    4145f0d Merge pull request 'feat(portal): rate-limit and lock out brute-force portal logins (PMS-501)' (#385) from feat/PMS-501-portal-login-rate-limit-lockout into main
    9f431b3 feat(portal): rate-limit and lock out brute-force portal logins (PMS-501)
    06166ac Merge pull request 'feat(time-tracking): split billable from worked minutes per entry (PMS-395)' (#384) from feat/PMS-395-billable-minutes-split into main
    0ddb5d8 feat(time-tracking): split billable from worked minutes per entry (PMS-395)
    ac07565 Merge pull request 'fix(config): require 64-hex ENCRYPTION_KEY outside dev/test (PMS-498)' (#383) from fix/PMS-498-require-hex-encryption-key-in-prod into main
    cb2789e fix(config): require 64-hex ENCRYPTION_KEY outside dev/test (PMS-498)
    4ae07e4 fix(ci): derive publish mode from trigger to stop twin-publish race
    df0090a Merge pull request 'feat(db): self-provision split DB roles at startup (PMS-489)' (#381) from feat/PMS-489-self-provision-db-roles into main
    c73584a feat(db): self-provision split DB roles at startup (PMS-489)
    8daf44d Merge pull request 'fix(auth): enforce role ceiling on create_user (PMS-503)' (#380) from fix/PMS-503-create-user-role-ceiling into main
    10f4a2c fix(auth): enforce role ceiling on create_user (PMS-503)
    6d92b7b Merge pull request 'feat(email-intake): store inbound email attachments on the ticket (PMS-450)' (#379) from feat/PMS-450-email-intake-attachments into main
    8a5cf9a feat(email-intake): store inbound email attachments on the ticket (PMS-450)
    9999177 Merge pull request 'fix(config): refuse dev JWT_SECRET/ENCRYPTION_KEY outside dev/test (PMS-499)' (#378) from fix/PMS-499-guard-dev-secrets-in-prod into main
    aaad18c fix(config): refuse dev JWT_SECRET/ENCRYPTION_KEY outside dev/test (PMS-499)
    a37c2bf Merge pull request 'chore(toolchain): pin rust-toolchain.toml to 1.94.1 (PMS-412)' (#377) from chore/PMS-412-pin-rust-toolchain into main
    8262fb9 chore(toolchain): pin rust-toolchain.toml to 1.94.1 (PMS-412)
    0952d0f Merge pull request 'feat(assets): add licence section (vendor, seat count, expiry) to CMDB (PMS-454)' (#376) from feat/PMS-454-asset-license-fields into main
    c3b4f31 feat(assets): add licence section (vendor, seat count, expiry) to CMDB (PMS-454)
    df9ea35 Merge pull request 'chore(ci): rename OCI build job to mokosh-server (PMS-133)' (#374) from chore/PMS-133-rename-oci-job-to-mokosh-server into main
    71932ee chore(ci): rename OCI build job to mokosh-server (PMS-133)
    ee465ac Merge pull request 'chore(security): triage audit roll-up findings (PMS-504)' (#373) from chore/PMS-504-security-audit-triage into main
    2d4cf75 Merge pull request 'ci(oci): switch build cache from type=registry/inline to type=gha' (#366) from fix/PMS-525-oci-gha-build-cache into main
    fd0051c Merge branch 'main' into fix/PMS-525-oci-gha-build-cache
    6dc0077 chore(security): triage audit roll-up findings (PMS-504)
    05ff30c Merge pull request 'fix(ci): pass PR head ref as data, scope contents:write to publish job (PMS-500)' (#372) from fix/PMS-500-harden-create-release-injection into main
    7cdf124 fix(ci): pass PR head ref as data, scope contents:write to publish job (PMS-500)
    821b04a Merge pull request 'feat(dev): merge compose.dev-sso overlay into compose.dev.yml (PMS-511)' (#371) from feat/PMS-511-merge-dev-compose-files into main
    4dd9fdb feat(dev): merge compose.dev-sso overlay into compose.dev.yml (PMS-511)
    e6089c9 fix(config): refuse to boot with a dev-default JWT_SECRET outside dev/test (PMS-497)
    922d736 Merge pull request 'feat(cli): fold bootstrap-infisical/clients/qa subcommands into mokosh-server (PMS-494)' (#369) from feat/PMS-494-fold-bootstrap-subcommands-into-server into main
    bf8a0d6 feat(cli): fold bootstrap-infisical/clients/qa subcommands into mokosh-server (PMS-494)
    ac94aaa Merge pull request 'feat(auth): stamp profile_completed_at on Bunyip OIDC INSERT when both name claims arrive (MAPPS-329)' (#368) from feat/MAPPS-329-auto-populate-profile-from-bunyip into main
    1a2e641 feat(auth): stamp profile_completed_at on Bunyip OIDC INSERT when both name claims arrive (MAPPS-329)
    dc6a8ba feat(api): emit security response headers on every response (PMS-388)
    f3b1d12 ci(oci): switch build cache from type=registry/inline to type=gha
    7272270 Merge pull request 'test(e2e): un-fixme form-validation + auth-ui to verify AC7 on staging (PMS-519)' (#364) from test/PMS-519-unfixme-form-validation into main
    3444532 Merge pull request 'fix(e2e): handle the OIDC consent screen in loginViaSpa (PMS-521)' (#365) from fix/PMS-521-loginviaspa-consent into main
    bd21d6d test(e2e): target the visible desktop sidebar, not the hidden drawer (PMS-519)
    3d0320e test(e2e): drive form-validation via in-app nav, not hard goto (PMS-519)
    b222ff4 test(e2e): instrument form-validation + isolate from auth-ui (PMS-519)
    356a9ce Merge branch 'fix/PMS-521-loginviaspa-consent' into test/PMS-519-unfixme-form-validation
    25c1ea3 fix(e2e): handle the OIDC consent screen in loginViaSpa (PMS-521)
    44d7796 test(e2e): un-fixme form-validation + auth-ui login to verify on staging (PMS-519)
    37f3fa9 Merge pull request 'test(e2e): form-validation regression coverage (PMS-518 AC7)' (#363) from test/PMS-518-form-validation-e2e into main
    98378e6 test(e2e): fix form-validation spec quarantine + login frugality (review)
    729257f test(e2e): add form-validation regression coverage (PMS-518 AC7)
    bc51006 Merge pull request 'fix(timesheets): request full page in history range test (PMS-507)' (#361) from fix/PMS-507-timesheet-history-range into main
    b4403d2 Merge origin/main into fix/PMS-507-timesheet-history-range
    aab6a71 Merge pull request 'fix(tickets): honour contact_id filter on list_tickets (MAPPS-311)' (#362) from fix/MAPPS-311-tickets-contact-id-filter into main
    248c1b0 fix(tickets): honour contact_id filter on list_tickets (MAPPS-311)
    771ca07 Merge pull request 'feat(attachments): ticket-note attachment upload / download / delete (PMS-483)' (#358) from feat/PMS-483-note-attachments into main
    a7d2f6d fix(attachments): portal sub-router needs its own auth middleware layer (PMS-483)
    a5619c6 fix(test): portal login body must include tenant_slug (PMS-483)
    e9443dc Merge pull request 'feat(timesheets): status filter + multi-week range + decision audit on list (PMS-506)' (#360) from feat/PMS-506-timesheet-history into main
    43583f1 fix(timesheets): request full page in history range test (PMS-507)
    4ae0471 fix(test): seed contact uses is_portal_user, not portal_enabled (PMS-483)
    3227ab4 fix(test): timesheet_history fixture must spell out pagination (PMS-506)
    4451660 fix(test): seed distinctly-named companies in portal cross-company test (PMS-483)
    60b7fe5 feat(timesheets): status filter + multi-week range + decision audit on list (PMS-506)
    b99e0ba Merge pull request 'feat(approvals): change_requests + quotes parent tables (PMS-484)' (#357) from feat/PMS-484-approvals-parent-tables into main
    46f0ea9 Merge pull request 'feat(kb): top-ticket-driving-articles endpoint (PMS-485)' (#356) from feat/PMS-485-kb-top-driving into main
    5c0ad78 feat(attachments): ticket-note attachment upload / download / delete (PMS-483)
    d54fec4 feat(approvals): change_requests + quotes parent tables (PMS-484)
    79300a1 feat(kb): top-ticket-driving-articles endpoint (PMS-485)
    c500b82 Merge pull request 'docs(e2e): add dev-docs/e2e.md engineering reference' (#355) from docs/PMS-492-e2e-kb into main
    df31fd6 docs(e2e): add dev-docs/e2e.md engineering reference
    7d24578 Merge pull request 'docs: add postgres provisioning + RLS conversation transcription' (#354) from docs/postgres-security-transcription into main
    46f4358 docs: add postgres provisioning + RLS conversation transcription
    3713f71 Merge pull request 'chore(dev): move RLS bootstrap to postgres initdb.d, drop password-drift reset' (#353) from chore/PMS-488-postgres-initdb-rls into main
    c55ae98 chore(dev): move RLS bootstrap to postgres initdb.d, drop password-drift reset
    3bcd7db Merge pull request 'refactor: collapse CreateTicketRequest + RequireAdminUser + CurrentContact::tenant (PMS-479)' (#352) from feat/PMS-479-refactor-boilerplate into main
    13ca35d refactor: collapse CreateTicketRequest + RequireAdminUser + CurrentContact::tenant (PMS-479)
    81bbfb6 Merge pull request 'feat(dashboards): scheduled dashboard delivery worker (PMS-471)' (#349) from feat/PMS-471-scheduled-dashboards into main
    bbebe35 Merge pull request 'feat(approvals): polymorphic approval surface across tickets + time-entries (PMS-470)' (#351) from feat/PMS-470-polymorphic-approvals into main
    607979b Merge pull request 'feat(notes): expose created_by_contact_id on note DTO + contact-notes feed (PMS-468)' (#350) from feat/PMS-468-portal-notes-attachments into main
    94f4d1e feat(approvals): polymorphic approval surface across tickets + time-entries (PMS-470)
    371c12a feat(dashboards): scheduled dashboard delivery worker (PMS-471)
    9a48f58 feat(notes): expose created_by_contact_id on note DTO + contact-notes feed (PMS-468)
    2ceb931 Merge pull request 'feat(workflows): mutating actions on transition triggers + per-tenant cycle cap (PMS-467)' (#345) from feat/PMS-467-workflow-mutating-transitions into main
    74e08a0 feat(workflows): mutating actions on transition triggers + per-tenant cycle cap (PMS-467)
    f89a41b Merge pull request 'feat(reports): scheduled report delivery worker (PMS-478)' (#348) from feat/PMS-478-scheduled-reports into main
    c14ca8e Merge pull request 'feat(assets): CI impact-graph traversal endpoint (PMS-475)' (#347) from feat/PMS-475-ci-impact-traversal into main
    1cbc846 feat(reports): scheduled report delivery worker (PMS-478)
    5cc093e feat(assets): CI impact-graph traversal endpoint (PMS-475)
    41fbfef Merge pull request 'feat(email-intake): auto-create contact + reply-as-comment + intake-log audit (PMS-469)' (#346) from feat/PMS-469-email-intake-follow-ups into main
    660e4d7 feat(email-intake): auto-create contact + reply-as-comment + intake-log audit (PMS-469)
    7a9b4da Merge pull request 'feat(reports): saved-report execution runtime - tickets, equality+IN, whitelisted columns (PMS-477)' (#344) from feat/PMS-477-report-execution into main
    b81787a feat(reports): saved-report execution runtime - tickets, equality+IN, whitelisted columns (PMS-477)
    d32fd2a Merge pull request 'feat(workflows): ticket.status_changed + ticket.priority_changed triggers (PMS-448 phase 2)' (#343) from feat/PMS-448-phase2-more-triggers into main
    0638a98 Merge pull request 'feat(email-intake): admin CRUD for tenant intake tokens (PMS-450 phase 2)' (#342) from feat/PMS-450-phase2-token-admin into main
    5b0dcb9 Merge pull request 'feat(workflows): ticket.created workflow rules with in-line executor (PMS-448 phase 1)' (#341) from feat/PMS-448-ticket-created-rules into main
    9d28927 feat(workflows): ticket.status_changed + ticket.priority_changed triggers (PMS-448 phase 2)
    0b55f25 feat(email-intake): admin CRUD for tenant intake tokens (PMS-450 phase 2)
    8b499a6 Merge pull request 'feat(approvals): per-ticket approval requests (PMS-451 phase 1)' (#334) from feat/PMS-451-ticket-approvals into main
    5f2f6bf Merge pull request 'feat(portal): customer ticket comments (PMS-449 phase 1)' (#339) from feat/PMS-449-portal-ticket-comments into main
    1a17f43 fix(approvals): use axum 0.8 brace path-capture syntax (PMS-451)
    eb6a6eb feat(approvals): per-ticket approval requests (PMS-451 phase 1)
    7f28f34 feat(workflows): ticket.created workflow rules with in-line executor (PMS-448 phase 1)
    746efc2 Merge pull request 'feat(reports): saved custom-report definitions (PMS-457 phase 1)' (#340) from feat/PMS-457-saved-reports into main
    6706fa6 feat(reports): saved custom-report definitions (PMS-457 phase 1)
    5811441 Merge pull request 'feat(cmdb): ITIL CI classification on asset types and lifecycle on assets (PMS-456)' (#336) from feat/PMS-456-itil-ci-types into main
    642f84c Merge pull request 'feat(email-intake): tenant-scoped email-to-ticket POST endpoint (PMS-450 phase 1)' (#337) from feat/PMS-450-email-intake into main
    f655561 feat(portal): customer ticket comments (PMS-449 phase 1)
    5071ad9 feat(email-intake): tenant-scoped email-to-ticket POST endpoint (PMS-450 phase 1)
    9154572 Merge pull request 'feat(tickets): source KB article on ticket creation (PMS-452 phase 1)' (#338) from feat/PMS-452-kb-source-ticket into main
    2b9f90c feat(tickets): source KB article on ticket creation (PMS-452 phase 1)
    d25ebca feat(cmdb): ITIL CI classification on asset types and lifecycle on assets (PMS-456)
    17f9154 Merge pull request 'feat/PMS-453-saved-dashboards' (#335) from feat/PMS-453-saved-dashboards into main
    99fb6af fix(dashboards): use axum 0.8 brace path-capture syntax (PMS-453)
    8959ddf feat(dashboards): per-user saved dashboards CRUD (PMS-453 phase 1)
    6691ce8 feat(assets): CMDB expansion - assigned user, IP, hostname, MAC, installed date, department, in-transit ticket (PMS-454)
    bcaa2a8 Merge pull request 'feat(audit): tenant-scoped CSV export endpoint (PMS-455)' (#333) from feat/PMS-455-audit-log-csv-export into main
    987b273 Merge pull request 'feat(search): cross-entity tenant-scoped /api/v1/search endpoint (MAPPS-298)' (#332) from feat/MAPPS-298-global-search-endpoint into main
    a6f764e feat(audit): tenant-scoped CSV export endpoint (PMS-455)
    7c04a3b feat(search): cross-entity tenant-scoped /api/v1/search endpoint (MAPPS-298)
    0aa200b Merge pull request 'fix(auth): preserve invite role over the Bunyip subscriber floor' (#324) from fix/PMS-458-preserve-invite-role-over-subscriber-floor into main
    0843268 Merge origin/main into fix/PMS-458-preserve-invite-role-over-subscriber-floor
    b6ce330 Merge pull request 'test(contacts): isolate duplicate-name assertion from demo seed' (#331) from fix/PMS-464-company-dup-test-demo-seed into main
    22ad97f Merge branch 'main' into fix/PMS-464-company-dup-test-demo-seed
    ea44c80 Merge pull request 'ci(e2e): run npm/npx via corepack, not the absent npm24' (#330) from fix/PMS-462-e2e-corepack-npm into main
    5afc32b test(contacts): isolate duplicate-name assertion from demo seed
    8258de5 ci(e2e): run npm/npx via corepack, not the absent npm24
    7f258aa Merge branch 'main' into fix/PMS-429-e2e-versioned-node-binaries
    ff4c49a Merge pull request 'chore: Claude fix is now Claude Run' (#329) from chore/remove-claude-fix into main
    8710667 chore: Claude fix is now Claude Run
    04c86a9 Merge pull request 'chore: Sync the checks and justfile recipes' (#328) from chore/sync-checks-justfile into main
    0f2909f Merge branch 'main' into chore/sync-checks-justfile
    ecf3745 chore: Sync the checks and justfile recipes
    3718d2f Merge pull request 'feat(jit): consume bunyip given_name/family_name claims (BUNYIP-141)' (#327) from feat/bunyip-141-jit-consume-name-claims into main
    4ebd699 Merge pull request 'fix(ci): create-release workflow concludes success on non-release merges' (#326) from fix/ci-create-release-skip-conclusion into main
    4d03d85 fix(ci): create-release workflow concludes success on non-release merges
    a31a5b7 Merge pull request 'fix(auth): gate PMS-447 subscriber-Admin floor on invite absence' (#325) from fix/PMS-447-preserve-invite-role into main
    1e0efe3 fix(auth): gate PMS-447 subscriber-Admin floor on invite absence
    83bf97e fix(auth): preserve invite role over the Bunyip subscriber floor
    ecaf31e Merge pull request 'feat(auth): single-tenancy admin floor on Bunyip RS path (PMS-447)' (#323) from feat/PMS-447-single-tenancy-admin-default into main
    8802de2 feat(auth): single-tenancy admin floor on Bunyip RS path (PMS-447)
    7a21cc2 Merge branch 'main' into fix/PMS-429-e2e-versioned-node-binaries
    c58b00d Merge pull request 'test(e2e): quarantine OIDC token-flow spec blocked on bunyip BUNYIP-146' (#319) from fix/PMS-435-quarantine-oidc-token-flow-bunyip-146 into main
    c684ed6 Merge remote-tracking branch 'origin/main' into fix/PMS-435-quarantine-oidc-token-flow-bunyip-146
    1f57c76 Merge pull request 'test(e2e): surface authorize redirect target in OIDC token-flow (PMS-443)' (#322) from chore/pms-e2e-oidc-surface-redirect-target into main
    81c162b test(e2e): surface authorize redirect target in OIDC token-flow
    e3bf9b6 Merge pull request 'fix(rls): make tenant-isolation GUC count symmetric with owner count' (#320) from fix/PMS-437-rls-isolation-internal-count-symmetry into main
    6103ab7 fix(rls): make tenant-isolation GUC count symmetric with owner count
    1a2b2dd test(e2e): quarantine OIDC token-flow spec blocked on bunyip BUNYIP-146
    87e6fb8 Merge pull request 'chore(e2e): log OP-session cookie name to diagnose OIDC authorize bounce (PMS-434)' (#318) from chore/pms-e2e-oidc-cookie-name-diagnostic into main
    0962f69 chore(e2e): log OP-session cookie name to diagnose OIDC authorize bounce
    ec767bc ci(e2e): invoke versioned node24/npm24/npx24 binaries
    6b2b286 ci(e2e): use runner's preinstalled nodejs24 instead of setup-node
    74602d6 Merge pull request 'test(rls): make tenant-isolation count robust to own-company seed' (#310) from fix/PMS-414-rls-isolation-test-own-company-seed into main
    9625d65 Merge branch 'main' into fix/PMS-414-rls-isolation-test-own-company-seed
    dafee52 Merge pull request 'feat(ci): cap native check.yml parallelism at nproc/2' (#313) from feat/GOV-10-native-check-nproc-cap into main
    3f7e38d feat(ci): cap native check.yml + integration.yml parallelism at nproc/2
    3616ce4 feat(jit): consume bunyip given_name/family_name claims (BUNYIP-141)
    4271f34 Merge pull request 'fix(migrations): exempt internal own-company from duplicate-name unique index (staging hotfix, PMS-413)' (#311) from fix/PMS-413-own-company-name-unique-collision into main
    31132b3 fix(migrations): exempt internal own-company from the duplicate-name unique index (PMS-413)
    f84b10c Merge branch 'main' into fix/PMS-414-rls-isolation-test-own-company-seed
    d7f575a Merge pull request 'feat(tenants): tenant own-company for general time logging (PMS-413)' (#309) from feat/PMS-413-tenant-own-company into main
    45ecbb7 style: match CI rustfmt for the PMS-413 emptiness-check edits (PMS-413)
    edbd8cd fix(seed): exclude internal own-company from tenant emptiness checks (PMS-413)
    1cce469 test(rls): make tenant-isolation count robust to own-company seed
    b3e1632 fix(reports): exclude internal own-company from clients report; match CI rustfmt (PMS-413)
    cf0a808 feat(tenants): add tenant own-company for general time logging
    42744d5 Merge pull request 'Seed pre-commit .env from committed .env.example' (#302) from fix/pre-commit-clean-clone into main
    ff6111b Merge branch 'main' into fix/pre-commit-clean-clone
    73692c6 Merge pull request 'feat(contracts): add weekly and bi-weekly billing cycles (PMS-404)' (#308) from feat/PMS-404-weekly-biweekly-billing-cycles into main
    4a1826f Merge pull request 'fix(time-tracking): consume contract hours on timesheet approval (PMS-405)' (#307) from fix/PMS-405-wire-time-to-contract-balance into main
    775efea Merge pull request 'feat(projects): reject task/phase create on cancelled project (PMS-399)' (#305) from fix/PMS-399-reject-children-on-cancelled-project into main
    af44724 style: match CI rustfmt in PMS-404 contracts tests (PMS-404)
    ea42a18 style: match CI rustfmt in contracts tests (PMS-405)
    224c2b7 style: collapse short query to one line for CI rustfmt (PMS-399)
    bc71b9c feat(projects): reject task/phase create on cancelled project (PMS-399)
    7f0cb05 feat(contracts): add weekly and bi_weekly billing cycles
    ff194ee Merge pull request 'feat(contacts): freeform company on contacts (nullable company_id + company_name) (PMS-402)' (#306) from feat/PMS-402-freeform-company-on-contacts into main
    9926b2e fix(time): consume contract hours on timesheet approval
    4c29f2e feat(contacts): accept freeform company on contacts
    7d74781 Merge pull request 'fix(projects): reject inverted task start/due dates (PMS-398)' (#304) from fix/PMS-398-task-date-ordering into main
    7828b27 fix(projects): reject inverted task start/due dates (PMS-398)
    712cb34 Merge pull request 'feat(time-tracking): enforce per-tenant max-hours-per-day cap' (#298) from feat/PMS-396-max-hours-per-day-cap into main
    9993537 fix(test): set work_category on day-cap test helpers after rebase
    e0ecc09 Merge pull request 'test(tickets): pin server-side inline status/priority/assignee edit + SLA recalc' (#272) from test/pms-359-ticket-inline-edits into main
    2503606 test(tickets): pin server-side inline status/priority/assignee edit + SLA recalc
    cc2b1de Merge pull request 'feat(projects): per-project standard due date override (PMS-345)' (#268) from feat/PMS-345-per-project-default-due into main
    15abfaf Merge pull request 'feat(calendar): add scheduling-template model and CRUD endpoints (PMS-403)' (#299) from feat/PMS-403-scheduling-templates into main
    4653c86 Merge pull request 'feat(tickets): wire team_id filter and team-scoped dashboard query' (#295) from feat/PMS-406-ticket-team-scope-filter into main
    4c0296b Merge pull request 'feat(contacts): reject duplicate company names per tenant' (#296) from feat/PMS-400-reject-duplicate-company-names into main
    69e707a Merge pull request 'test(modules): verify company-scoped list endpoint filtering' (#300) from test/PMS-401-company-scoped-list-filtering into main
    0315de4 fix(seed): set default_due_business_days on QA project specs after rebase
    e0636e2 chore(migrations): renumber project_default_due_business_days 051->059 after PMS-411 collision fix
    e8d995c feat(projects): per-project standard due date override (PMS-345)
    43cf626 feat(time-tracking): enforce per-tenant max-hours-per-day cap
    90e3717 chore(migrations): renumber tickets_tenant_team_idx to 058 after PMS-411 collision fix
    11fafc0 chore(migrations): renumber companies_tenant_name_unique to 057 after PMS-411 collision fix
    73ef7b0 feat(tickets): wire team_id filter and team-scoped dashboard query
    4e5ebbd feat(contacts): reject duplicate company names per tenant
    335773e chore(migrations): renumber scheduling_templates 054->056 after PMS-411 collision fix
    c8114e3 feat(calendar): add scheduling-template model and CRUD endpoints (PMS-403)
    aa720ff Merge pull request 'fix(migrations): renumber duplicate 054 prefix to 055 (PMS-411)' (#303) from fix/PMS-migration-054-prefix-collision into main
    57aefe6 fix(migrations): renumber duplicate 054 prefix to 055 (PMS-411)
    3af2a24 fix(hooks): seed .env from committed .env.example on clean clone
    dfa58e9 Merge pull request 'style(time-tracking): rustfmt the work_category unit tests' (#297) from feat/PMS-394-time-entry-general-category into main
    07abad8 Merge pull request 'feat(auth): persist per-user theme preference (base mode + accent) (PMS-410)' (#301) from fix/PMS-410-theme-prefs into main
    c355277 feat(auth): persist per-user theme preference (base mode + accent) (PMS-410)
    9d6b083 test(modules): verify company-scoped list endpoint filtering
    1e82ded style(time-tracking): rustfmt the work_category unit tests
    9c87f15 feat(time-tracking): add general-bucket work_category to time entries
    a5b45ba Merge pull request 'fix(validation): range/scale-guard money and hours numeric fields (PMS-383)' (#294) from fix/PMS-383-numeric-money-hours-validation into main
    80a0333 Merge branch 'main' into fix/PMS-383-numeric-money-hours-validation
    d372b9b Merge pull request 'feat(notifications): add PUT update endpoints for channels/templates/rules' (#293) from feat/PMS-386-notification-config-update-endpoints into main
    f5756b2 Merge branch 'main' into feat/PMS-386-notification-config-update-endpoints
    91a42cd Merge pull request 'feat(rmm): add PUT update routes for device-mappings and alert-rules' (#292) from feat/PMS-387-rmm-device-mapping-alert-rule-update into main
    42acff8 Merge branch 'main' into feat/PMS-387-rmm-device-mapping-alert-rule-update
    d08de8f Merge pull request 'fix(reports): bucket dashboard ticket trend in the user's timezone' (#274) from fix/pms-360-user-timezone-date-bucketing into main
    c3814e3 Merge branch 'main' into fix/pms-360-user-timezone-date-bucketing
    6eef45e fix(validation): range/scale-guard money and hours numeric fields (PMS-383)
    cad860a feat(notifications): add PUT update endpoints for channels/templates/rules
    ada540c feat(rmm): add PUT update routes for device-mappings and alert-rules
    9d61aa5 Merge pull request 'fix(validation): validate tenant admin_email as an email (PMS-385)' (#290) from fix/PMS-385-tenant-admin-email-validation into main
    c86c58d Merge branch 'main' into fix/PMS-385-tenant-admin-email-validation
    fc42816 Merge pull request 'fix(validation): reject non-positive time-entry duration_minutes (PMS-384)' (#291) from fix/PMS-384-time-entry-duration-validation into main
    f7366bf Merge branch 'main' into fix/PMS-384-time-entry-duration-validation
    a432921 Merge pull request 'fix(cors): cache preflight with Access-Control-Max-Age=600 (PMS-389)' (#289) from fix/PMS-389-cors-max-age into main
    87598da fix(validation): reject non-positive time-entry duration_minutes
    5917e76 fix(validation): validate tenant admin_email as an email
    f200bd3 fix(cors): cache preflight with Access-Control-Max-Age=600
    50ea5cc Merge pull request 'fix(seed): repair qa seed so main compiles (actual_end_date + AuditCtx) (PMS-379)' (#288) from fix/qa-seed-build-actual-end-date-audit-ctx into main
    2bc8995 fix(migrations): renumber duplicate 051 prefixes to 052/053
    5869e1f fix(seed): repair qa seed for actual_end_date + AuditCtx create signatures
    0b3dd22 Merge main into fix/pms-360-user-timezone-date-bucketing
    d6d16df Merge pull request 'test(projects): pin oversized-budget 422 and company-id injection safety' (#283) from fix/pms-316-project-budget-oversize-and-safe-company-tests into main
    0a4a1b0 Merge pull request 'fix(validation): recurse nested ValidationErrors so 422 errors[] carry dotted field paths' (#280) from fix/pms-330-nested-validation-field-paths into main
    13ddfab Merge pull request 'test(contacts): pin company search to substring ILIKE, not leading prefix' (#279) from fix/pms-372-company-search-substring into main
    a9b0c7e Merge pull request 'fix(contacts): populate company_name via LEFT JOIN to companies' (#266) from fix/pms-334-contact-company-name into main
    5799c34 Merge pull request 'fix(billing): widen tax_rates.rate and validate the percentage range' (#281) from fix/pms-339-tax-rate-column-width-and-validation into main
    20307fb Merge pull request 'feat(projects): accept actual_end_date on create for edit-form parity' (#278) from feat/pms-361-create-project-symmetry into main
    ae60fa7 Merge pull request 'feat(projects): surface owning company name in ProjectResponse' (#276) from feat/pms-335-project-company-name into main
    7a3f773 Merge pull request 'fix(assets): surface company_name on AssetResponse via LEFT JOIN' (#275) from fix/pms-336-asset-company-name into main
    dfa96c8 Merge pull request 'fix(billing): reject non-positive payment amounts with a 422' (#269) from fix/PMS-373-reject-non-positive-payment-amount into main
    62433cb Merge pull request 'fix(tickets): humanise FK column names in change-history field labels' (#267) from fix/pms-370-humanize-history-field-labels into main
    8c74d8b Merge pull request 'feat(seed): on-demand QA walkthrough dataset for a QA-marked tenant' (#265) from feat/PMS-331-qa-seed-dataset into main
    d24677e Merge pull request 'fix(billing): make payment-gateway credential a write-only secret' (#263) from fix/pms-342-payment-gateway-write-only-secret into main
    6730ed2 Merge pull request 'feat(knowledge_base): wire client_specific company scoping write path' (#260) from feat/pms-341-kb-company-ids into main
    077fce3 Merge pull request 'test(tickets): pin priority round-trip on ticket create (PMS-358)' (#270) from feat/pms-358-priority-roundtrip-integration-test into main
    45c0c07 Merge pull request 'fix(validation): symmetric URL error message + close credential URL gap (PMS-351)' (#273) from fix/pms-351-url-field-validation-sweep into main
    3504d3c Merge pull request 'fix(rbac): gate Contracts/Rate Cards reads and financial reports like Invoices' (#271) from fix/PMS-350-rbac-finance-gates into main
    d12dbba fix(rbac): gate Client Profitability report + its CSV export like Invoices
    3b7c2bd Merge pull request 'feat(audit): emit Create audit row on mileage entry creation' (#284) from feat/pms-315-mileage-create-audit into main
    5f0f3e1 feat(audit): emit Create audit row on mileage entry creation
    b8ea044 Merge pull request 'fix(projects): emit Create audit row on task creation' (#282) from fix/pms-318-create-task-audit-row into main
    2a173e4 feat(audit): emit Create audit rows for every remaining create path
    1bb9439 Merge pull request 'feat(mileage): mileage entries, per-mile rate card rate, invoice line type' (#277) from feat/pms-315-mileage-entries into main
    0cd6088 fix(mileage,time): validate re-associated ticket_id on update against tenant
    3fd861e Merge pull request 'fix(calendar): validate recurrence_rule as RFC 5545 on appointment create' (#264) from fix/pms-374-validate-recurrence-rule into main
    4ad95d0 Merge pull request 'fix(reports): zero-fill canonical project and asset status buckets' (#261) from fix/pms-366-project-status-counters-zero-fill into main
    7b2370d test(projects): pin oversized-budget 422 and company-id injection safety
    649c820 fix(projects): emit Create audit row on task creation
    e70560c fix(billing): widen tax_rates.rate and validate the percentage range
    29c12e3 fix(validation): recurse nested ValidationErrors so 422 errors[] carry dotted field paths
    6a16cf2 test(contacts): pin company search to substring ILIKE, not leading prefix
    5ac383a feat(projects): accept actual_end_date on create for edit-form parity
    ee415e8 feat(mileage): mileage entries, per-mile rate card rate, invoice line type
    43102d3 feat(projects): surface owning company name in ProjectResponse
    a3c9bb3 fix(assets): surface company_name on AssetResponse via LEFT JOIN
    e56be90 fix(reports): bucket dashboard ticket trend in the user's timezone
    8689abb fix(validation): symmetric URL error message + close credential URL gap (PMS-351)
    a12a3b8 Merge pull request 'fix(validation): re-key cross-field rules onto a named field so forms show inline messages' (#262) from fix/pms-364-cross-field-validation-field-key into main
    73cc9af fix(rbac): gate Contracts/Rate Cards reads and financial reports like Invoices
    075555d test(tickets): pin priority round-trip on ticket create (PMS-358)
    ca16ea9 fix(billing): reject non-positive payment amounts with a 422
    a7565fe fix(tickets): humanise FK column names in change-history field labels
    27ddca3 fix(contacts): populate company_name via LEFT JOIN to companies
    553a995 feat(seed): on-demand QA walkthrough dataset for a QA-marked tenant
    7e04bbc fix(calendar): validate recurrence_rule as RFC 5545 on appointment create
    8d31b36 fix(billing): make payment-gateway credential a write-only secret
    064c8df fix(validation): re-key cross-field rules onto a named field so forms show inline messages
    13b7252 fix(reports): zero-fill canonical project and asset status buckets
    82a6912 feat(knowledge_base): wire client_specific company scoping write path
    1cb2ab1 Merge pull request 'fix(tickets,assets): asset_id update + AssetFilter.q so PMS-344 inline editor and picker work end to end' (#259) from fix/pms-344-asset-filter-and-update-clearing-semantics into main
    fb4e7dc fix(tickets,assets): asset_id update + AssetFilter.q so PMS-344 inline editor and picker work end to end
    1e8b387 Merge pull request 'feat(tickets): TicketResponse carries asset_id + asset_name; TicketFilter accepts asset_id (PMS-344)' (#258) from feat/pms-344-server-asset-id-filter-and-name into main
    3a35f97 feat(tickets): TicketResponse carries asset_id + asset_name; TicketFilter accepts asset_id (PMS-344)
    c9f84e0 Merge pull request 'feat(scheduling): tenant-wide standard due date for tasks and no-SLA tickets' (#257) from feat/PMS-345-standard-due-date into main
    12f9bb7 feat(scheduling): tenant-wide standard due date for tasks and no-SLA tickets
    c3fa665 Merge pull request 'fix(audit): skip change-history entry for description checkbox toggles (PMS-349)' (#256) from fix/PMS-349-skip-audit-for-checkbox-toggles into main
    d793cba fix(audit): don't record a change-history entry for description checkbox toggles (PMS-349)
    0b6287d Merge pull request 'fix(sla): validate SLA target hours (non-negative, <=2dp, first_response<=resolution) (PMS-338)' (#255) from fix/PMS-338-sla-target-validation into main
    87494f2 Merge remote-tracking branch 'origin/main' into fix/PMS-338-sla-target-validation
    2957572 Merge pull request 'fix(contracts): validate billing_cycle and status at the request layer (422 not 500) (PMS-337)' (#254) from fix/PMS-337-contract-status-billing-validation into main
    1cf5505 fix(sla): validate SLA target hours (non-negative, <=2dp, first_response<=resolution)
    6d378c9 fix(contracts): validate billing_cycle and status at the request layer (422 not 500)
    c1ed191 Merge pull request 'fix(calendar): reject inverted appointment time range on update (PMS-343)' (#253) from fix/PMS-343-appointment-time-range into main
    d3be695 fix(calendar): reject inverted appointment time range on update (PMS-343)
    9f1f092 Merge pull request 'feat(billing): payment_terms lookup table + CRUD, invoices reference it by FK' (#252) from feat/PMS-333-payment-terms-lookup into main
    8e9d65b feat(billing): payment_terms lookup table + CRUD, invoices reference it by FK
    6de032a Merge pull request 'fix(dev): pre-create pg extensions + grant CREATE-on-db for fresh split-role volume' (#251) from fix/dev-pg-preinstall-extensions into main
    32d1b38 fix(dev): pre-create pg extensions + grant CREATE-on-db for fresh split-role volume
    e866ed1 Merge pull request 'feat(time): join work-item names (ticket/project/task) into the time-entry response' (#250) from feat/PMS-332-time-entry-work-item-names into main
    1e22e9c feat(time): join work-item names into the time-entry response
    6982cad Merge pull request 'feat(projects): expose per-task logged_hours (non-rejected) alongside approved actual_hours' (#249) from feat/PMS-329-task-logged-hours into main
    ecd62b4 feat(projects): expose per-task logged_hours alongside approved actual_hours
    b9769f2 Merge pull request 'fix(time): PUT /time-entries preserves task_id (and expose it in the response)' (#248) from fix/PMS-328-time-entry-preserve-task-id into main
    319b96e fix(time): restrict time-entry edit/delete to owner or admin
    106c298 fix(time): preserve task_id on time-entry update and expose it
    828afbf Merge pull request 'docs(qa): extend QA test-plan prompt with this round's validation findings [PMS-326]' (#246) from docs/PMS-326-qa-test-plan into main
    e2c60ac Merge pull request 'feat(contacts): validate phone/timezone/country/postal (fix contact phone 500) [PMS-325]' (#247) from fix/PMS-325-contact-field-validation into main
    90cefed feat(contacts): validate phone, timezone, country, and postal fields
    b13bfbd docs(qa): fold validation findings into the QA test-plan prompt
    768412d Merge pull request 'fix(projects): server-side validation for name + budget (fix Budget Hours 422) [PMS-324]' (#243) from fix/PMS-324-project-input-validation into main
    457be7f Merge branch 'main' into fix/PMS-324-project-input-validation
    0c46c90 Merge pull request 'feat(contracts): add GET /rate-cards/{id} for single rate-card fetch' (#244) from feat/PMS-327-get-rate-card into main
    2807521 feat(contracts): add GET /rate-cards/{id} for single rate-card fetch
    39de4e6 Merge pull request 'harden(lookups): DB partial unique index for single-default per tenant' (#242) from feat/PMS-323-single-default-unique-index into main
    0953a74 fix(projects): validate name length and budget fields, accept numeric budgets
    fd15023 harden(lookups): DB partial unique index for single-default per tenant
    1e649e9 Merge pull request 'test(projects): pin duplicate project-type name as a 409' (#241) from feat/PMS-322-project-type-lookup into main
    9bdca67 test(projects): pin duplicate project-type name as a 409
    aa04df8 Merge pull request 'feat(projects): tenant-scoped project_types lookup table + CRUD' (#240) from feat/PMS-322-project-type-lookup into main
    77625ca feat(projects): tenant-scoped project_types lookup + CRUD
    c4dabdc Merge pull request 'feat(tickets): management CRUD for ticket status/priority/type/queue/category lookups' (#239) from feat/PMS-321-ticket-lookup-crud into main
    d6c7f17 fix(tickets): enforce same-tenant category parent at the DB layer (migration 047)
    e7ec4a1 fix(tickets): reject transitive category cycles; cover priority sla_multiplier
    06bbe5c feat(tickets): management CRUD for status/priority/type/queue/category lookups
    8a8253f Merge pull request 'fix(justfile): add ensure-env dependency to dev-clean' (#238) from fix/PMS-307-dev-clean-ensure-env into main
    7438c7f fix(justfile): add ensure-env dependency to dev-clean

    Downloads
  • v0.3.0 2c59a0fddb

    v0.3.0
    All checks were successful
    Build OCI container / Build and push mokosh-api image (push) Successful in 4m21s
    Stable

    David released this 2026-06-14 19:00:55 +02:00 | 676 commits to main since this release

    Changelog

    e070df6 Merge pull request 'Release v0.3.0' (#237) from release/v0.3.0 into main
    0ed8a38 Release v0.3.0
    008e467 Merge pull request 'fix(validation): reject inverted date ranges and oversized payment amounts' (#236) from fix/PMS-306-validation-date-range-money-overflow into main
    8b5b99f fix(validation): reject inverted date ranges and oversized payment amounts
    5d9b8b7 Merge pull request 'fix(migrations): renumber duplicate 033 profile_completed_at migration to 046' (#235) from chore/checks-workflow-048ddb98 into main
    c2cf8c2 fix(migrations): renumber duplicate 033 profile_completed_at migration to 046
    048ddb9 Merge pull request 'feat(auth): force new Bunyip-JIT users through name onboarding' (#151) from feat/force-profile-setup-on-jit into main
    5faff29 Merge branch 'main' into feat/force-profile-setup-on-jit
    56be716 Merge pull request 'feat(audit-log): resolve user + entity names server-side so the SPA can display them' (#152) from feat/audit-log-resolve-names into main
    b4c7f6e Merge branch 'main' into feat/audit-log-resolve-names
    a603d3e Merge pull request 'docs(qa): add reusable AI-driven QA test plan' (#234) from docs/qa-test-plan into main
    86e899c Merge branch 'main' into docs/qa-test-plan
    6bfb215 Merge pull request 'fix(contracts): reject out-of-set contract_type with 422 instead of 500' (#233) from fix/pms-299-contract-type-validation into main
    187eb94 Merge branch 'main' into fix/pms-299-contract-type-validation
    a2f3bf7 Merge pull request 'fix(contacts): validate company create/update input to 422 not 500' (#232) from fix/pms-297-company-create-validation into main
    19a5750 Merge branch 'main' into fix/pms-297-company-create-validation
    d8264a5 docs(qa): add reusable AI-driven QA test plan
    a7e5bad Merge pull request 'fix(error): unify 4xx envelope and populate validation messages' (#231) from fix/pms-298-error-envelope into main
    8b0625d fix(contracts): reject out-of-set contract_type with 422 instead of 500
    fe9cf7a fix(contacts): validate company create/update input to 422 not 500
    8694729 fix(error): unify 4xx envelope and populate validation messages
    6dac348 Merge pull request 'feat(ci): split check into fast and Postgres-backed integration gates' (#230) from feat/pms-267-split-fast-integration-ci into main
    3d75880 Merge branch 'main' into feat/pms-267-split-fast-integration-ci
    615adad feat(ci): split check into fast and Postgres-backed integration gates
    009ef0c Merge pull request 'docs(auth): clarify SSO startup log + record PMS-291 posture decision' (#229) from fix/pms-291-sso-log-and-docs into main
    6f4aaf6 docs(auth): clarify SSO startup log + record PMS-291 posture decision
    da5b705 Merge pull request 'Release v0.2.0' (#228) from release/v0.2.0 into main
    cbc6b03 Release v0.2.0
    00084a0 Merge pull request 'fix(assets): wire configured AES key and harden vault crypto integrity' (#226) from fix/pms-188-assets-zero-key-crypto-integrity into main
    93df0f9 Merge origin/main into fix/pms-188-assets-zero-key-crypto-integrity
    92a4975 Merge origin/main into fix/pms-188-assets-zero-key-crypto-integrity
    3a4327a Merge pull request 'feat(rls): activate RLS at runtime via split migrator/app DB roles' (#224) from feat/pms-285-rls-runtime-role-split into main
    714658e Merge origin/main into feat/pms-285-rls-runtime-role-split
    55000e0 Merge pull request 'chore(cleanup): remove dead crates code and stray config/seed/test cruft' (#220) from chore/pms-202-remove-dead-code into main
    1f4884e Merge origin/main into chore/pms-202-remove-dead-code
    e1e04e2 Merge pull request 'chore(cleanup): remove dead and stray code from src/ tree' (#218) from chore/pms-201-remove-dead-stray-code into main
    5755852 Merge origin/main into chore/pms-201-remove-dead-stray-code
    69bdf0a Merge pull request 'fix: correct rmm, notifications, SLA, KB, portal, and contacts defects' (#217) from fix/pms-195-module-correctness-defects into main
    0c7f45d Merge origin/main into fix/pms-195-module-correctness-defects
    02c6300 Merge pull request 'test(settings): hit canonical /api/v1/invoices in module-gate tests' (#215) from chore/checks-workflow-986a08ef into main
    258450c Merge origin/main into chore/checks-workflow-986a08ef
    2e34ede Merge pull request 'fix(contracts,billing): close financial-integrity gaps' (#213) from fix/pms-194-contracts-billing-financial-integrity into main
    6d07954 Merge origin/main into fix/pms-194-contracts-billing-financial-integrity
    bad33e0 Merge pull request 'refactor: consolidate duplicated helpers, types, and test fixtures' (#212) from feat/pms-199-consolidate-duplicated-helpers into main
    c00cae2 Merge origin/main into feat/pms-199-consolidate-duplicated-helpers
    eac8dbb Merge pull request 'fix(db): converge ticket filter builder; audit count_query placeholder family' (#211) from feat/pms-197-audit-count-query-placeholder-family into main
    4a7c72e Merge origin/main into feat/pms-197-audit-count-query-placeholder-family
    06bddef Merge pull request 'fix(schema): harden migrations, FKs, seed, and truncation (PMS-196)' (#210) from feat/pms-196-harden-migrations-rls-integrity into main
    ad47e38 Merge origin/main into feat/pms-196-harden-migrations-rls-integrity
    85fd769 Merge pull request 'fix(tickets): scope writes to tenant and validate FK references' (#209) from fix/pms-193-scope-tickets-writes-validate-fk into main
    3154fba ci: re-trigger Check (transient failure; green locally)
    bde8f81 Merge origin/main into fix/pms-193-scope-tickets-writes-validate-fk
    27fa80b Merge pull request 'fix(misc): correctness, concurrency, and test-reliability defects' (#208) from fix/pms-200-misc-correctness-concurrency-test-reliability into main
    7caf796 Merge origin/main into fix/pms-200-misc-correctness-concurrency-test-reliability
    a49cfbe Merge pull request 'fix(db,auth): dedupe migration prefix 041 and fix main compile' (#227) from fix/pms-293-dedupe-migration-prefix-041 into main
    2c9c7c3 fix(auth): return user: None in login_with_google mfa_required branch
    820cc4d fix(db): renumber portal_setup_tokens migration 041 to 042
    893f5d8 fix(auth): omit user profile in Google-callback mfa_required branch
    5b96470 Merge origin/main into fix/pms-200-misc-correctness-concurrency-test-reliability
    5946001 Merge pull request 'feat(portal): mint setup-link token + email on portal-access grant' (#204) from feat/pms-136-portal-setup-link into main
    c1b9663 Merge origin/main into feat/pms-136-portal-setup-link
    66367d5 Merge pull request 'fix(auth): close invite, signup, password, and validation defects' (#203) from fix/pms-192-invite-signup-password-validation into main
    52b32c5 Merge origin/main into fix/pms-192-invite-signup-password-validation
    8829844 Merge pull request 'fix(auth): close SSO/OIDC auth-strength and conformance gaps' (#202) from feat/pms-191-sso-oidc-security-gaps into main
    fbacbde Merge origin/main into feat/pms-191-sso-oidc-security-gaps
    3337ef3 Merge pull request 'fix(auth): close legacy PSA auth path security gaps' (#201) from fix/pms-190-legacy-auth-security-gaps into main
    7dd8b6a Merge origin/main into fix/pms-190-legacy-auth-security-gaps
    7e5fa28 fix(assets): allow 'deleted' in audit-log CHECK; add delete regression test
    269bf16 Merge pull request 'test(settings): fix stale billing route path in module-gating tests' (#225) from fix/pms-290-settings-stale-billing-path into main
    4c89552 fix(assets): wire configured AES key and harden vault crypto integrity
    a8589c0 test(settings): fix stale billing route path in module-gating tests
    f67057c feat(rls): activate RLS at runtime via split migrator/app DB roles
    8d564e0 Merge branch 'main' into chore/pms-201-remove-dead-stray-code
    6dfb9ee Merge pull request 'fix(startup): fail loud when configured SSO bootstrap fails' (#223) from fix/pms-289-sso-fail-loud-when-configured into main
    d79ab98 fix(startup): fail loud when configured SSO bootstrap fails
    86e40ab Merge pull request 'fix(tenants): seed lookup config for tenants reached off the PSA path' (#222) from feat/pms-288-seed-lookup-config-on-placement into main
    e0daef1 perf(tenants): cheap-guard the per-request sequence seed
    74adb16 fix(tenants): seed lookup config for tenants reached off the PSA path
    f75618f Merge pull request 'fix(tenants): set NOT-NULL kind in create_tenant; add regression test' (#221) from fix/pms-287-create-tenant-kind into main
    f7f8b7d fix(tenants): set NOT-NULL kind in create_tenant; add regression test
    8e2faad Merge pull request 'fix(startup): make migration failure fatal instead of warn-and-continue' (#219) from fix/pms-286-fatal-migrations into main
    b5ad162 Merge pull request 'feat(justfile): add dev-clean / dev-clean-all teardown recipes' (#214) from feat/dev-clean-recipes into main
    f82d9b3 Merge branch 'main' into feat/dev-clean-recipes
    398c0ba chore(cleanup): remove dead crates code and stray config/seed/test cruft
    a3333df fix(startup): make migration failure fatal instead of warn-and-continue
    842b18f chore(cleanup): remove dead and stray code from src/ tree
    303efb5 Merge pull request 'test(isolation): automate PMS-236 tenant-isolation QA across the API' (#216) from feat/pms-236-tenant-isolation-integration-test into main
    a3224a8 test(isolation): harden module-config and ticket_number checks
    f5520f7 fix: correct rmm, notifications, SLA, KB, portal, and contacts defects
    2c69eb9 test(isolation): automate PMS-236 tenant-isolation QA across the API
    6678777 test(settings): hit canonical /api/v1/invoices in module-gate tests
    5086c63 feat(justfile): add dev-clean / dev-clean-all teardown recipes
    9579f2b fix(contracts,billing): close financial-integrity gaps
    5f7c728 refactor: consolidate duplicated helpers, types, and test fixtures
    d68af1b fix(db): converge ticket filter builder; audit count_query placeholder family
    eaa150f fix(schema): harden migrations, FKs, seed, and truncation (PMS-196)
    0433815 fix(tickets): scope writes to tenant and validate FK references
    f69cc50 Merge pull request 'fix(rls): cover tenant-less tables; tenant-scope user_oauth_identities' (#207) from feat/pms-258-rls-cover-tenantless-tables into main
    eca763e Merge branch 'main' into feat/pms-258-rls-cover-tenantless-tables
    92bf278 fix(misc): correctness, concurrency, and test-reliability defects
    c224ffb fix(rls): cover tenant-less tables; tenant-scope user_oauth_identities
    3b508c1 Merge pull request 'refactor: converge duplicated subsystems and document architecture seams' (#206) from feat/pms-198-converge-subsystems into main
    f8029eb Merge branch 'main' into feat/pms-198-converge-subsystems
    a9ccf05 Merge pull request 'fix(audit): remove coarse per-request middleware that wrote unusable rows' (#198) from fix/pms-275-remove-coarse-audit-middleware into main
    c54ebe3 Merge branch 'main' into fix/pms-275-remove-coarse-audit-middleware
    1b55130 Merge pull request 'fix(ci): gate prod image push to main/tags; close CI coverage gaps' (#199) from fix/pms-189-ci-release-pipeline-drift into main
    9c0a400 Merge branch 'main' into fix/pms-189-ci-release-pipeline-drift
    0316824 refactor: converge duplicated subsystems and document architecture seams
    e7521d3 Merge pull request 'feat/pms-271-e2e-staging-prod-secrets' (#205) from feat/pms-271-e2e-staging-prod-secrets into main
    5807db8 fix(e2e): correct OIDC redirect_uri default to /auth/callback
    fb735aa refactor(e2e): name CI secrets after deployment variables
    0d33d0c feat(portal): mint setup-link token + email on portal-access grant
    2eb8977 fix(auth): close invite, signup, password, and validation defects
    222c649 Revert "fix(e2e): keep bare E2E_* as staging, add only E2E_PRODUCTION_"
    a4fef72 fix(auth): close SSO/OIDC auth-strength and conformance gaps
    d8e8eb0 fix(e2e): keep bare E2E_
    as staging, add only E2E_PRODUCTION_*
    ffb58e1 fix(auth): close legacy PSA auth path security gaps
    3221b6b Merge pull request 'feat(e2e): split CI env vars into staging/production secrets; manual prod opt-in' (#200) from feat/pms-271-e2e-staging-prod-secrets into main
    7788f76 feat(e2e): split CI env vars into staging/production secrets
    986a08e fix(ci): gate prod image push to main/tags; close CI coverage gaps
    6892bc4 fix(audit): remove coarse per-request middleware that wrote unusable rows
    730553a Merge pull request 'feat(migrations): no-op backfill + verification for co-mingled default-tenant rows' (#197) from feat/pms-263-comingled-backfill-verification into main
    2e65ee8 Merge branch 'main' into feat/pms-263-comingled-backfill-verification
    2e62ad8 Merge pull request 'test(isolation): per-user cross-isolation suite across modules' (#196) from feat/pms-264-per-user-isolation-suite into main
    8968aa0 feat(migrations): no-op backfill + verification for co-mingled default-tenant rows
    f71a7c6 test(isolation): per-user cross-isolation suite across modules
    a927e4e Merge pull request 'fix(workers): scope notification template lookup; audit from_trusted' (#195) from feat/pms-261-worker-from-trusted-audit into main
    2a9f970 fix(workers): scope notification template lookup; audit from_trusted
    6f54b2b Merge pull request 'refactor(domain): inject domains via env vars, never hardcode or fall back' (#192) from feat/pms-235-mokosh-domain-rebrand into main
    d155840 Merge branch 'main' into feat/pms-235-mokosh-domain-rebrand
    6cd85ca refactor(domain): inject all domains via env; drop hardcoded fallbacks
    2ef2fba Merge pull request 'fix(security): scope auth session methods to tenant; pin login-helper reachability' (#190) from feat/pms-260-cross-tenant-leak-fixes into main
    3c987d4 Merge branch 'main' into feat/pms-260-cross-tenant-leak-fixes
    4028957 Merge pull request 'feat(tenants): seed all editable lookups per tenant; reserve system-shared class' (#189) from feat/pms-259-seed-lookups-system-shared-class into main
    57804d1 fix(rls): renumber system-shared migration to 039; layer on PMS-257 fail-closed policy
    408d9bd Merge remote-tracking branch 'origin/main' into feat/pms-259-seed-lookups-system-shared-class
    100c59f Merge pull request 'feat(rls): flip tenant_isolation fail-closed with WITH CHECK and FORCE RLS' (#188) from feat/pms-257-rls-fail-closed into main
    6175e84 Merge branch 'main' into feat/pms-257-rls-fail-closed
    d8d2696 Merge pull request 'feat(tenant): remove the single-tenant cargo feature; no normal user lands in the default tenant' (#187) from feat/pms-262-remove-single-tenant-feature into main
    28e059f Merge branch 'main' into feat/pms-262-remove-single-tenant-feature
    50580b1 Merge pull request 'feat(db): route tenant-scoped service queries through begin_with_tenant' (#186) from feat/pms-256-rls-guc-plumbing into main
    c929e7c Merge branch 'main' into feat/pms-256-rls-guc-plumbing
    5da88eb Merge pull request 'docs(rls): per-user isolation reference and decomposition for PMS-255' (#185) from docs/pms-255-rls-isolation-plan into main
    18914ba Merge branch 'main' into docs/pms-255-rls-isolation-plan
    732427a Merge pull request 'chore: add pre-commit hook recipes' (#191) from chore/pre-commit-hooks into main
    22b4f59 Merge branch 'main' into chore/pre-commit-hooks
    106f983 Merge pull request 'fix(version): sanitise error bodies, warn on bad versions, test network path' (#194) from feat/pms-238-self-hosted-version-check into main
    fcf44f5 fix(version): sanitise error bodies, warn on bad versions, test network path
    35445fd Merge pull request 'feat(version): add self-hosted update check endpoint' (#193) from feat/pms-238-self-hosted-version-check into main
    551db74 feat(version): cache update-check result with a TTL
    e08a3ad feat(version): add self-hosted update check endpoint
    ebaa8d2 docs(domain): rebrand a8n.systems references to mokosh.systems (Bucket A)
    2a34180 chore: add install-hooks and pre-commit just recipes
    cbcfad6 fix(security): scope auth session methods to tenant; pin login-helper reachability
    78c5992 feat(tenants): seed all editable lookups per tenant; reserve system-shared class
    3476ed6 feat(rls): flip tenant_isolation fail-closed with WITH CHECK and FORCE RLS
    6969297 feat(tenant): remove the single-tenant cargo feature; no normal user lands in the default tenant
    bb76afb feat(db): route tenant-scoped service queries through begin_with_tenant
    864a0d8 docs(rls): per-user isolation reference and decomposition for PMS-255
    2f89fc7 Merge pull request 'feat(users): persist date_format_string preference on users (backend)' (#184) from feat/pms-253-date-format-preference-backend into main
    c57d5c6 feat(users): persist date_format_string preference on users (backend)
    a8116f3 Merge pull request 'chore(fmt): apply cargo fmt --all after PMS-244..PMS-249 sweep' (#183) from chore/pms-251-cargo-fmt-sweep into main
    990d585 chore(fmt): apply cargo fmt --all after PMS-244..PMS-249 sweep
    23b3259 Merge pull request 'test(auth): end-to-end placement tests for the bunyip login path (PMS-249)' (#182) from feat/mapps-249-bunyip-login-e2e into main
    9291524 test(auth): end-to-end placement tests for the bunyip login path (PMS-249)
    339e61b Merge pull request 'feat(invitations): email the invitee on invite create (PMS-246)' (#181) from feat/mapps-246-invite-email into main
    9d474ed Merge remote-tracking branch 'origin/main' into feat/mapps-246-invite-email
    85820e8 Merge pull request 'feat(auth): backfill default-tenant users off the shared tenant on login (PMS-245)' (#180) from feat/mapps-245-backfill-default-tenant-users into main
    9a752a6 feat(invitations): email the invitee on invite create (PMS-246)
    d8b05df feat(auth): backfill default-tenant users off the shared tenant on login (PMS-245)
    bf53e19 Merge pull request 'test(auth): pin the bunyip userinfo email_verified contract (PMS-248)' (#179) from chore/mapps-248-userinfo-email-verified-contract into main
    fd253a6 test(auth): pin the bunyip userinfo email_verified contract (PMS-248)
    f6c7d3f Merge pull request 'feat(invitations): tenant invite CRUD - Mokosh-owned org membership (PMS-244 phase 1)' (#178) from feat/mapps-244-invitations into main
    bd8ffd7 feat(auth): resolve SSO login to a tenant via Mokosh invites/self-signup (PMS-244, phase 2)
    b54b06c feat(invitations): tenant invite CRUD - Mokosh-owned org membership (PMS-244, phase 1)
    fa7724f Merge pull request 'feat(auth): lazily re-home default-tenant users to their org tenant (PMS-243)' (#177) from feat/mapps-243-rehome into main
    b78fa59 feat(auth): lazily re-home default-tenant users to their org tenant (PMS-243)
    59618ac Merge pull request 'feat(auth): resolve SSO users to a per-org tenant via the Bunyip org claim (PMS-240)' (#175) from feat/mapps-240-bunyip-org-tenant into main
    6b8ed51 feat(auth): resolve SSO users to a per-org tenant via the Bunyip org claim (PMS-240)
    bfb815f Merge pull request 'fix(seed): never auto-seed the shared Bunyip landing tenant (PMS-239)' (#174) from fix/mapps-239-demo-seed-shared-tenant into main
    18c60ef fix(seed): never auto-seed the shared Bunyip landing tenant (PMS-239)
    b2cf1b3 Merge pull request 'chore: cargo fmt --all after the PMS-139 TenantId sweep' (#173) from chore/cargo-fmt-after-pms-139-sweep into main
    c441b1c chore: cargo fmt --all after the PMS-139 TenantId sweep
    c7badab Merge pull request 'feat(auth): migrate the tickets hub to TenantId, completing PMS-139 (sweep batch 8)' (#171) from feat/pms-139-sweep-tickets into main
    17611c3 feat(auth): migrate the tickets hub to TenantId, completing PMS-139 (sweep batch 8)
    1ac0d22 Merge pull request 'feat(auth): migrate the notifications hub to TenantId (PMS-139 sweep batch 7)' (#169) from feat/pms-139-sweep-notifications into main
    d5c6133 feat(auth): migrate the notifications hub to TenantId (PMS-139 sweep batch 7)
    b4303f5 Merge pull request 'feat(auth): migrate the audit hub to TenantId (PMS-139 sweep batch 6)' (#167) from feat/pms-139-sweep-hubs into main
    f607764 feat(auth): migrate the audit hub to TenantId (PMS-139 sweep batch 6)
    23e36ba Merge pull request 'feat(auth): migrate contacts + billing to TenantId (PMS-139 sweep batch 5)' (#164) from feat/pms-139-sweep-contacts-billing into main
    3deb53e feat(auth): migrate contacts + billing to TenantId (PMS-139 sweep batch 5)
    e2a6de3 Merge pull request 'feat(auth): migrate knowledge_base + tenants + settings to TenantId (PMS-139 sweep batch 4)' (#163) from feat/pms-139-sweep-kb-tenants-settings into main
    0a1006d feat(auth): migrate knowledge_base + tenants + settings to TenantId (PMS-139 sweep batch 4)
    85e08a2 Merge pull request 'feat(auth): migrate calendar + sla + contracts to TenantId (PMS-139 sweep batch 3)' (#161) from feat/pms-139-sweep-calendar-sla-contracts into main
    b77e671 feat(auth): migrate calendar + sla + contracts to TenantId (PMS-139 sweep batch 3)
    c6b6adb Merge pull request 'feat(auth): migrate assets + projects to TenantId (PMS-139 sweep batch 2)' (#160) from feat/pms-139-sweep-assets-projects into main
    ee26720 feat(auth): migrate assets + projects to TenantId (PMS-139 sweep batch 2)
    622f3f4 Merge pull request 'feat(auth): migrate rmm + time_tracking to TenantId (PMS-139 sweep batch 1)' (#159) from feat/pms-139-sweep-rmm-time into main
    961251c feat(auth): migrate rmm + time_tracking to TenantId (PMS-139 sweep batch 1)
    1091d76 Merge pull request 'feat(auth): typed TenantId scoping foundation + reports migration (PMS-139)' (#158) from feat/pms-139-tenant-id-foundation into main
    2cc6511 feat(auth): typed TenantId scoping foundation + reports migration (PMS-139)
    a497e81 Merge pull request 'feat(timesheets): draft state + withdraw endpoint (PMS-183)' (#157) from feat/pms-183-timesheet-withdraw into main
    e75bdf9 feat(timesheets): draft state + withdraw endpoint for timesheets (PMS-183)
    45f51fc Merge pull request 'feat(audit): surface field-level before/after content in change history (PMS-204)' (#156) from feat/pms-204-history-content into main
    60f0e4f feat(audit): surface field-level before/after content in change history (PMS-204)
    9acd44a Merge pull request 'feat(projects): record task and project edits in the audit trail (PMS-184)' (#155) from feat/pms-184-task-edit-history into main
    6b20c9f Merge pull request 'feat(audit): per-record change-history endpoint (PMS-182)' (#154) from feat/pms-182-ticket-edit-history into main
    f123889 feat(projects): record task and project edits in the audit trail (PMS-184)
    110f532 feat(audit): per-record change-history endpoint for detail pages (PMS-182)
    caacdc9 Merge pull request 'docs: add 2026-06-06 platform audit to dev-docs/audits' (#153) from docs/add-platform-audit-2026-06-06 into main
    455e9cc docs: add 2026-06-06 platform audit to dev-docs/audits
    579bee7 feat(audit-log): resolve user + entity names server-side so the SPA can display them
    b4a1571 feat(auth): force new Bunyip-JIT users through name onboarding
    9cd4103 Merge pull request 'feat(billing): also surface invoice_number on payment responses (PMS-186)' (#150) from feat/pms-186-billing-company-name into main
    11bcb0e feat(billing): also surface invoice_number on payment responses (PMS-186)
    e450f5b Merge pull request 'feat(billing): include company_name on invoice and payment responses (PMS-186)' (#149) from feat/pms-186-billing-company-name into main
    6bbe7ff feat(billing): include company_name on invoice and payment responses (PMS-186)
    c32987c Merge pull request 'feat(reports): add safe custom report builder (PMS-180)' (#148) from feat/pms-180-custom-report-builder into main
    cc0023b feat(reports): add safe custom report builder (PMS-180)
    4bc54e0 Merge pull request 'feat(reports): add project + client report types end to end (PMS-179)' (#147) from feat/pms-179-project-client-reports into main
    148d3f6 feat(reports): add project + client report types end to end (PMS-179)
    92e77ea Merge pull request 'feat(seed): seed demo data on a new account's first visit (PMS-157)' (#146) from feat/pms-157-new-account-seeding into main
    b6f6ba1 fix(seed): make demo ticket seeding best-effort; document E2E kill-switch
    909dc07 feat(seed): seed demo data on a new account's first visit (PMS-157)
    aa6118f Merge pull request 'fix(contacts): 400 not 500 deleting a company with related records (PMS-170)' (#145) from fix/pms-170-company-delete-fk-400 into main
    7e13835 fix(contacts): return 400 not 500 when deleting a company with related records
    a86e802 Merge pull request 'fix(audit): correct SQL placeholder numbering in audit-log list (PMS-178)' (#144) from fix/pms-178-audit-list-placeholders into main
    7acb419 fix(audit): correct SQL placeholder numbering in audit-log list query
    ce0dc98 Merge pull request 'docs(e2e): spell out how to set the E2E account's admin role (SQL + bootstrap)' (#143) from docs/pms-155-e2e-admin-account into main
    d436054 Merge pull request 'feat(auth): translate Bunyip bunyip_role claim to mokosh role (PMS-172)' (#142) from feat/pms-172-bunyip-role-translation into main
    e950947 refactor(auth): clarify authz source + log Bunyip role transitions (PMS-172 review)
    8378289 feat(auth): translate Bunyip bunyip_role claim to mokosh role on the RS path
    0e3285b Merge pull request 'docs(e2e): how to set the E2E account admin role (SQL + bootstrap)' (#141) from docs/pms-155-e2e-admin-howto into main
    67c9cb8 docs(e2e): spell out how to set the E2E account's admin role (SQL + bootstrap)
    abd36b9 docs(e2e): spell out how to set the E2E account's admin role (SQL + bootstrap)
    3fcd925 Merge pull request 'docs(e2e): require admin role for the E2E account (PMS-155)' (#140) from docs/pms-155-e2e-admin-account into main
    e025056 docs(e2e): require admin role for the E2E account (PMS-155)
    f636e04 Merge pull request 'test(e2e): add API CRUD specs for time-tracking, projects, billing, contracts (PMS-155)' (#136) from feat/pms-155-e2e-core-modules into main
    8f878dd test(e2e): add API specs for RMM and the dispatch board (PMS-155 batch 4)
    007ba8b test(e2e): add API specs for notifications, settings, audit, reports (PMS-155 batch 3)
    fcf367f test(e2e): add API CRUD specs for calendar, SLA, assets, knowledge-base (PMS-155 batch 2)
    9aaeac6 test(e2e): make PMS-155 spec cleanup resilient to a single delete failure
    802a291 test(e2e): add API CRUD specs for time-tracking, projects, billing, contracts (PMS-155)
    c1dab9c Merge pull request 'fix(e2e): tolerate 405 on ticket delete until staging deploys PMS-149 route' (#135) from fix/pms-149-e2e-deploy-tolerant-ticket-delete into main
    289e772 fix(e2e): tolerate 405 on ticket delete until staging deploys PMS-149 route
    ba39edd Merge pull request 'feat(tickets): add DELETE /tickets/{id} so E2E cleans up its company (PMS-149)' (#133) from feat/pms-149-ticket-delete-route into main
    451fa6d fix(tickets): map ticket-delete FK violation to 400, not 500
    8a2e442 feat(tickets): add DELETE /tickets/{id} so E2E cleans up its company
    5cc3581 Merge pull request 'feat(e2e): un-fixme OIDC token-flow via replayed OP cookies (PMS-143)' (#132) from feat/pms-143-oidc-unfixme into main
    0de7285 chore(e2e): surface bunyip body on OIDC authorize non-3xx
    868658c fix(e2e): skip parent-domain derivation for IP literals
    0368bff feat(e2e): un-fixme OIDC token-flow via replayed OP cookies (PMS-143)
    853f9a1 Merge pull request 'test(e2e): re-fixme auth-ui pending PMS-148 login race' (#131) from fix/pms-148-refixme-auth-ui into main
    82418fc test(e2e): re-fixme auth-ui pending PMS-148 login race
    3e34835 Merge pull request 'feat(e2e): un-fixme auth-ui login + logout round-trip (PMS-142 v2)' (#130) from feat/pms-142-auth-ui-unfixme-v2 into main
    9b3cfe1 feat(e2e): un-fixme auth-ui login + logout round-trip (PMS-142 v2)
    5f850e7 Merge pull request 'chore(build): bump rust-builder-glibc to v1.0.1' (#127) from chore/bump-rust-builder-glibc-v1.0.1 into main
    0b7e68e Merge branch 'main' into chore/bump-rust-builder-glibc-v1.0.1
    5b184d2 Merge pull request 'feat(reports): GET /reports registry + integration tests (PMS-146)' (#129) from feat/pms-146-reports-registry-and-tests into main
    73e8682 feat(reports): add GET /reports registry + integration tests (PMS-146, closes PMS-93 gaps)
    cc4dcbd Merge pull request 'feat(assets): audited secret reveal, no plaintext in lists, tests (PMS-71)' (#128) from feat/pms-71-asset-secret-reveal-and-tests into main
    10b3760 feat(assets): stop leaking secrets in lists; add audited reveal endpoints + tests (PMS-71)
    78672ae chore(build): bump rust-builder-glibc to v1.0.1
    aac7c96 Merge pull request 'feat(projects): budget-vs-actual + time rollup + tests (PMS-51)' (#126) from feat/pms-51-project-actuals-and-tests into main
    5dc8acd feat(projects): compute budget-vs-actual, roll approved time into actuals, add tests (PMS-51)
    56e0a18 Merge pull request 'feat(portal): wire company-scoped invoices + add portal isolation tests (PMS-25)' (#125) from feat/pms-25-portal-invoices-and-tests into main
    7e0f275 feat(portal): wire company-scoped invoices + add portal isolation tests (PMS-25)
    bcf6f01 Merge pull request 'chore: Sync .gitignore' (#124) from chore/sync-gitignore into main
    86bbeca chore: Sync .gitignore
    70e2ae4 Merge pull request 'chore(e2e): capture access_token from /oauth2/token response, not just Bearer header' (#123) from chore/e2e-capture-spa-build-hash into main
    160688d chore(e2e): capture access_token from /oauth2/token response, not just Bearer header
    7619ecb Merge pull request 'chore(e2e): surface mokosh-apps SPA bundle hash in timeout diagnostic' (#122) from chore/e2e-capture-spa-build-hash into main
    cdf46ce chore(e2e): surface mokosh-apps SPA bundle hash in timeout diagnostic
    63ab9c1 Merge pull request 'fix(auth): JIT placeholder name no longer surfaces UUID fragments' (#121) from fix/jit-fallback-name-not-uuid-fragments into main
    37fcc9c fix(auth): JIT placeholder name no longer surfaces UUID fragments
    321520a Merge pull request 'feat(kb): one vote per user account for article ratings (toggle/switch)' (#120) from feat/kb-rating-per-user-votes into main
    f2e670f style: cargo fmt
    aeb0e56 feat(kb): make article ratings one vote per user account
    7a19fb6 Merge pull request 'feat(billing): generate recurring invoices from contract items (PMS-64 AC5)' (#119) from feat/pms64-recurring-invoicing into main
    a58c1db Merge branch 'main' into feat/pms64-recurring-invoicing
    4a4ec49 Merge pull request 'feat(kb): serve portal KB feed only via the portal-authed route' (#118) from feat/portal-kb-feed-auth into main
    bb0d8d2 Merge branch 'main' into feat/portal-kb-feed-auth
    98ec545 Merge pull request 'feat(notifications): backfill default rules for pre-existing tenants' (#117) from feat/notifications-seed-existing-tenants into main
    0f837be style: apply cargo fmt
    a1747d4 style: apply cargo fmt
    bd72ab6 style: apply cargo fmt
    e266dca feat(billing): generate recurring invoices from contracts (PMS-64 AC5)
    de3ebff feat(kb): serve portal KB feed only via portal-authed route
    d03ff7f feat(notifications): backfill default worker rules for existing tenants
    0c620dc Merge pull request 'fix(api): repair list ORDER BY misuse + time-entries count placeholders (PMS-145)' (#116) from fix/pms-145-time-entries-count-placeholders into main
    df0ce4b fix(api): repair list ORDER BY misuse + time-entries count placeholders
    66a1da9 Merge pull request 'fix(ci): cap integration-test parallelism to dodge sqlx-test pool timeout' (#115) from fix/check-pg-pool-saturation into main
    d531f38 Merge pull request 'feat(e2e): close PMS-142 - un-fixme the auth-ui login + logout round-trip' (#112) from feat/pms-142-auth-ui-unfixme into main
    eea87f3 fix(ci): cap integration-test parallelism to dodge sqlx-test pool timeout
    02c30e0 Merge pull request 'fix(billing): gate invoicing on timesheet approval (PMS-144)' (#114) from fix/pms-144-billing-approval-gate into main
    4bd93f4 fix(billing): gate invoicing on timesheet approval (ready_to_bill)
    d9abe84 Merge pull request 'feat(dev-sso): validate bunyip-issued tokens as a Resource Server' (#113) from feat/dev-sso-rs-trust-bunyip into main
    b6d9619 test(e2e): re-quarantine auth-ui - bunyip /logout does not kill OP session
    ce25821 feat(dev-sso): validate bunyip-issued tokens as a Resource Server
    8c50b19 test(e2e): tighten page-diagnostics API + use Error cause for original stack
    826e5b4 feat(e2e): close PMS-142 - un-fixme the auth-ui login + logout round-trip
    c94e090 Merge pull request 'feat(ci): close PMS-141 - promote E2E to merge gate on PRs to main' (#111) from feat/pms-141-e2e-merge-gate into main
    ae35028 feat(ci): close PMS-141 - promote E2E suite to merge gate on PRs to main
    b60a8d3 Merge pull request 'test(e2e): quarantine OIDC token-flow test - needs OP session cookie' (#110) from fix/e2e-oidc-fixme into main
    bfa3945 test(e2e): quarantine OIDC token-flow test - needs OP session cookie
    cab7078 Merge pull request 'fix(e2e): point OIDC test at the OP host, quarantine auth-ui round-trip' (#109) from fix/e2e-oidc-op-base-url-and-auth-ui-fixme into main
    dc5019e docs(e2e): refresh stale comments + README harness description
    718e6bb fix(e2e): point OIDC test at the OP host, quarantine auth-ui round-trip
    d9d3084 Merge pull request 'fix(e2e): capture bearer from ANY host, not just /api/v1' (#108) from fix/e2e-capture-bearer-any-host into main
    8a69f3f fix(e2e): capture bearer from ANY host, not just /api/v1
    95637bd Merge pull request 'fix(ci): pass E2E_TOTP_SECRET secret into the E2E job env' (#107) from fix/e2e-wire-totp-secret into main
    5bcaf8d fix(ci): pass E2E_TOTP_SECRET secret into the E2E job env
    319f8d2 Merge pull request 'feat(e2e): enter TOTP second factor during SPA login' (#106) from feat/e2e-totp-handling into main
    1a9e37e feat(e2e): enter TOTP second factor during SPA login
    4d46bf6 Merge pull request 'fix(e2e): treat /login/* as still IN the login flow' (#105) from fix/e2e-login-pathname-anchor into main
    88c274b fix(e2e): treat /login/* (e.g. /login/2fa) as still IN the login flow
    a90fbfb Merge pull request 'test(e2e): dump full URL/trail/request diagnostic when setup times out' (#104) from fix/e2e-bearer-diagnostics into main
    b30dc74 test(e2e): dump full URL/trail/request diagnostic when setup times out
    a383fb1 Merge pull request 'fix(e2e): probe data routes for bearer, merge auth-ui to dodge rate limit' (#103) from fix/e2e-spa-bearer-intercept-and-logout into main
    66e3433 test(e2e): harden auth-ui locators with .first(), tighten /api/v1 filter
    de17374 fix(e2e): force a post-login probe + merge auth-ui tests to dodge rate limit
    29d2eb8 Merge pull request 'fix(e2e): intercept SPA bearer for setup, click User-menu -> Logout' (#102) from fix/e2e-spa-bearer-intercept-and-logout into main
    30d66a9 test(e2e): refresh auth-state comment, drop dead routes.authLogin
    328ffbc fix(e2e): intercept SPA bearer for setup, click User-menu -> Logout
    4b01eef Merge pull request 'fix(ci): wait-for-deploy refetches missing trees, skips gate if unresolvable' (#101) from fix/e2e-wait-deploy-skip-when-unresolvable into main
    9c73dd1 fix(ci): wait-for-deploy refetches missing trees, skips gate if unresolvable
    6bf0e9a Merge pull request 'fix(ci): wait-for-deploy unshallows the clone when git log returns empty' (#100) from fix/e2e-wait-deploy-unshallow into main
    efea598 fix(ci): wait-for-deploy unshallows the clone when git log returns empty
    d47d72a Merge pull request 'fix(ci): resolve deploy-sync expected SHA to the last build-relevant commit' (#99) from fix/e2e-runner-toolcache into main
    6040838 fix(ci): resolve deploy-sync expected SHA to the last build-relevant commit
    8924692 Merge pull request 'test(e2e): aggregate-all-missing env vars in a preflight setup project' (#98) from fix/e2e-runner-toolcache into main
    104ed55 test(e2e): aggregate-all-missing env vars in a preflight setup project
    99738ad Merge pull request 'fix(ci): bake MOKOSH_GIT_HASH + describe + date into OCI build' (#97) from fix/e2e-runner-toolcache into main
    52742c6 fix(ci): bake MOKOSH_GIT_HASH + describe + date into OCI build
    256d17f Merge pull request 'fix(ci): API host is api.msp., not msp-api.' (#96) from fix/e2e-runner-toolcache into main
    3adb4a2 fix(ci): API host is api.msp., not msp-api.
    9d8b11a Merge pull request 'fix(ci): split E2E SPA host from API host, derive msp-api.' (#95) from fix/e2e-runner-toolcache into main
    3883fc8 fix(ci): split E2E SPA host from API host, derive msp-api.
    80a160e Merge pull request 'fix(ci): treat empty E2E_BASE_URL as missing, not as override' (#94) from fix/e2e-runner-toolcache into main
    0de6163 fix(ci): treat empty E2E_BASE_URL as missing, not as override
    e965450 Merge pull request 'fix(ci): drop playwright --with-deps - opensuse runner has no apt-get' (#93) from fix/e2e-runner-toolcache into main
    21ca57f fix(ci): drop playwright --with-deps - opensuse runner has no apt-get
    78ed118 Merge pull request 'fix(ci): hardcode RUNNER_TOOL_CACHE path - runner context unbound in job env' (#92) from fix/e2e-runner-toolcache into main
    020631b fix(ci): hardcode RUNNER_TOOL_CACHE path - runner context unbound in job env
    3f8c2a4 Merge pull request 'fix(ci): point E2E setup-node tool cache at RUNNER_TEMP' (#91) from fix/e2e-runner-toolcache into main
    55eb53c fix(ci): point E2E setup-node tool cache at RUNNER_TEMP
    aadb3bb Merge pull request 'PMS-140: Add Playwright E2E suite against staging, run on every merge to main' (#90) from feat/pms-140-e2e-playwright into main
    01a4d7d test(e2e): fix auth path, require OIDC redirect, harden teardown and CI
    b4ccc9a test(e2e): add Playwright suite against staging, run post-merge to main
    0c39d1e Merge pull request 'feat(auth): close PMS-138 - subdomain-driven tenant hint on login + forgot-password + Google JIT' (#89) from feat/pms-138-closeout into main
    c7d40ed feat(auth): close PMS-138 - subdomain-driven tenant hint on login + forgot-password + Google JIT
    4b4c1f4 Merge pull request 'fix(settings): drop legacy DELETE /settings/{id} - matchit conflict with /settings/{category}' (#88) from fix/settings-route-conflict into main
    a0d3d91 fix(settings): drop legacy DELETE /settings/{id} - matchit conflict with /settings/{category}
    934f96a Merge pull request 'feat(settings): close PMS-113 - unify module-config, add category endpoints + value validation, gate optional modules at runtime' (#87) from feat/pms-113-closeout into main
    606ba79 feat(settings): close PMS-113 - unify module-config, add category endpoints + value validation, gate optional modules at runtime
    63a1bfc Merge pull request 'feat(tenants): close PMS-21 - cfg-gate cleanup + module-config and cross-tenant tests' (#86) from feat/pms-21-closeout into main
    272d179 feat(tenants): close PMS-21 - cfg-gate cleanup + module-config and cross-tenant tests
    7a34463 Merge pull request 'fix(list-queries): correct WHERE param numbering for count_query in 6 list_* methods' (#85) from fix/list-count-query-param-mismatch into main
    6acbb23 fix(list-queries): correct WHERE param numbering for count_query in 6 list_* methods
    68580e0 Merge pull request 'fix(migrations): rename PMS-4 recovery-codes migration 027 -> 029 (version collision with PMS-106)' (#84) from fix/migration-027-collision into main
    7edbf13 fix(migrations): rename PMS-4 recovery-codes migration 027 -> 029 (version collision with PMS-106)
    4d995d4 Merge pull request 'feat(auth): close PMS-4 - list filter, layered rate limit, MFA recovery codes, tenant hardening' (#83) from feat/pms-4-closeout into main
    387e18d Merge pull request 'feat(calendar): appointment reminders worker (PMS-58)' (#81) from feat/pms-58-calendar-reminders into main
    e922e0f Merge branch 'main' into feat/pms-58-calendar-reminders
    d00c3d0 Merge remote-tracking branch 'origin/main' into feat/pms-58-calendar-reminders
    6a0e444 Merge pull request 'feat(audit): record billing + contracts mutations in the audit log (PMS-117)' (#82) from feat/pms-117-audit-hooks into main
    be05264 Merge branch 'main' into feat/pms-117-audit-hooks
    ef0ffbf feat(audit): record billing + contracts mutations in the audit log (PMS-117)
    35704a5 Merge pull request 'feat(sla): at-risk + breach notifications via sweep worker (PMS-106)' (#80) from feat/pms-106-sla-notify into main
    1d020fb feat(calendar): appointment reminders worker (PMS-58)
    d25444b feat(auth): close PMS-4 - list filter, layered rate limit, MFA recovery codes, tenant hardening
    e1244a0 feat(sla): at-risk + breach notifications via sweep worker (PMS-106)
    242360a Merge pull request 'feat(calendar): RRULE expansion + dispatch view (PMS-58)' (#77) from feat/pms-58-calendar into main
    8874be8 Merge branch 'main' into feat/pms-58-calendar
    bdcb4d5 Merge pull request 'feat(contracts): hour-balance consumption, rate resolution, lifecycle (PMS-64)' (#75) from feat/pms-64-contracts into main
    0fd9124 Merge remote-tracking branch 'origin/main' into feat/pms-58-calendar
    39950bf Merge remote-tracking branch 'origin/main' into feat/pms-64-contracts
    269c79d Merge pull request 'feat(kb): publish lifecycle, slug uniqueness, version restore, trigram search, feedback, portal feed (PMS-79)' (#78) from feat/pms-79-kb into main
    89fd6fd Merge branch 'main' into feat/pms-79-kb
    dbf8c91 feat(kb): publish lifecycle, slug uniqueness, version restore, trigram search, feedback, portal feed (PMS-79)
    f1d7e99 Merge pull request 'feat(billing): generate invoices from billable time entries (PMS-33)' (#76) from feat/pms-33-billing into main
    c7884b1 Merge branch 'main' into feat/pms-33-billing
    025f6d3 Merge pull request 'feat(sla): business-hours-aware SLA clock engine (PMS-106)' (#72) from feat/pms-106-sla-clock into main
    bc9e86a Merge branch 'main' into feat/pms-106-sla-clock
    1cff709 Merge pull request 'feat(audit): record entity mutations + auth events (PMS-117)' (#70) from feat/pms-117-audit-log into main
    d5e9b1b feat(calendar): RRULE expansion + dispatch view (PMS-58)
    ba69f11 Merge remote-tracking branch 'origin/main' into feat/pms-64-contracts
    d807030 Merge remote-tracking branch 'origin/main' into feat/pms-106-sla-clock
    f7f08c1 Merge remote-tracking branch 'origin/main' into feat/pms-117-audit-log
    7396d1b feat(billing): generate invoices from billable time entries (PMS-33)
    9c14ffa feat(contracts): hour-balance consumption, rate resolution, lifecycle (PMS-64)
    a3ebcd0 Merge pull request 'test(pms-11): cover assign + assert JOINed ticket DTO fields (F3)' (#73) from feat/pms-11-tickets-assign-joined-fields-test into main
    fca3248 Merge pull request 'fix(contacts): make update_company and update_contact persist every UpdateRequest field' (#74) from feat/pms-17-tests into main
    ef5af80 test(pms-11): cover assign + assert JOINed ticket DTO fields (F3)
    3bbf1e5 fix(contacts): make update_company and update_contact persist every Update
    Request field
    6bd23e4 feat(sla): business-hours-aware SLA clock engine (PMS-106)
    e489d7a Merge pull request 'test(pms-17): integration tests pinning F4 fix + site/contact CRUD + portal flag + F9 filter' (#71) from feat/pms-17-tests into main
    e1aec1a test(pms-17): integration tests pinning F4 fix + site/contact CRUD + portal flag + F9 filter
    708d322 feat(audit): record entity mutations + auth events in the audit log
    a305bca Merge pull request 'feat(scheduler): shared background-job runner (PMS-135)' (#69) from feat/pms-135-scheduler into main
    3895e17 fix(scheduler): address code-review findings on shared scheduler (PMS-135)
    887dbd7 feat(scheduler): shared background-job runner (PMS-135)
    0d4c473 Merge pull request 'feat(api): readiness probe at /api/v1/ready (PMS-130)' (#68) from feat/pms-130-readiness into main
    bbe9855 fix(api): address code-review findings on /ready (PMS-130)
    6174aa4 feat(api): readiness probe at /api/v1/ready (PMS-130)
    69364fc Merge pull request 'feat(types): extract shared DTOs into mokosh-types workspace crate (PMS-129)' (#67) from feat/pms-129-mokosh-types into main
    99ca90e fix(types): add serde_json dep + refresh Cargo.lock for new crate (PMS-129)
    02fa99f fix(types): replace AppError reference with crate-local AuthRequired marker (PMS-129)
    5454f62 feat(types): extract shared DTOs into a mokosh-types workspace crate (PMS-129)
    e24ff59 Merge pull request 'feat(migrations): split 1688-line initial schema into per-feature files (PMS-128)' (#66) from feat/pms-128-migration-split into main
    813c1c1 feat(migrations): split 1688-line initial schema into per-feature files (PMS-128)
    666913f Merge pull request 'feat(api): paginate every list handler (PMS-127)' (#65) from feat/pms-127-pagination into main
    a03bfff docs(pagination): clarify audit-log cap drop + dual-Query convention
    91e1a40 feat(api): route every list handler through utils/pagination (PMS-127)
    46a45a1 Merge pull request 'feat(tracing): named instrument spans on every service method (PMS-126)' (#64) from feat/pms-126-tracing-spans into main
    529e778 feat(tracing): add named instrument spans on every service method (PMS-126)
    6c80f4c Merge pull request 'feat(router): self-documenting 501 body for placeholder modules (PMS-125)' (#63) from feat/pms-125-stub-bodies into main
    d5044c0 style: allow dead_code on common::{seed_admin,login} for unauthenticated test binaries
    d8ae8af style: rewrap assert! to satisfy rustfmt in dispatch_stub test
    0940a9d style: reflow long lines for rustfmt in PMS-125 stub
    f421318 feat(router): self-documenting 501 body for placeholder modules (PMS-125)
    bf5c42a Merge pull request 'feat(rmm): device-sync worker, provider trait, suppression + dedupe (PMS-103)' (#62) from feat/pms-103-rmm-sync-worker into main
    0dfea6d style: drop unused RmmService accessor methods
    6aefab3 style: apply rustfmt to PMS-103 rmm sync worker work
    1840992 feat(rmm): sync worker, provider trait, prefs/dedupe, encryption key wire-up
    9889f59 Merge pull request 'feat(notifications): dispatcher worker, prefs, encryption (PMS-92)' (#61) from feat/pms-92-notifications-dispatcher into main
    09e5435 style(tests): factor notifications row tuple into NotifRow alias
    4f43203 style: blank line between bullet list and paragraph in dispatch doc
    76a54d7 style: apply rustfmt to PMS-92 notifications dispatcher work
    d3717d4 feat(notifications): dispatcher worker, preference enforcement, encryption key wire-up
    98d3aa7 Merge pull request 'fix(auth): seed first_name/last_name defaults on bunyip JIT user upsert' (#60) from feat/jit-user-name-defaults into main
    7dceaf6 fix(auth): seed first_name/last_name defaults on bunyip JIT user upsert
    d775285 Merge pull request 'feat(contacts): populate CompanyResponse rollup fields' (#59) from feat/crm-ops into main
    abc2bc4 feat(contacts): populate CompanyResponse rollup fields
    7747fb4 Merge pull request 'feat(time-tracking): rounding, rate derivation, timesheet approval, tenant scoping' (#58) from feat/time-tracking-resolve-rounding into main
    d0acbb5 feat(time-tracking): apply rounding, derive rates, add timesheet approval + tenant scoping
    248e135 Merge pull request 'fix/dev-sso-private-network-name' (#57) from fix/dev-sso-private-network-name into main
    dd34ae9 fix(dev): drop server's depends_on infisical so compose project is valid
    0aaa5f2 fix(dev-sso): match overlay private network name to base per-user name
    87cac87 Merge pull request 'style(tests): rustfmt wrap on hash_password call' (#56) from fix/pms-124-fmt-wrap into main
    81a294c fix(tickets): cast sla_targets DECIMAL columns to float8 for sqlx decode
    fb96025 test(common): init tracing subscriber so the real sqlx error surfaces
    2de1bd8 fix(tests): send custom_fields: {} on create ticket, surface error body
    81487c8 fix(ci): point DATABASE_URL at the postgres service name, not localhost
    c4727dc style(tests): allow dead_code on per-binary-unused common items
    e56e42a style(tests): rustfmt wrap on hash_password call
    cc5f7c3 Merge pull request 'feat(tests): module-level integration tests for real route groups (F10)' (#55) from feat/pms-124-integration-tests into main
    806312f fix(tests): authenticate via Authorization: Bearer, not a cookie jar
    d172c15 feat(tests): module-level integration tests for real route groups (F10)
    3b62355 Merge pull request 'feat(api): add length validation to all Filter query types (F9)' (#54) from feat/pms-123-filter-validate into main
    7b67a7a docs(kb): note ILIKE cap rationale on KbArticleFilter.q
    b7c612f feat(api): add length validation to all Filter query types (F9)
    53450bf Merge pull request 'style(auth): cargo fmt oidc_rs.rs' (#53) from fix/oidc-rs-fmt into main
    9c3dffd style(auth): cargo fmt oidc_rs.rs
    52a790d Merge pull request 'feat/bunyip-op-cutover' (#52) from feat/bunyip-op-cutover into main
    cae15d2 feat(auth): wire bunyip RS verifier into AuthMiddleware
    cf2ff21 feat(auth): implement Resource-Server at+jwt verifier
    8131438 feat(auth): scaffold Resource-Server OIDC verifier (bunyip-as-OP cutover)
    d351093 Merge pull request 'feat/fix-just-dev-wont-start-infisical' (#51) from feat/fix-just-dev-wont-start-infisical into main
    de6da3a rewrite comment for dev-down and dev-clean
    d286b3a fix comment
    ef1649a add start infisical recipe
    0b3727c add tag
    eabf8e4 Merge pull request 'fix(ci): generate release notes from commits since previous tag' (#50) from fix/release-notes-since-prev-tag into main
    dd7f0fd fix(ci): generate release notes from commits since previous tag
    5b5eef0 Merge pull request 'fix/db-migrations-in-app' (#49) from fix/db-migrations-in-app into main
    0c9a066 WIP: Build on every push
    3d2b51a WIP: Build on every push
    82b3044 WIP: Build on every push
    562f8e4 fix(migrations): move mokosh_auth schema/columns into app migrations
    9b551af Merge pull request 'PSA audit: auth + tenancy security fixes' (#48) from feat/m1-vas into main
    dc202a9 fix(test): bind reset-token strings before borrowing (E0716)
    1b2568e fix(router): use api.msp host prefix in not_a_frontend fallback
    033b6bc style: cargo fmt --all
    92c485e test(auth): unit-test reset-token parsing + super-admin email allowlist
    ddaaeeb ci: publish the server image as mokosh-server (single name)
    6f406d3 ci: publish the server image as both mokosh-server and mokosh-api
    71e65ce fix(db): parameterize RLS tenant GUC; drop ineffective with_tenant
    1d5ac98 feat(auth): harden Google OAuth (verified-email link, email allowlist, fail-closed)
    5e0af57 fix(tenancy): validate cross-tenant foreign keys on create/update
    bd1d794 fix(auth): bind reset tokens to user; gate sign-in on tenant status
    6c50a88 Merge pull request 'fix(dev-sso): route Traefik labels on Compose 2.40 + shared Traefik' (#47) from fix/dev-sso-traefik-label-interpolation into main
    d13ba40 fix(dev-sso): route Traefik labels correctly on Compose 2.40 + shared Traefik
    c170baa Merge pull request 'docs: quickstart for fresh clones + CLAUDE.md' (#46) from docs/quickstart into main
    a05771a docs: add quickstart for fresh clones, plus CLAUDE.md
    e1ddf6f Merge pull request 'fix(ci): trigger image build on crates/
    edits + add manual dispatch' (#45) from fix/ci-trigger-crates into main
    6cb9513 fix(ci): trigger image build on crates/** edits + add manual dispatch
    3fdc0ef Merge pull request 'feat(ci): use $RUNNER_TEMP instead of /tmp in claude-fix workflow' (#44) from feat/pms-132-no-tmp into main
    bcd0990 feat(ci): guard against unset RUNNER_TEMP before using it
    54f1941 feat(ci): use $RUNNER_TEMP instead of /tmp in claude-fix workflow
    11fc65b Merge pull request 'fix(clippy): use ok_or instead of ok_or_else for plain HttpError values' (#43) from fix/clippy-sessions-ok-or into main
    1f83ca5 fix(clippy): use ok_or instead of ok_or_else for plain HttpError values
    d2e9177 Merge pull request 'Implementing SSO, OIDC (All auth)' (#42) from feature-sso into main
    09751b3 Merge remote-tracking branch 'origin/main' into feature-sso
    ce3f90f feat(oidc): fan back-channel logout out on RP-initiated /oauth2/logout
    93883ed feat(extractor): BearerUser rejects tokens from revoked op_sessions
    0b99014 feat(auth): emit back-channel logout to every registered RP on /v1/auth/logout
    97af972 Merge pull request 'feat(justfile): fj-driven create-release + group attrs (DEV-331)' (#41) from feat/justfile-fj-pr-create-DEV-331 into main
    778a440 feat(justfile): fj-driven create-release + group attrs (DEV-331)
    4353d52 log 5xx errors with underlying AuthError detail
    8a0b75f Merge pull request 'feat: consolidate PMS server PRs (PMS-4..PMS-131)' (#40) from feat/pms-merge into main
    9fa4c00 fix(clippy): resolve remaining workspace clippy warnings for -D warnings
    ff7413d fix(clippy): apply machine-applicable clippy suggestions
    0db5161 style: cargo fmt --all; suppress two pre-existing clippy lints in mokosh-auth-core
    01ebd03 ci(check): add fmt + clippy + compile + tests workflow
    01aefb8 Merge remote-tracking branch 'origin/chore/claude-fix-workflow' into feat/pms-merge
    13d5dda Merge remote-tracking branch 'origin/feat/audit-story-pms-117' into feat/pms-merge
    52ad3ac Merge remote-tracking branch 'origin/feat/settings-story-pms-113' into feat/pms-merge
    be878e7 Merge remote-tracking branch 'origin/feat/sla-story-pms-106' into feat/pms-merge
    0862e7f Merge remote-tracking branch 'origin/feat/rmm-story-pms-100' into feat/pms-merge
    d1d4efb Merge remote-tracking branch 'origin/feat/reports-story-pms-93' into feat/pms-merge
    a057e56 Merge remote-tracking branch 'origin/feat/notifications-story-pms-85' into feat/pms-merge
    6023551 Merge remote-tracking branch 'origin/feat/kb-story-pms-79' into feat/pms-merge
    c415e73 Merge remote-tracking branch 'origin/feat/assets-story-pms-71' into feat/pms-merge
    b718b3f Merge remote-tracking branch 'origin/feat/contracts-story-pms-64' into feat/pms-merge
    236e484 Merge remote-tracking branch 'origin/feat/calendar-story-pms-58' into feat/pms-merge
    62b9643 Merge remote-tracking branch 'origin/feat/projects-story-pms-51' into feat/pms-merge
    b239c93 Merge remote-tracking branch 'origin/feat/time-tracking-story-pms-42' into feat/pms-merge
    c166d47 Merge remote-tracking branch 'origin/feat/billing-story-pms-33' into feat/pms-merge
    7ea90af Merge remote-tracking branch 'origin/feat/portal-story-pms-25' into feat/pms-merge
    40d26ac Merge remote-tracking branch 'origin/feat/tenants-story-pms-21' into feat/pms-merge
    66f1da3 Merge remote-tracking branch 'origin/feat/contacts-story-pms-17' into feat/pms-merge
    e957286 Merge remote-tracking branch 'origin/feat/tickets-story-pms-11' into feat/pms-merge
    a35b3f0 Merge remote-tracking branch 'origin/feat/auth-story-pms-4' into feat/pms-merge
    e2fb8b2 ci: add claude-fix workflow
    47b3574 fix(justfile): register lets-chat with per-provider redirect_uri slug
    02deaed feat(audit): scaffold + writer middleware + read endpoint
    43b1c1f feat(settings): scaffold + tenant settings + module configs
    f4a2d8a feat(sla): scaffold + policies, targets, business hours, holidays, evaluator
    ff4b78e feat(rmm): scaffold + connections, device mappings, alert rules, ingest
    25ea790 feat(reports): scaffold + dashboard, tickets, time, billing, CSV export
    fe702cc feat(notifications): scaffold + channels, templates, prefs, inbox, rules, dispatcher
    d1812ea feat(kb): scaffold + categories, articles, versioning, portal feed
    ffce981 feat(assets): scaffold + asset types, assets, relationships, CIs, credentials, audit
    7298b90 feat(contracts): scaffold + CRUD for contracts, items, balances, rate cards
    24ddf81 feat(calendar): scaffold + appointments, availability, time-off, on-call
    96ce98b feat(projects): scaffold + projects, phases, task statuses, tasks, dependencies
    adaa594 feat(time-tracking): implement work types, time entries, timesheets, timers, rounding rules
    cefa9ac feat(time-tracking): scaffold module + wire router (PMS-43)
    f31778b feat(billing): tax rates CRUD + jurisdiction lookup
    81da537 feat(billing): payment gateway configs CRUD with at-rest encryption
    d3de7f1 feat(billing): payments CRUD with invoice-status reconciliation
    e67030c feat(billing): PUT /api/v1/invoices/:id with frozen-status guard
    47a0ae4 feat(billing): POST /api/v1/invoices with atomic sequence + line items
    44e178f feat(billing): GET /api/v1/invoices/:id with lines
    04e89de feat(billing): GET /api/v1/invoices paginated + filterable (F7)
    5105a8a feat(billing): scaffold module + wire router (PMS-34)
    fed6a72 feat(portal): GET /api/v1/portal/kb placeholder pending PMS-79
    28775fc feat(portal): GET /api/v1/portal/invoices/:id placeholder pending PMS-33
    73d99e2 feat(portal): GET /api/v1/portal/invoices placeholder pending PMS-33
    ab0b9f2 feat(portal): GET /api/v1/portal/tickets/:id with cross-company 404
    60488bc feat(portal): GET /api/v1/portal/tickets lists company-scoped tickets
    b06c6b0 feat(portal): POST /api/v1/portal/tickets creates contact-scoped ticket
    c1d6b7a feat(portal): contact-scoped session + portal auth middleware (F6)
    c38cdea feat(router): gate /api/v1/tenants behind the multi-tenant feature
    3bcfe6f feat(auth): TenantScope extractor for explicit tenant-id propagation
    2b634ab chore(router): drop the dead /companies alias
    e00a272 feat(contacts): honor create_portal_access on create_contact
    06de4ab feat(tickets): dispatch notifications + webhooks from automation rules
    7bb0df5 feat(tickets): send email when add_note has send_email = true
    eaf66db fix(tickets): join lookup tables into TicketResponse (F3)
    e90708c feat(auth): personal API key CRUD (issue / list / revoke)
    3a9cb3f feat(auth): implement MFA TOTP verification + enrollment endpoints
    8ce5238 feat(auth): send welcome email on admin user creation
    d67f8bf feat(auth): send password reset email via Mailer trait
    e2eff78 Merge pull request 'Revert M1 image distribution work' (#19) from revert/m1-image-distribution into main
    b573fc4 Revert M1 image distribution work
    848fcc3 Merge pull request 'ci: fix QEMU binfmt registration for arm64 cross-build' (#18) from feat/m1-image-distribution into main
    fa7ab7d ci: fix QEMU binfmt registration for arm64 cross-build
    89d19f4 Merge pull request 'ci: register QEMU binfmt inline instead of via setup-qemu-action' (#17) from feat/m1-image-distribution into main
    a74fbd5 ci: register QEMU binfmt inline instead of via setup-qemu-action
    2444c15 Merge pull request 'feat/m1-image-distribution' (#16) from feat/m1-image-distribution into main
    36b647e docs(operator): document container registry auth for self-hosters
    fcb323e feat(m1): operator compose.yml + update check + multi-arch build
    dc17aee Merge pull request 'fix(oci): drop image.source label causing dual-org package listing' (#15) from fix/drop-oci-source-label into main
    87a85b6 fix(oci): drop image.source label causing dual-org package listing
    bc546f0 chore(justfile): add register-lets-chat-client recipe (sso M1)
    a253ef3 feat(users): protect Owner memberships from other admins
    c097ae3 fix(auth): switch_active_tenant rebinds new token to caller's op_session
    8ac097a feat(audit): filter extensions + CSV export (item 7 server)
    e0575bd feat(sessions): admin endpoints to list+revoke another user's sessions
    139d71b Merge pull request 'feat: multi-origin CORS + msp-api placeholder + calendar route stub' (#14) from feat/cors-multi-origin-and-placeholder into main
    7beb7a4 security: stop tracking .env.dev; gitignore future copies
    4fb15b2 docs: milestone-1 handoff for the PSA Systems v0.1.0 cutover

    Downloads
  • v0.2.0 9d641d56ed

    v0.2.0
    Some checks failed
    Build OCI container / Build and push mokosh-api image (push) Failing after 9s
    Stable

    David released this 2026-05-14 23:57:18 +02:00 | 1257 commits to main since this release

    Changelog

    da5b705 Merge pull request 'Release v0.2.0' (#228) from release/v0.2.0 into main
    cbc6b03 Release v0.2.0
    00084a0 Merge pull request 'fix(assets): wire configured AES key and harden vault crypto integrity' (#226) from fix/pms-188-assets-zero-key-crypto-integrity into main
    93df0f9 Merge origin/main into fix/pms-188-assets-zero-key-crypto-integrity
    92a4975 Merge origin/main into fix/pms-188-assets-zero-key-crypto-integrity
    3a4327a Merge pull request 'feat(rls): activate RLS at runtime via split migrator/app DB roles' (#224) from feat/pms-285-rls-runtime-role-split into main
    714658e Merge origin/main into feat/pms-285-rls-runtime-role-split
    55000e0 Merge pull request 'chore(cleanup): remove dead crates code and stray config/seed/test cruft' (#220) from chore/pms-202-remove-dead-code into main
    1f4884e Merge origin/main into chore/pms-202-remove-dead-code
    e1e04e2 Merge pull request 'chore(cleanup): remove dead and stray code from src/ tree' (#218) from chore/pms-201-remove-dead-stray-code into main
    5755852 Merge origin/main into chore/pms-201-remove-dead-stray-code
    69bdf0a Merge pull request 'fix: correct rmm, notifications, SLA, KB, portal, and contacts defects' (#217) from fix/pms-195-module-correctness-defects into main
    0c7f45d Merge origin/main into fix/pms-195-module-correctness-defects
    02c6300 Merge pull request 'test(settings): hit canonical /api/v1/invoices in module-gate tests' (#215) from chore/checks-workflow-986a08ef into main
    258450c Merge origin/main into chore/checks-workflow-986a08ef
    2e34ede Merge pull request 'fix(contracts,billing): close financial-integrity gaps' (#213) from fix/pms-194-contracts-billing-financial-integrity into main
    6d07954 Merge origin/main into fix/pms-194-contracts-billing-financial-integrity
    bad33e0 Merge pull request 'refactor: consolidate duplicated helpers, types, and test fixtures' (#212) from feat/pms-199-consolidate-duplicated-helpers into main
    c00cae2 Merge origin/main into feat/pms-199-consolidate-duplicated-helpers
    eac8dbb Merge pull request 'fix(db): converge ticket filter builder; audit count_query placeholder family' (#211) from feat/pms-197-audit-count-query-placeholder-family into main
    4a7c72e Merge origin/main into feat/pms-197-audit-count-query-placeholder-family
    06bddef Merge pull request 'fix(schema): harden migrations, FKs, seed, and truncation (PMS-196)' (#210) from feat/pms-196-harden-migrations-rls-integrity into main
    ad47e38 Merge origin/main into feat/pms-196-harden-migrations-rls-integrity
    85fd769 Merge pull request 'fix(tickets): scope writes to tenant and validate FK references' (#209) from fix/pms-193-scope-tickets-writes-validate-fk into main
    3154fba ci: re-trigger Check (transient failure; green locally)
    bde8f81 Merge origin/main into fix/pms-193-scope-tickets-writes-validate-fk
    27fa80b Merge pull request 'fix(misc): correctness, concurrency, and test-reliability defects' (#208) from fix/pms-200-misc-correctness-concurrency-test-reliability into main
    7caf796 Merge origin/main into fix/pms-200-misc-correctness-concurrency-test-reliability
    a49cfbe Merge pull request 'fix(db,auth): dedupe migration prefix 041 and fix main compile' (#227) from fix/pms-293-dedupe-migration-prefix-041 into main
    2c9c7c3 fix(auth): return user: None in login_with_google mfa_required branch
    820cc4d fix(db): renumber portal_setup_tokens migration 041 to 042
    893f5d8 fix(auth): omit user profile in Google-callback mfa_required branch
    5b96470 Merge origin/main into fix/pms-200-misc-correctness-concurrency-test-reliability
    5946001 Merge pull request 'feat(portal): mint setup-link token + email on portal-access grant' (#204) from feat/pms-136-portal-setup-link into main
    c1b9663 Merge origin/main into feat/pms-136-portal-setup-link
    66367d5 Merge pull request 'fix(auth): close invite, signup, password, and validation defects' (#203) from fix/pms-192-invite-signup-password-validation into main
    52b32c5 Merge origin/main into fix/pms-192-invite-signup-password-validation
    8829844 Merge pull request 'fix(auth): close SSO/OIDC auth-strength and conformance gaps' (#202) from feat/pms-191-sso-oidc-security-gaps into main
    fbacbde Merge origin/main into feat/pms-191-sso-oidc-security-gaps
    3337ef3 Merge pull request 'fix(auth): close legacy PSA auth path security gaps' (#201) from fix/pms-190-legacy-auth-security-gaps into main
    7dd8b6a Merge origin/main into fix/pms-190-legacy-auth-security-gaps
    7e5fa28 fix(assets): allow 'deleted' in audit-log CHECK; add delete regression test
    269bf16 Merge pull request 'test(settings): fix stale billing route path in module-gating tests' (#225) from fix/pms-290-settings-stale-billing-path into main
    4c89552 fix(assets): wire configured AES key and harden vault crypto integrity
    a8589c0 test(settings): fix stale billing route path in module-gating tests
    f67057c feat(rls): activate RLS at runtime via split migrator/app DB roles
    8d564e0 Merge branch 'main' into chore/pms-201-remove-dead-stray-code
    6dfb9ee Merge pull request 'fix(startup): fail loud when configured SSO bootstrap fails' (#223) from fix/pms-289-sso-fail-loud-when-configured into main
    d79ab98 fix(startup): fail loud when configured SSO bootstrap fails
    86e40ab Merge pull request 'fix(tenants): seed lookup config for tenants reached off the PSA path' (#222) from feat/pms-288-seed-lookup-config-on-placement into main
    e0daef1 perf(tenants): cheap-guard the per-request sequence seed
    74adb16 fix(tenants): seed lookup config for tenants reached off the PSA path
    f75618f Merge pull request 'fix(tenants): set NOT-NULL kind in create_tenant; add regression test' (#221) from fix/pms-287-create-tenant-kind into main
    f7f8b7d fix(tenants): set NOT-NULL kind in create_tenant; add regression test
    8e2faad Merge pull request 'fix(startup): make migration failure fatal instead of warn-and-continue' (#219) from fix/pms-286-fatal-migrations into main
    b5ad162 Merge pull request 'feat(justfile): add dev-clean / dev-clean-all teardown recipes' (#214) from feat/dev-clean-recipes into main
    f82d9b3 Merge branch 'main' into feat/dev-clean-recipes
    398c0ba chore(cleanup): remove dead crates code and stray config/seed/test cruft
    a3333df fix(startup): make migration failure fatal instead of warn-and-continue
    842b18f chore(cleanup): remove dead and stray code from src/ tree
    303efb5 Merge pull request 'test(isolation): automate PMS-236 tenant-isolation QA across the API' (#216) from feat/pms-236-tenant-isolation-integration-test into main
    a3224a8 test(isolation): harden module-config and ticket_number checks
    f5520f7 fix: correct rmm, notifications, SLA, KB, portal, and contacts defects
    2c69eb9 test(isolation): automate PMS-236 tenant-isolation QA across the API
    6678777 test(settings): hit canonical /api/v1/invoices in module-gate tests
    5086c63 feat(justfile): add dev-clean / dev-clean-all teardown recipes
    9579f2b fix(contracts,billing): close financial-integrity gaps
    5f7c728 refactor: consolidate duplicated helpers, types, and test fixtures
    d68af1b fix(db): converge ticket filter builder; audit count_query placeholder family
    eaa150f fix(schema): harden migrations, FKs, seed, and truncation (PMS-196)
    0433815 fix(tickets): scope writes to tenant and validate FK references
    f69cc50 Merge pull request 'fix(rls): cover tenant-less tables; tenant-scope user_oauth_identities' (#207) from feat/pms-258-rls-cover-tenantless-tables into main
    eca763e Merge branch 'main' into feat/pms-258-rls-cover-tenantless-tables
    92bf278 fix(misc): correctness, concurrency, and test-reliability defects
    c224ffb fix(rls): cover tenant-less tables; tenant-scope user_oauth_identities
    3b508c1 Merge pull request 'refactor: converge duplicated subsystems and document architecture seams' (#206) from feat/pms-198-converge-subsystems into main
    f8029eb Merge branch 'main' into feat/pms-198-converge-subsystems
    a9ccf05 Merge pull request 'fix(audit): remove coarse per-request middleware that wrote unusable rows' (#198) from fix/pms-275-remove-coarse-audit-middleware into main
    c54ebe3 Merge branch 'main' into fix/pms-275-remove-coarse-audit-middleware
    1b55130 Merge pull request 'fix(ci): gate prod image push to main/tags; close CI coverage gaps' (#199) from fix/pms-189-ci-release-pipeline-drift into main
    9c0a400 Merge branch 'main' into fix/pms-189-ci-release-pipeline-drift
    0316824 refactor: converge duplicated subsystems and document architecture seams
    e7521d3 Merge pull request 'feat/pms-271-e2e-staging-prod-secrets' (#205) from feat/pms-271-e2e-staging-prod-secrets into main
    5807db8 fix(e2e): correct OIDC redirect_uri default to /auth/callback
    fb735aa refactor(e2e): name CI secrets after deployment variables
    0d33d0c feat(portal): mint setup-link token + email on portal-access grant
    2eb8977 fix(auth): close invite, signup, password, and validation defects
    222c649 Revert "fix(e2e): keep bare E2E_* as staging, add only E2E_PRODUCTION_"
    a4fef72 fix(auth): close SSO/OIDC auth-strength and conformance gaps
    d8e8eb0 fix(e2e): keep bare E2E_
    as staging, add only E2E_PRODUCTION_*
    ffb58e1 fix(auth): close legacy PSA auth path security gaps
    3221b6b Merge pull request 'feat(e2e): split CI env vars into staging/production secrets; manual prod opt-in' (#200) from feat/pms-271-e2e-staging-prod-secrets into main
    7788f76 feat(e2e): split CI env vars into staging/production secrets
    986a08e fix(ci): gate prod image push to main/tags; close CI coverage gaps
    6892bc4 fix(audit): remove coarse per-request middleware that wrote unusable rows
    730553a Merge pull request 'feat(migrations): no-op backfill + verification for co-mingled default-tenant rows' (#197) from feat/pms-263-comingled-backfill-verification into main
    2e65ee8 Merge branch 'main' into feat/pms-263-comingled-backfill-verification
    2e62ad8 Merge pull request 'test(isolation): per-user cross-isolation suite across modules' (#196) from feat/pms-264-per-user-isolation-suite into main
    8968aa0 feat(migrations): no-op backfill + verification for co-mingled default-tenant rows
    f71a7c6 test(isolation): per-user cross-isolation suite across modules
    a927e4e Merge pull request 'fix(workers): scope notification template lookup; audit from_trusted' (#195) from feat/pms-261-worker-from-trusted-audit into main
    2a9f970 fix(workers): scope notification template lookup; audit from_trusted
    6f54b2b Merge pull request 'refactor(domain): inject domains via env vars, never hardcode or fall back' (#192) from feat/pms-235-mokosh-domain-rebrand into main
    d155840 Merge branch 'main' into feat/pms-235-mokosh-domain-rebrand
    6cd85ca refactor(domain): inject all domains via env; drop hardcoded fallbacks
    2ef2fba Merge pull request 'fix(security): scope auth session methods to tenant; pin login-helper reachability' (#190) from feat/pms-260-cross-tenant-leak-fixes into main
    3c987d4 Merge branch 'main' into feat/pms-260-cross-tenant-leak-fixes
    4028957 Merge pull request 'feat(tenants): seed all editable lookups per tenant; reserve system-shared class' (#189) from feat/pms-259-seed-lookups-system-shared-class into main
    57804d1 fix(rls): renumber system-shared migration to 039; layer on PMS-257 fail-closed policy
    408d9bd Merge remote-tracking branch 'origin/main' into feat/pms-259-seed-lookups-system-shared-class
    100c59f Merge pull request 'feat(rls): flip tenant_isolation fail-closed with WITH CHECK and FORCE RLS' (#188) from feat/pms-257-rls-fail-closed into main
    6175e84 Merge branch 'main' into feat/pms-257-rls-fail-closed
    d8d2696 Merge pull request 'feat(tenant): remove the single-tenant cargo feature; no normal user lands in the default tenant' (#187) from feat/pms-262-remove-single-tenant-feature into main
    28e059f Merge branch 'main' into feat/pms-262-remove-single-tenant-feature
    50580b1 Merge pull request 'feat(db): route tenant-scoped service queries through begin_with_tenant' (#186) from feat/pms-256-rls-guc-plumbing into main
    c929e7c Merge branch 'main' into feat/pms-256-rls-guc-plumbing
    5da88eb Merge pull request 'docs(rls): per-user isolation reference and decomposition for PMS-255' (#185) from docs/pms-255-rls-isolation-plan into main
    18914ba Merge branch 'main' into docs/pms-255-rls-isolation-plan
    732427a Merge pull request 'chore: add pre-commit hook recipes' (#191) from chore/pre-commit-hooks into main
    22b4f59 Merge branch 'main' into chore/pre-commit-hooks
    106f983 Merge pull request 'fix(version): sanitise error bodies, warn on bad versions, test network path' (#194) from feat/pms-238-self-hosted-version-check into main
    fcf44f5 fix(version): sanitise error bodies, warn on bad versions, test network path
    35445fd Merge pull request 'feat(version): add self-hosted update check endpoint' (#193) from feat/pms-238-self-hosted-version-check into main
    551db74 feat(version): cache update-check result with a TTL
    e08a3ad feat(version): add self-hosted update check endpoint
    ebaa8d2 docs(domain): rebrand a8n.systems references to mokosh.systems (Bucket A)
    2a34180 chore: add install-hooks and pre-commit just recipes
    cbcfad6 fix(security): scope auth session methods to tenant; pin login-helper reachability
    78c5992 feat(tenants): seed all editable lookups per tenant; reserve system-shared class
    3476ed6 feat(rls): flip tenant_isolation fail-closed with WITH CHECK and FORCE RLS
    6969297 feat(tenant): remove the single-tenant cargo feature; no normal user lands in the default tenant
    bb76afb feat(db): route tenant-scoped service queries through begin_with_tenant
    864a0d8 docs(rls): per-user isolation reference and decomposition for PMS-255
    2f89fc7 Merge pull request 'feat(users): persist date_format_string preference on users (backend)' (#184) from feat/pms-253-date-format-preference-backend into main
    c57d5c6 feat(users): persist date_format_string preference on users (backend)
    a8116f3 Merge pull request 'chore(fmt): apply cargo fmt --all after PMS-244..PMS-249 sweep' (#183) from chore/pms-251-cargo-fmt-sweep into main
    990d585 chore(fmt): apply cargo fmt --all after PMS-244..PMS-249 sweep
    23b3259 Merge pull request 'test(auth): end-to-end placement tests for the bunyip login path (PMS-249)' (#182) from feat/mapps-249-bunyip-login-e2e into main
    9291524 test(auth): end-to-end placement tests for the bunyip login path (PMS-249)
    339e61b Merge pull request 'feat(invitations): email the invitee on invite create (PMS-246)' (#181) from feat/mapps-246-invite-email into main
    9d474ed Merge remote-tracking branch 'origin/main' into feat/mapps-246-invite-email
    85820e8 Merge pull request 'feat(auth): backfill default-tenant users off the shared tenant on login (PMS-245)' (#180) from feat/mapps-245-backfill-default-tenant-users into main
    9a752a6 feat(invitations): email the invitee on invite create (PMS-246)
    d8b05df feat(auth): backfill default-tenant users off the shared tenant on login (PMS-245)
    bf53e19 Merge pull request 'test(auth): pin the bunyip userinfo email_verified contract (PMS-248)' (#179) from chore/mapps-248-userinfo-email-verified-contract into main
    fd253a6 test(auth): pin the bunyip userinfo email_verified contract (PMS-248)
    f6c7d3f Merge pull request 'feat(invitations): tenant invite CRUD - Mokosh-owned org membership (PMS-244 phase 1)' (#178) from feat/mapps-244-invitations into main
    bd8ffd7 feat(auth): resolve SSO login to a tenant via Mokosh invites/self-signup (PMS-244, phase 2)
    b54b06c feat(invitations): tenant invite CRUD - Mokosh-owned org membership (PMS-244, phase 1)
    fa7724f Merge pull request 'feat(auth): lazily re-home default-tenant users to their org tenant (PMS-243)' (#177) from feat/mapps-243-rehome into main
    b78fa59 feat(auth): lazily re-home default-tenant users to their org tenant (PMS-243)
    59618ac Merge pull request 'feat(auth): resolve SSO users to a per-org tenant via the Bunyip org claim (PMS-240)' (#175) from feat/mapps-240-bunyip-org-tenant into main
    6b8ed51 feat(auth): resolve SSO users to a per-org tenant via the Bunyip org claim (PMS-240)
    bfb815f Merge pull request 'fix(seed): never auto-seed the shared Bunyip landing tenant (PMS-239)' (#174) from fix/mapps-239-demo-seed-shared-tenant into main
    18c60ef fix(seed): never auto-seed the shared Bunyip landing tenant (PMS-239)
    b2cf1b3 Merge pull request 'chore: cargo fmt --all after the PMS-139 TenantId sweep' (#173) from chore/cargo-fmt-after-pms-139-sweep into main
    c441b1c chore: cargo fmt --all after the PMS-139 TenantId sweep
    c7badab Merge pull request 'feat(auth): migrate the tickets hub to TenantId, completing PMS-139 (sweep batch 8)' (#171) from feat/pms-139-sweep-tickets into main
    17611c3 feat(auth): migrate the tickets hub to TenantId, completing PMS-139 (sweep batch 8)
    1ac0d22 Merge pull request 'feat(auth): migrate the notifications hub to TenantId (PMS-139 sweep batch 7)' (#169) from feat/pms-139-sweep-notifications into main
    d5c6133 feat(auth): migrate the notifications hub to TenantId (PMS-139 sweep batch 7)
    b4303f5 Merge pull request 'feat(auth): migrate the audit hub to TenantId (PMS-139 sweep batch 6)' (#167) from feat/pms-139-sweep-hubs into main
    f607764 feat(auth): migrate the audit hub to TenantId (PMS-139 sweep batch 6)
    23e36ba Merge pull request 'feat(auth): migrate contacts + billing to TenantId (PMS-139 sweep batch 5)' (#164) from feat/pms-139-sweep-contacts-billing into main
    3deb53e feat(auth): migrate contacts + billing to TenantId (PMS-139 sweep batch 5)
    e2a6de3 Merge pull request 'feat(auth): migrate knowledge_base + tenants + settings to TenantId (PMS-139 sweep batch 4)' (#163) from feat/pms-139-sweep-kb-tenants-settings into main
    0a1006d feat(auth): migrate knowledge_base + tenants + settings to TenantId (PMS-139 sweep batch 4)
    85e08a2 Merge pull request 'feat(auth): migrate calendar + sla + contracts to TenantId (PMS-139 sweep batch 3)' (#161) from feat/pms-139-sweep-calendar-sla-contracts into main
    b77e671 feat(auth): migrate calendar + sla + contracts to TenantId (PMS-139 sweep batch 3)
    c6b6adb Merge pull request 'feat(auth): migrate assets + projects to TenantId (PMS-139 sweep batch 2)' (#160) from feat/pms-139-sweep-assets-projects into main
    ee26720 feat(auth): migrate assets + projects to TenantId (PMS-139 sweep batch 2)
    622f3f4 Merge pull request 'feat(auth): migrate rmm + time_tracking to TenantId (PMS-139 sweep batch 1)' (#159) from feat/pms-139-sweep-rmm-time into main
    961251c feat(auth): migrate rmm + time_tracking to TenantId (PMS-139 sweep batch 1)
    1091d76 Merge pull request 'feat(auth): typed TenantId scoping foundation + reports migration (PMS-139)' (#158) from feat/pms-139-tenant-id-foundation into main
    2cc6511 feat(auth): typed TenantId scoping foundation + reports migration (PMS-139)
    a497e81 Merge pull request 'feat(timesheets): draft state + withdraw endpoint (PMS-183)' (#157) from feat/pms-183-timesheet-withdraw into main
    e75bdf9 feat(timesheets): draft state + withdraw endpoint for timesheets (PMS-183)
    45f51fc Merge pull request 'feat(audit): surface field-level before/after content in change history (PMS-204)' (#156) from feat/pms-204-history-content into main
    60f0e4f feat(audit): surface field-level before/after content in change history (PMS-204)
    9acd44a Merge pull request 'feat(projects): record task and project edits in the audit trail (PMS-184)' (#155) from feat/pms-184-task-edit-history into main
    6b20c9f Merge pull request 'feat(audit): per-record change-history endpoint (PMS-182)' (#154) from feat/pms-182-ticket-edit-history into main
    f123889 feat(projects): record task and project edits in the audit trail (PMS-184)
    110f532 feat(audit): per-record change-history endpoint for detail pages (PMS-182)
    caacdc9 Merge pull request 'docs: add 2026-06-06 platform audit to dev-docs/audits' (#153) from docs/add-platform-audit-2026-06-06 into main
    455e9cc docs: add 2026-06-06 platform audit to dev-docs/audits
    9cd4103 Merge pull request 'feat(billing): also surface invoice_number on payment responses (PMS-186)' (#150) from feat/pms-186-billing-company-name into main
    11bcb0e feat(billing): also surface invoice_number on payment responses (PMS-186)
    e450f5b Merge pull request 'feat(billing): include company_name on invoice and payment responses (PMS-186)' (#149) from feat/pms-186-billing-company-name into main
    6bbe7ff feat(billing): include company_name on invoice and payment responses (PMS-186)
    c32987c Merge pull request 'feat(reports): add safe custom report builder (PMS-180)' (#148) from feat/pms-180-custom-report-builder into main
    cc0023b feat(reports): add safe custom report builder (PMS-180)
    4bc54e0 Merge pull request 'feat(reports): add project + client report types end to end (PMS-179)' (#147) from feat/pms-179-project-client-reports into main
    148d3f6 feat(reports): add project + client report types end to end (PMS-179)
    92e77ea Merge pull request 'feat(seed): seed demo data on a new account's first visit (PMS-157)' (#146) from feat/pms-157-new-account-seeding into main
    b6f6ba1 fix(seed): make demo ticket seeding best-effort; document E2E kill-switch
    909dc07 feat(seed): seed demo data on a new account's first visit (PMS-157)
    aa6118f Merge pull request 'fix(contacts): 400 not 500 deleting a company with related records (PMS-170)' (#145) from fix/pms-170-company-delete-fk-400 into main
    7e13835 fix(contacts): return 400 not 500 when deleting a company with related records
    a86e802 Merge pull request 'fix(audit): correct SQL placeholder numbering in audit-log list (PMS-178)' (#144) from fix/pms-178-audit-list-placeholders into main
    7acb419 fix(audit): correct SQL placeholder numbering in audit-log list query
    ce0dc98 Merge pull request 'docs(e2e): spell out how to set the E2E account's admin role (SQL + bootstrap)' (#143) from docs/pms-155-e2e-admin-account into main
    d436054 Merge pull request 'feat(auth): translate Bunyip bunyip_role claim to mokosh role (PMS-172)' (#142) from feat/pms-172-bunyip-role-translation into main
    e950947 refactor(auth): clarify authz source + log Bunyip role transitions (PMS-172 review)
    8378289 feat(auth): translate Bunyip bunyip_role claim to mokosh role on the RS path
    0e3285b Merge pull request 'docs(e2e): how to set the E2E account admin role (SQL + bootstrap)' (#141) from docs/pms-155-e2e-admin-howto into main
    67c9cb8 docs(e2e): spell out how to set the E2E account's admin role (SQL + bootstrap)
    abd36b9 docs(e2e): spell out how to set the E2E account's admin role (SQL + bootstrap)
    3fcd925 Merge pull request 'docs(e2e): require admin role for the E2E account (PMS-155)' (#140) from docs/pms-155-e2e-admin-account into main
    e025056 docs(e2e): require admin role for the E2E account (PMS-155)
    f636e04 Merge pull request 'test(e2e): add API CRUD specs for time-tracking, projects, billing, contracts (PMS-155)' (#136) from feat/pms-155-e2e-core-modules into main
    8f878dd test(e2e): add API specs for RMM and the dispatch board (PMS-155 batch 4)
    007ba8b test(e2e): add API specs for notifications, settings, audit, reports (PMS-155 batch 3)
    fcf367f test(e2e): add API CRUD specs for calendar, SLA, assets, knowledge-base (PMS-155 batch 2)
    9aaeac6 test(e2e): make PMS-155 spec cleanup resilient to a single delete failure
    802a291 test(e2e): add API CRUD specs for time-tracking, projects, billing, contracts (PMS-155)
    c1dab9c Merge pull request 'fix(e2e): tolerate 405 on ticket delete until staging deploys PMS-149 route' (#135) from fix/pms-149-e2e-deploy-tolerant-ticket-delete into main
    289e772 fix(e2e): tolerate 405 on ticket delete until staging deploys PMS-149 route
    ba39edd Merge pull request 'feat(tickets): add DELETE /tickets/{id} so E2E cleans up its company (PMS-149)' (#133) from feat/pms-149-ticket-delete-route into main
    451fa6d fix(tickets): map ticket-delete FK violation to 400, not 500
    8a2e442 feat(tickets): add DELETE /tickets/{id} so E2E cleans up its company
    5cc3581 Merge pull request 'feat(e2e): un-fixme OIDC token-flow via replayed OP cookies (PMS-143)' (#132) from feat/pms-143-oidc-unfixme into main
    0de7285 chore(e2e): surface bunyip body on OIDC authorize non-3xx
    868658c fix(e2e): skip parent-domain derivation for IP literals
    0368bff feat(e2e): un-fixme OIDC token-flow via replayed OP cookies (PMS-143)
    853f9a1 Merge pull request 'test(e2e): re-fixme auth-ui pending PMS-148 login race' (#131) from fix/pms-148-refixme-auth-ui into main
    82418fc test(e2e): re-fixme auth-ui pending PMS-148 login race
    3e34835 Merge pull request 'feat(e2e): un-fixme auth-ui login + logout round-trip (PMS-142 v2)' (#130) from feat/pms-142-auth-ui-unfixme-v2 into main
    9b3cfe1 feat(e2e): un-fixme auth-ui login + logout round-trip (PMS-142 v2)
    5f850e7 Merge pull request 'chore(build): bump rust-builder-glibc to v1.0.1' (#127) from chore/bump-rust-builder-glibc-v1.0.1 into main
    0b7e68e Merge branch 'main' into chore/bump-rust-builder-glibc-v1.0.1
    5b184d2 Merge pull request 'feat(reports): GET /reports registry + integration tests (PMS-146)' (#129) from feat/pms-146-reports-registry-and-tests into main
    73e8682 feat(reports): add GET /reports registry + integration tests (PMS-146, closes PMS-93 gaps)
    cc4dcbd Merge pull request 'feat(assets): audited secret reveal, no plaintext in lists, tests (PMS-71)' (#128) from feat/pms-71-asset-secret-reveal-and-tests into main
    10b3760 feat(assets): stop leaking secrets in lists; add audited reveal endpoints + tests (PMS-71)
    78672ae chore(build): bump rust-builder-glibc to v1.0.1
    aac7c96 Merge pull request 'feat(projects): budget-vs-actual + time rollup + tests (PMS-51)' (#126) from feat/pms-51-project-actuals-and-tests into main
    5dc8acd feat(projects): compute budget-vs-actual, roll approved time into actuals, add tests (PMS-51)
    56e0a18 Merge pull request 'feat(portal): wire company-scoped invoices + add portal isolation tests (PMS-25)' (#125) from feat/pms-25-portal-invoices-and-tests into main
    7e0f275 feat(portal): wire company-scoped invoices + add portal isolation tests (PMS-25)
    bcf6f01 Merge pull request 'chore: Sync .gitignore' (#124) from chore/sync-gitignore into main
    86bbeca chore: Sync .gitignore
    70e2ae4 Merge pull request 'chore(e2e): capture access_token from /oauth2/token response, not just Bearer header' (#123) from chore/e2e-capture-spa-build-hash into main
    160688d chore(e2e): capture access_token from /oauth2/token response, not just Bearer header
    7619ecb Merge pull request 'chore(e2e): surface mokosh-apps SPA bundle hash in timeout diagnostic' (#122) from chore/e2e-capture-spa-build-hash into main
    cdf46ce chore(e2e): surface mokosh-apps SPA bundle hash in timeout diagnostic
    63ab9c1 Merge pull request 'fix(auth): JIT placeholder name no longer surfaces UUID fragments' (#121) from fix/jit-fallback-name-not-uuid-fragments into main
    37fcc9c fix(auth): JIT placeholder name no longer surfaces UUID fragments
    321520a Merge pull request 'feat(kb): one vote per user account for article ratings (toggle/switch)' (#120) from feat/kb-rating-per-user-votes into main
    f2e670f style: cargo fmt
    aeb0e56 feat(kb): make article ratings one vote per user account
    7a19fb6 Merge pull request 'feat(billing): generate recurring invoices from contract items (PMS-64 AC5)' (#119) from feat/pms64-recurring-invoicing into main
    a58c1db Merge branch 'main' into feat/pms64-recurring-invoicing
    4a4ec49 Merge pull request 'feat(kb): serve portal KB feed only via the portal-authed route' (#118) from feat/portal-kb-feed-auth into main
    bb0d8d2 Merge branch 'main' into feat/portal-kb-feed-auth
    98ec545 Merge pull request 'feat(notifications): backfill default rules for pre-existing tenants' (#117) from feat/notifications-seed-existing-tenants into main
    0f837be style: apply cargo fmt
    a1747d4 style: apply cargo fmt
    bd72ab6 style: apply cargo fmt
    e266dca feat(billing): generate recurring invoices from contracts (PMS-64 AC5)
    de3ebff feat(kb): serve portal KB feed only via portal-authed route
    d03ff7f feat(notifications): backfill default worker rules for existing tenants
    0c620dc Merge pull request 'fix(api): repair list ORDER BY misuse + time-entries count placeholders (PMS-145)' (#116) from fix/pms-145-time-entries-count-placeholders into main
    df0ce4b fix(api): repair list ORDER BY misuse + time-entries count placeholders
    66a1da9 Merge pull request 'fix(ci): cap integration-test parallelism to dodge sqlx-test pool timeout' (#115) from fix/check-pg-pool-saturation into main
    d531f38 Merge pull request 'feat(e2e): close PMS-142 - un-fixme the auth-ui login + logout round-trip' (#112) from feat/pms-142-auth-ui-unfixme into main
    eea87f3 fix(ci): cap integration-test parallelism to dodge sqlx-test pool timeout
    02c30e0 Merge pull request 'fix(billing): gate invoicing on timesheet approval (PMS-144)' (#114) from fix/pms-144-billing-approval-gate into main
    4bd93f4 fix(billing): gate invoicing on timesheet approval (ready_to_bill)
    d9abe84 Merge pull request 'feat(dev-sso): validate bunyip-issued tokens as a Resource Server' (#113) from feat/dev-sso-rs-trust-bunyip into main
    b6d9619 test(e2e): re-quarantine auth-ui - bunyip /logout does not kill OP session
    ce25821 feat(dev-sso): validate bunyip-issued tokens as a Resource Server
    8c50b19 test(e2e): tighten page-diagnostics API + use Error cause for original stack
    826e5b4 feat(e2e): close PMS-142 - un-fixme the auth-ui login + logout round-trip
    c94e090 Merge pull request 'feat(ci): close PMS-141 - promote E2E to merge gate on PRs to main' (#111) from feat/pms-141-e2e-merge-gate into main
    ae35028 feat(ci): close PMS-141 - promote E2E suite to merge gate on PRs to main
    b60a8d3 Merge pull request 'test(e2e): quarantine OIDC token-flow test - needs OP session cookie' (#110) from fix/e2e-oidc-fixme into main
    bfa3945 test(e2e): quarantine OIDC token-flow test - needs OP session cookie
    cab7078 Merge pull request 'fix(e2e): point OIDC test at the OP host, quarantine auth-ui round-trip' (#109) from fix/e2e-oidc-op-base-url-and-auth-ui-fixme into main
    dc5019e docs(e2e): refresh stale comments + README harness description
    718e6bb fix(e2e): point OIDC test at the OP host, quarantine auth-ui round-trip
    d9d3084 Merge pull request 'fix(e2e): capture bearer from ANY host, not just /api/v1' (#108) from fix/e2e-capture-bearer-any-host into main
    8a69f3f fix(e2e): capture bearer from ANY host, not just /api/v1
    95637bd Merge pull request 'fix(ci): pass E2E_TOTP_SECRET secret into the E2E job env' (#107) from fix/e2e-wire-totp-secret into main
    5bcaf8d fix(ci): pass E2E_TOTP_SECRET secret into the E2E job env
    319f8d2 Merge pull request 'feat(e2e): enter TOTP second factor during SPA login' (#106) from feat/e2e-totp-handling into main
    1a9e37e feat(e2e): enter TOTP second factor during SPA login
    4d46bf6 Merge pull request 'fix(e2e): treat /login/* as still IN the login flow' (#105) from fix/e2e-login-pathname-anchor into main
    88c274b fix(e2e): treat /login/* (e.g. /login/2fa) as still IN the login flow
    a90fbfb Merge pull request 'test(e2e): dump full URL/trail/request diagnostic when setup times out' (#104) from fix/e2e-bearer-diagnostics into main
    b30dc74 test(e2e): dump full URL/trail/request diagnostic when setup times out
    a383fb1 Merge pull request 'fix(e2e): probe data routes for bearer, merge auth-ui to dodge rate limit' (#103) from fix/e2e-spa-bearer-intercept-and-logout into main
    66e3433 test(e2e): harden auth-ui locators with .first(), tighten /api/v1 filter
    de17374 fix(e2e): force a post-login probe + merge auth-ui tests to dodge rate limit
    29d2eb8 Merge pull request 'fix(e2e): intercept SPA bearer for setup, click User-menu -> Logout' (#102) from fix/e2e-spa-bearer-intercept-and-logout into main
    30d66a9 test(e2e): refresh auth-state comment, drop dead routes.authLogin
    328ffbc fix(e2e): intercept SPA bearer for setup, click User-menu -> Logout
    4b01eef Merge pull request 'fix(ci): wait-for-deploy refetches missing trees, skips gate if unresolvable' (#101) from fix/e2e-wait-deploy-skip-when-unresolvable into main
    9c73dd1 fix(ci): wait-for-deploy refetches missing trees, skips gate if unresolvable
    6bf0e9a Merge pull request 'fix(ci): wait-for-deploy unshallows the clone when git log returns empty' (#100) from fix/e2e-wait-deploy-unshallow into main
    efea598 fix(ci): wait-for-deploy unshallows the clone when git log returns empty
    d47d72a Merge pull request 'fix(ci): resolve deploy-sync expected SHA to the last build-relevant commit' (#99) from fix/e2e-runner-toolcache into main
    6040838 fix(ci): resolve deploy-sync expected SHA to the last build-relevant commit
    8924692 Merge pull request 'test(e2e): aggregate-all-missing env vars in a preflight setup project' (#98) from fix/e2e-runner-toolcache into main
    104ed55 test(e2e): aggregate-all-missing env vars in a preflight setup project
    99738ad Merge pull request 'fix(ci): bake MOKOSH_GIT_HASH + describe + date into OCI build' (#97) from fix/e2e-runner-toolcache into main
    52742c6 fix(ci): bake MOKOSH_GIT_HASH + describe + date into OCI build
    256d17f Merge pull request 'fix(ci): API host is api.msp., not msp-api.' (#96) from fix/e2e-runner-toolcache into main
    3adb4a2 fix(ci): API host is api.msp., not msp-api.
    9d8b11a Merge pull request 'fix(ci): split E2E SPA host from API host, derive msp-api.' (#95) from fix/e2e-runner-toolcache into main
    3883fc8 fix(ci): split E2E SPA host from API host, derive msp-api.
    80a160e Merge pull request 'fix(ci): treat empty E2E_BASE_URL as missing, not as override' (#94) from fix/e2e-runner-toolcache into main
    0de6163 fix(ci): treat empty E2E_BASE_URL as missing, not as override
    e965450 Merge pull request 'fix(ci): drop playwright --with-deps - opensuse runner has no apt-get' (#93) from fix/e2e-runner-toolcache into main
    21ca57f fix(ci): drop playwright --with-deps - opensuse runner has no apt-get
    78ed118 Merge pull request 'fix(ci): hardcode RUNNER_TOOL_CACHE path - runner context unbound in job env' (#92) from fix/e2e-runner-toolcache into main
    020631b fix(ci): hardcode RUNNER_TOOL_CACHE path - runner context unbound in job env
    3f8c2a4 Merge pull request 'fix(ci): point E2E setup-node tool cache at RUNNER_TEMP' (#91) from fix/e2e-runner-toolcache into main
    55eb53c fix(ci): point E2E setup-node tool cache at RUNNER_TEMP
    aadb3bb Merge pull request 'PMS-140: Add Playwright E2E suite against staging, run on every merge to main' (#90) from feat/pms-140-e2e-playwright into main
    01a4d7d test(e2e): fix auth path, require OIDC redirect, harden teardown and CI
    b4ccc9a test(e2e): add Playwright suite against staging, run post-merge to main
    0c39d1e Merge pull request 'feat(auth): close PMS-138 - subdomain-driven tenant hint on login + forgot-password + Google JIT' (#89) from feat/pms-138-closeout into main
    c7d40ed feat(auth): close PMS-138 - subdomain-driven tenant hint on login + forgot-password + Google JIT
    4b4c1f4 Merge pull request 'fix(settings): drop legacy DELETE /settings/{id} - matchit conflict with /settings/{category}' (#88) from fix/settings-route-conflict into main
    a0d3d91 fix(settings): drop legacy DELETE /settings/{id} - matchit conflict with /settings/{category}
    934f96a Merge pull request 'feat(settings): close PMS-113 - unify module-config, add category endpoints + value validation, gate optional modules at runtime' (#87) from feat/pms-113-closeout into main
    606ba79 feat(settings): close PMS-113 - unify module-config, add category endpoints + value validation, gate optional modules at runtime
    63a1bfc Merge pull request 'feat(tenants): close PMS-21 - cfg-gate cleanup + module-config and cross-tenant tests' (#86) from feat/pms-21-closeout into main
    272d179 feat(tenants): close PMS-21 - cfg-gate cleanup + module-config and cross-tenant tests
    7a34463 Merge pull request 'fix(list-queries): correct WHERE param numbering for count_query in 6 list_* methods' (#85) from fix/list-count-query-param-mismatch into main
    6acbb23 fix(list-queries): correct WHERE param numbering for count_query in 6 list_* methods
    68580e0 Merge pull request 'fix(migrations): rename PMS-4 recovery-codes migration 027 -> 029 (version collision with PMS-106)' (#84) from fix/migration-027-collision into main
    7edbf13 fix(migrations): rename PMS-4 recovery-codes migration 027 -> 029 (version collision with PMS-106)
    4d995d4 Merge pull request 'feat(auth): close PMS-4 - list filter, layered rate limit, MFA recovery codes, tenant hardening' (#83) from feat/pms-4-closeout into main
    387e18d Merge pull request 'feat(calendar): appointment reminders worker (PMS-58)' (#81) from feat/pms-58-calendar-reminders into main
    e922e0f Merge branch 'main' into feat/pms-58-calendar-reminders
    d00c3d0 Merge remote-tracking branch 'origin/main' into feat/pms-58-calendar-reminders
    6a0e444 Merge pull request 'feat(audit): record billing + contracts mutations in the audit log (PMS-117)' (#82) from feat/pms-117-audit-hooks into main
    be05264 Merge branch 'main' into feat/pms-117-audit-hooks
    ef0ffbf feat(audit): record billing + contracts mutations in the audit log (PMS-117)
    35704a5 Merge pull request 'feat(sla): at-risk + breach notifications via sweep worker (PMS-106)' (#80) from feat/pms-106-sla-notify into main
    1d020fb feat(calendar): appointment reminders worker (PMS-58)
    d25444b feat(auth): close PMS-4 - list filter, layered rate limit, MFA recovery codes, tenant hardening
    e1244a0 feat(sla): at-risk + breach notifications via sweep worker (PMS-106)
    242360a Merge pull request 'feat(calendar): RRULE expansion + dispatch view (PMS-58)' (#77) from feat/pms-58-calendar into main
    8874be8 Merge branch 'main' into feat/pms-58-calendar
    bdcb4d5 Merge pull request 'feat(contracts): hour-balance consumption, rate resolution, lifecycle (PMS-64)' (#75) from feat/pms-64-contracts into main
    0fd9124 Merge remote-tracking branch 'origin/main' into feat/pms-58-calendar
    39950bf Merge remote-tracking branch 'origin/main' into feat/pms-64-contracts
    269c79d Merge pull request 'feat(kb): publish lifecycle, slug uniqueness, version restore, trigram search, feedback, portal feed (PMS-79)' (#78) from feat/pms-79-kb into main
    89fd6fd Merge branch 'main' into feat/pms-79-kb
    dbf8c91 feat(kb): publish lifecycle, slug uniqueness, version restore, trigram search, feedback, portal feed (PMS-79)
    f1d7e99 Merge pull request 'feat(billing): generate invoices from billable time entries (PMS-33)' (#76) from feat/pms-33-billing into main
    c7884b1 Merge branch 'main' into feat/pms-33-billing
    025f6d3 Merge pull request 'feat(sla): business-hours-aware SLA clock engine (PMS-106)' (#72) from feat/pms-106-sla-clock into main
    bc9e86a Merge branch 'main' into feat/pms-106-sla-clock
    1cff709 Merge pull request 'feat(audit): record entity mutations + auth events (PMS-117)' (#70) from feat/pms-117-audit-log into main
    d5e9b1b feat(calendar): RRULE expansion + dispatch view (PMS-58)
    ba69f11 Merge remote-tracking branch 'origin/main' into feat/pms-64-contracts
    d807030 Merge remote-tracking branch 'origin/main' into feat/pms-106-sla-clock
    f7f08c1 Merge remote-tracking branch 'origin/main' into feat/pms-117-audit-log
    7396d1b feat(billing): generate invoices from billable time entries (PMS-33)
    9c14ffa feat(contracts): hour-balance consumption, rate resolution, lifecycle (PMS-64)
    a3ebcd0 Merge pull request 'test(pms-11): cover assign + assert JOINed ticket DTO fields (F3)' (#73) from feat/pms-11-tickets-assign-joined-fields-test into main
    fca3248 Merge pull request 'fix(contacts): make update_company and update_contact persist every UpdateRequest field' (#74) from feat/pms-17-tests into main
    ef5af80 test(pms-11): cover assign + assert JOINed ticket DTO fields (F3)
    3bbf1e5 fix(contacts): make update_company and update_contact persist every Update
    Request field
    6bd23e4 feat(sla): business-hours-aware SLA clock engine (PMS-106)
    e489d7a Merge pull request 'test(pms-17): integration tests pinning F4 fix + site/contact CRUD + portal flag + F9 filter' (#71) from feat/pms-17-tests into main
    e1aec1a test(pms-17): integration tests pinning F4 fix + site/contact CRUD + portal flag + F9 filter
    708d322 feat(audit): record entity mutations + auth events in the audit log
    a305bca Merge pull request 'feat(scheduler): shared background-job runner (PMS-135)' (#69) from feat/pms-135-scheduler into main
    3895e17 fix(scheduler): address code-review findings on shared scheduler (PMS-135)
    887dbd7 feat(scheduler): shared background-job runner (PMS-135)
    0d4c473 Merge pull request 'feat(api): readiness probe at /api/v1/ready (PMS-130)' (#68) from feat/pms-130-readiness into main
    bbe9855 fix(api): address code-review findings on /ready (PMS-130)
    6174aa4 feat(api): readiness probe at /api/v1/ready (PMS-130)
    69364fc Merge pull request 'feat(types): extract shared DTOs into mokosh-types workspace crate (PMS-129)' (#67) from feat/pms-129-mokosh-types into main
    99ca90e fix(types): add serde_json dep + refresh Cargo.lock for new crate (PMS-129)
    02fa99f fix(types): replace AppError reference with crate-local AuthRequired marker (PMS-129)
    5454f62 feat(types): extract shared DTOs into a mokosh-types workspace crate (PMS-129)
    e24ff59 Merge pull request 'feat(migrations): split 1688-line initial schema into per-feature files (PMS-128)' (#66) from feat/pms-128-migration-split into main
    813c1c1 feat(migrations): split 1688-line initial schema into per-feature files (PMS-128)
    666913f Merge pull request 'feat(api): paginate every list handler (PMS-127)' (#65) from feat/pms-127-pagination into main
    a03bfff docs(pagination): clarify audit-log cap drop + dual-Query convention
    91e1a40 feat(api): route every list handler through utils/pagination (PMS-127)
    46a45a1 Merge pull request 'feat(tracing): named instrument spans on every service method (PMS-126)' (#64) from feat/pms-126-tracing-spans into main
    529e778 feat(tracing): add named instrument spans on every service method (PMS-126)
    6c80f4c Merge pull request 'feat(router): self-documenting 501 body for placeholder modules (PMS-125)' (#63) from feat/pms-125-stub-bodies into main
    d5044c0 style: allow dead_code on common::{seed_admin,login} for unauthenticated test binaries
    d8ae8af style: rewrap assert! to satisfy rustfmt in dispatch_stub test
    0940a9d style: reflow long lines for rustfmt in PMS-125 stub
    f421318 feat(router): self-documenting 501 body for placeholder modules (PMS-125)
    bf5c42a Merge pull request 'feat(rmm): device-sync worker, provider trait, suppression + dedupe (PMS-103)' (#62) from feat/pms-103-rmm-sync-worker into main
    0dfea6d style: drop unused RmmService accessor methods
    6aefab3 style: apply rustfmt to PMS-103 rmm sync worker work
    1840992 feat(rmm): sync worker, provider trait, prefs/dedupe, encryption key wire-up
    9889f59 Merge pull request 'feat(notifications): dispatcher worker, prefs, encryption (PMS-92)' (#61) from feat/pms-92-notifications-dispatcher into main
    09e5435 style(tests): factor notifications row tuple into NotifRow alias
    4f43203 style: blank line between bullet list and paragraph in dispatch doc
    76a54d7 style: apply rustfmt to PMS-92 notifications dispatcher work
    d3717d4 feat(notifications): dispatcher worker, preference enforcement, encryption key wire-up
    98d3aa7 Merge pull request 'fix(auth): seed first_name/last_name defaults on bunyip JIT user upsert' (#60) from feat/jit-user-name-defaults into main
    7dceaf6 fix(auth): seed first_name/last_name defaults on bunyip JIT user upsert
    d775285 Merge pull request 'feat(contacts): populate CompanyResponse rollup fields' (#59) from feat/crm-ops into main
    abc2bc4 feat(contacts): populate CompanyResponse rollup fields
    7747fb4 Merge pull request 'feat(time-tracking): rounding, rate derivation, timesheet approval, tenant scoping' (#58) from feat/time-tracking-resolve-rounding into main
    d0acbb5 feat(time-tracking): apply rounding, derive rates, add timesheet approval + tenant scoping
    248e135 Merge pull request 'fix/dev-sso-private-network-name' (#57) from fix/dev-sso-private-network-name into main
    dd34ae9 fix(dev): drop server's depends_on infisical so compose project is valid
    0aaa5f2 fix(dev-sso): match overlay private network name to base per-user name
    87cac87 Merge pull request 'style(tests): rustfmt wrap on hash_password call' (#56) from fix/pms-124-fmt-wrap into main
    81a294c fix(tickets): cast sla_targets DECIMAL columns to float8 for sqlx decode
    fb96025 test(common): init tracing subscriber so the real sqlx error surfaces
    2de1bd8 fix(tests): send custom_fields: {} on create ticket, surface error body
    81487c8 fix(ci): point DATABASE_URL at the postgres service name, not localhost
    c4727dc style(tests): allow dead_code on per-binary-unused common items
    e56e42a style(tests): rustfmt wrap on hash_password call
    cc5f7c3 Merge pull request 'feat(tests): module-level integration tests for real route groups (F10)' (#55) from feat/pms-124-integration-tests into main
    806312f fix(tests): authenticate via Authorization: Bearer, not a cookie jar
    d172c15 feat(tests): module-level integration tests for real route groups (F10)
    3b62355 Merge pull request 'feat(api): add length validation to all Filter query types (F9)' (#54) from feat/pms-123-filter-validate into main
    7b67a7a docs(kb): note ILIKE cap rationale on KbArticleFilter.q
    b7c612f feat(api): add length validation to all Filter query types (F9)
    53450bf Merge pull request 'style(auth): cargo fmt oidc_rs.rs' (#53) from fix/oidc-rs-fmt into main
    9c3dffd style(auth): cargo fmt oidc_rs.rs
    52a790d Merge pull request 'feat/bunyip-op-cutover' (#52) from feat/bunyip-op-cutover into main
    cae15d2 feat(auth): wire bunyip RS verifier into AuthMiddleware
    cf2ff21 feat(auth): implement Resource-Server at+jwt verifier
    8131438 feat(auth): scaffold Resource-Server OIDC verifier (bunyip-as-OP cutover)
    d351093 Merge pull request 'feat/fix-just-dev-wont-start-infisical' (#51) from feat/fix-just-dev-wont-start-infisical into main
    de6da3a rewrite comment for dev-down and dev-clean
    d286b3a fix comment
    ef1649a add start infisical recipe
    0b3727c add tag
    eabf8e4 Merge pull request 'fix(ci): generate release notes from commits since previous tag' (#50) from fix/release-notes-since-prev-tag into main
    dd7f0fd fix(ci): generate release notes from commits since previous tag
    5b5eef0 Merge pull request 'fix/db-migrations-in-app' (#49) from fix/db-migrations-in-app into main
    0c9a066 WIP: Build on every push
    3d2b51a WIP: Build on every push
    82b3044 WIP: Build on every push
    562f8e4 fix(migrations): move mokosh_auth schema/columns into app migrations
    9b551af Merge pull request 'PSA audit: auth + tenancy security fixes' (#48) from feat/m1-vas into main
    dc202a9 fix(test): bind reset-token strings before borrowing (E0716)
    1b2568e fix(router): use api.msp host prefix in not_a_frontend fallback
    033b6bc style: cargo fmt --all
    92c485e test(auth): unit-test reset-token parsing + super-admin email allowlist
    ddaaeeb ci: publish the server image as mokosh-server (single name)
    6f406d3 ci: publish the server image as both mokosh-server and mokosh-api
    71e65ce fix(db): parameterize RLS tenant GUC; drop ineffective with_tenant
    1d5ac98 feat(auth): harden Google OAuth (verified-email link, email allowlist, fail-closed)
    5e0af57 fix(tenancy): validate cross-tenant foreign keys on create/update
    bd1d794 fix(auth): bind reset tokens to user; gate sign-in on tenant status
    6c50a88 Merge pull request 'fix(dev-sso): route Traefik labels on Compose 2.40 + shared Traefik' (#47) from fix/dev-sso-traefik-label-interpolation into main
    d13ba40 fix(dev-sso): route Traefik labels correctly on Compose 2.40 + shared Traefik
    c170baa Merge pull request 'docs: quickstart for fresh clones + CLAUDE.md' (#46) from docs/quickstart into main
    a05771a docs: add quickstart for fresh clones, plus CLAUDE.md
    e1ddf6f Merge pull request 'fix(ci): trigger image build on crates/
    edits + add manual dispatch' (#45) from fix/ci-trigger-crates into main
    6cb9513 fix(ci): trigger image build on crates/** edits + add manual dispatch
    3fdc0ef Merge pull request 'feat(ci): use $RUNNER_TEMP instead of /tmp in claude-fix workflow' (#44) from feat/pms-132-no-tmp into main
    bcd0990 feat(ci): guard against unset RUNNER_TEMP before using it
    54f1941 feat(ci): use $RUNNER_TEMP instead of /tmp in claude-fix workflow
    11fc65b Merge pull request 'fix(clippy): use ok_or instead of ok_or_else for plain HttpError values' (#43) from fix/clippy-sessions-ok-or into main
    1f83ca5 fix(clippy): use ok_or instead of ok_or_else for plain HttpError values
    d2e9177 Merge pull request 'Implementing SSO, OIDC (All auth)' (#42) from feature-sso into main
    09751b3 Merge remote-tracking branch 'origin/main' into feature-sso
    ce3f90f feat(oidc): fan back-channel logout out on RP-initiated /oauth2/logout
    93883ed feat(extractor): BearerUser rejects tokens from revoked op_sessions
    0b99014 feat(auth): emit back-channel logout to every registered RP on /v1/auth/logout
    97af972 Merge pull request 'feat(justfile): fj-driven create-release + group attrs (DEV-331)' (#41) from feat/justfile-fj-pr-create-DEV-331 into main
    778a440 feat(justfile): fj-driven create-release + group attrs (DEV-331)
    4353d52 log 5xx errors with underlying AuthError detail
    8a0b75f Merge pull request 'feat: consolidate PMS server PRs (PMS-4..PMS-131)' (#40) from feat/pms-merge into main
    9fa4c00 fix(clippy): resolve remaining workspace clippy warnings for -D warnings
    ff7413d fix(clippy): apply machine-applicable clippy suggestions
    0db5161 style: cargo fmt --all; suppress two pre-existing clippy lints in mokosh-auth-core
    01ebd03 ci(check): add fmt + clippy + compile + tests workflow
    01aefb8 Merge remote-tracking branch 'origin/chore/claude-fix-workflow' into feat/pms-merge
    13d5dda Merge remote-tracking branch 'origin/feat/audit-story-pms-117' into feat/pms-merge
    52ad3ac Merge remote-tracking branch 'origin/feat/settings-story-pms-113' into feat/pms-merge
    be878e7 Merge remote-tracking branch 'origin/feat/sla-story-pms-106' into feat/pms-merge
    0862e7f Merge remote-tracking branch 'origin/feat/rmm-story-pms-100' into feat/pms-merge
    d1d4efb Merge remote-tracking branch 'origin/feat/reports-story-pms-93' into feat/pms-merge
    a057e56 Merge remote-tracking branch 'origin/feat/notifications-story-pms-85' into feat/pms-merge
    6023551 Merge remote-tracking branch 'origin/feat/kb-story-pms-79' into feat/pms-merge
    c415e73 Merge remote-tracking branch 'origin/feat/assets-story-pms-71' into feat/pms-merge
    b718b3f Merge remote-tracking branch 'origin/feat/contracts-story-pms-64' into feat/pms-merge
    236e484 Merge remote-tracking branch 'origin/feat/calendar-story-pms-58' into feat/pms-merge
    62b9643 Merge remote-tracking branch 'origin/feat/projects-story-pms-51' into feat/pms-merge
    b239c93 Merge remote-tracking branch 'origin/feat/time-tracking-story-pms-42' into feat/pms-merge
    c166d47 Merge remote-tracking branch 'origin/feat/billing-story-pms-33' into feat/pms-merge
    7ea90af Merge remote-tracking branch 'origin/feat/portal-story-pms-25' into feat/pms-merge
    40d26ac Merge remote-tracking branch 'origin/feat/tenants-story-pms-21' into feat/pms-merge
    66f1da3 Merge remote-tracking branch 'origin/feat/contacts-story-pms-17' into feat/pms-merge
    e957286 Merge remote-tracking branch 'origin/feat/tickets-story-pms-11' into feat/pms-merge
    a35b3f0 Merge remote-tracking branch 'origin/feat/auth-story-pms-4' into feat/pms-merge
    e2fb8b2 ci: add claude-fix workflow
    47b3574 fix(justfile): register lets-chat with per-provider redirect_uri slug
    02deaed feat(audit): scaffold + writer middleware + read endpoint
    43b1c1f feat(settings): scaffold + tenant settings + module configs
    f4a2d8a feat(sla): scaffold + policies, targets, business hours, holidays, evaluator
    ff4b78e feat(rmm): scaffold + connections, device mappings, alert rules, ingest
    25ea790 feat(reports): scaffold + dashboard, tickets, time, billing, CSV export
    fe702cc feat(notifications): scaffold + channels, templates, prefs, inbox, rules, dispatcher
    d1812ea feat(kb): scaffold + categories, articles, versioning, portal feed
    ffce981 feat(assets): scaffold + asset types, assets, relationships, CIs, credentials, audit
    7298b90 feat(contracts): scaffold + CRUD for contracts, items, balances, rate cards
    24ddf81 feat(calendar): scaffold + appointments, availability, time-off, on-call
    96ce98b feat(projects): scaffold + projects, phases, task statuses, tasks, dependencies
    adaa594 feat(time-tracking): implement work types, time entries, timesheets, timers, rounding rules
    cefa9ac feat(time-tracking): scaffold module + wire router (PMS-43)
    f31778b feat(billing): tax rates CRUD + jurisdiction lookup
    81da537 feat(billing): payment gateway configs CRUD with at-rest encryption
    d3de7f1 feat(billing): payments CRUD with invoice-status reconciliation
    e67030c feat(billing): PUT /api/v1/invoices/:id with frozen-status guard
    47a0ae4 feat(billing): POST /api/v1/invoices with atomic sequence + line items
    44e178f feat(billing): GET /api/v1/invoices/:id with lines
    04e89de feat(billing): GET /api/v1/invoices paginated + filterable (F7)
    5105a8a feat(billing): scaffold module + wire router (PMS-34)
    fed6a72 feat(portal): GET /api/v1/portal/kb placeholder pending PMS-79
    28775fc feat(portal): GET /api/v1/portal/invoices/:id placeholder pending PMS-33
    73d99e2 feat(portal): GET /api/v1/portal/invoices placeholder pending PMS-33
    ab0b9f2 feat(portal): GET /api/v1/portal/tickets/:id with cross-company 404
    60488bc feat(portal): GET /api/v1/portal/tickets lists company-scoped tickets
    b06c6b0 feat(portal): POST /api/v1/portal/tickets creates contact-scoped ticket
    c1d6b7a feat(portal): contact-scoped session + portal auth middleware (F6)
    c38cdea feat(router): gate /api/v1/tenants behind the multi-tenant feature
    3bcfe6f feat(auth): TenantScope extractor for explicit tenant-id propagation
    2b634ab chore(router): drop the dead /companies alias
    e00a272 feat(contacts): honor create_portal_access on create_contact
    06de4ab feat(tickets): dispatch notifications + webhooks from automation rules
    7bb0df5 feat(tickets): send email when add_note has send_email = true
    eaf66db fix(tickets): join lookup tables into TicketResponse (F3)
    e90708c feat(auth): personal API key CRUD (issue / list / revoke)
    3a9cb3f feat(auth): implement MFA TOTP verification + enrollment endpoints
    8ce5238 feat(auth): send welcome email on admin user creation
    d67f8bf feat(auth): send password reset email via Mailer trait
    e2eff78 Merge pull request 'Revert M1 image distribution work' (#19) from revert/m1-image-distribution into main
    b573fc4 Revert M1 image distribution work
    848fcc3 Merge pull request 'ci: fix QEMU binfmt registration for arm64 cross-build' (#18) from feat/m1-image-distribution into main
    fa7ab7d ci: fix QEMU binfmt registration for arm64 cross-build
    89d19f4 Merge pull request 'ci: register QEMU binfmt inline instead of via setup-qemu-action' (#17) from feat/m1-image-distribution into main
    a74fbd5 ci: register QEMU binfmt inline instead of via setup-qemu-action
    2444c15 Merge pull request 'feat/m1-image-distribution' (#16) from feat/m1-image-distribution into main
    36b647e docs(operator): document container registry auth for self-hosters
    fcb323e feat(m1): operator compose.yml + update check + multi-arch build
    dc17aee Merge pull request 'fix(oci): drop image.source label causing dual-org package listing' (#15) from fix/drop-oci-source-label into main
    87a85b6 fix(oci): drop image.source label causing dual-org package listing
    bc546f0 chore(justfile): add register-lets-chat-client recipe (sso M1)
    a253ef3 feat(users): protect Owner memberships from other admins
    c097ae3 fix(auth): switch_active_tenant rebinds new token to caller's op_session
    8ac097a feat(audit): filter extensions + CSV export (item 7 server)
    e0575bd feat(sessions): admin endpoints to list+revoke another user's sessions
    139d71b Merge pull request 'feat: multi-origin CORS + msp-api placeholder + calendar route stub' (#14) from feat/cors-multi-origin-and-placeholder into main
    7beb7a4 security: stop tracking .env.dev; gitignore future copies
    4fb15b2 docs: milestone-1 handoff for the PSA Systems v0.1.0 cutover

    Downloads