• 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